Refactor the features configuration scheme.

Features configuration is handled in its own API in
features_config.h and features_config.c. This way, features
configuration is accessible to anything that needs it.

In addition, features configuration has been altered to
be more channel-oriented. Most callers of features API
code will be supplying a channel so that the individual
channel's settings will be acquired rather than the global
setting.

Missing from this commit is XML documentation for the
features configuration. That will be handled in a separate
commit.

Review: https://reviewboard.asterisk.org/r/2578/

(issue ASTERISK-21542)



git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@390751 65c4cc65-6c06-0410-ace0-fbb531ad65f3
changes/78/78/1
Mark Michelson 12 years ago
parent 5f740572d0
commit 2dc8a06006

@ -69,6 +69,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/dial.h"
#include "asterisk/stasis_channels.h"
#include "asterisk/bridging.h"
#include "asterisk/features_config.h"
/*** DOCUMENTATION
<application name="Dial" language="en_US">
@ -1074,7 +1075,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
int caller_entertained = outgoing
&& ast_test_flag64(outgoing, OPT_MUSICBACK | OPT_RINGBACK);
struct ast_party_connected_line connected_caller;
struct ast_str *featurecode = ast_str_alloca(FEATURE_MAX_LEN + 1);
struct ast_str *featurecode = ast_str_alloca(AST_FEATURE_MAX_LEN + 1);
int cc_recall_core_id;
int is_cc_recall;
int cc_frame_received = 0;
@ -1701,22 +1702,31 @@ skip_frame:;
static int detect_disconnect(struct ast_channel *chan, char code, struct ast_str **featurecode)
{
struct ast_flags features = { AST_FEATURE_DISCONNECT }; /* only concerned with disconnect feature */
struct ast_call_feature feature = { 0, };
char disconnect_code[AST_FEATURE_MAX_LEN];
int res;
ast_str_append(featurecode, 1, "%c", code);
res = ast_feature_detect(chan, &features, ast_str_buffer(*featurecode), &feature);
if (res != AST_FEATURE_RETURN_STOREDIGITS) {
res = ast_get_builtin_feature(chan, "disconnect", disconnect_code, sizeof(disconnect_code));
if (res) {
ast_str_reset(*featurecode);
return 0;
}
if (feature.feature_mask & AST_FEATURE_DISCONNECT) {
return 1;
if (strlen(disconnect_code) > ast_str_strlen(*featurecode)) {
/* Could be a partial match, anyway */
if (strncmp(disconnect_code, ast_str_buffer(*featurecode), ast_str_strlen(*featurecode))) {
ast_str_reset(*featurecode);
}
return 0;
}
return 0;
if (strcmp(disconnect_code, ast_str_buffer(*featurecode))) {
ast_str_reset(*featurecode);
return 0;
}
return 1;
}
/* returns true if there is a valid privacy reply */

@ -49,6 +49,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/astobj2.h"
#include "asterisk/pbx.h"
#include "asterisk/parking.h"
#include "asterisk/features_config.h"
/*!
* \brief Helper function that presents dialtone and grabs extension
@ -59,6 +60,18 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
static int grab_transfer(struct ast_channel *chan, char *exten, size_t exten_len, const char *context)
{
int res;
int digit_timeout;
RAII_VAR(struct ast_features_xfer_config *, xfer_cfg, NULL, ao2_cleanup);
ast_channel_lock(chan);
xfer_cfg = ast_get_chan_features_xfer_config(chan);
if (!xfer_cfg) {
ast_log(LOG_ERROR, "Unable to get transfer configuration\n");
ast_channel_unlock(chan);
return -1;
}
digit_timeout = xfer_cfg->transferdigittimeout;
ast_channel_unlock(chan);
/* Play the simple "transfer" prompt out and wait */
res = ast_stream_and_wait(chan, "pbx-transfer", AST_DIGIT_ANY);
@ -73,8 +86,7 @@ static int grab_transfer(struct ast_channel *chan, char *exten, size_t exten_len
}
/* Drop to dialtone so they can enter the extension they want to transfer to */
/* BUGBUG the timeout needs to be configurable from features.conf. */
res = ast_app_dtget(chan, context, exten, exten_len, exten_len - 1, 3000);
res = ast_app_dtget(chan, context, exten, exten_len, exten_len - 1, digit_timeout);
if (res < 0) {
/* Hangup or error */
res = -1;
@ -265,6 +277,11 @@ static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridg
struct ast_bridge_features_attended_transfer *attended_transfer = hook_pvt;
const char *context;
enum atxfer_code transfer_code = ATXFER_INCOMPLETE;
const char *atxfer_abort;
const char *atxfer_threeway;
const char *atxfer_complete;
const char *fail_sound;
RAII_VAR(struct ast_features_xfer_config *, xfer_cfg, NULL, ao2_cleanup);
ast_bridge_channel_write_hold(bridge_channel, NULL);
@ -273,6 +290,22 @@ static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridg
ast_channel_lock(bridge_channel->chan);
context = ast_strdupa(get_transfer_context(bridge_channel->chan,
attended_transfer ? attended_transfer->context : NULL));
xfer_cfg = ast_get_chan_features_xfer_config(bridge_channel->chan);
if (!xfer_cfg) {
ast_log(LOG_ERROR, "Unable to get transfer configuration options\n");
ast_channel_unlock(bridge_channel->chan);
return 0;
}
if (attended_transfer) {
atxfer_abort = ast_strdupa(S_OR(attended_transfer->abort, xfer_cfg->atxferabort));
atxfer_threeway = ast_strdupa(S_OR(attended_transfer->threeway, xfer_cfg->atxferthreeway));
atxfer_complete = ast_strdupa(S_OR(attended_transfer->complete, xfer_cfg->atxfercomplete));
} else {
atxfer_abort = ast_strdupa(xfer_cfg->atxferabort);
atxfer_threeway = ast_strdupa(xfer_cfg->atxferthreeway);
atxfer_complete = ast_strdupa(xfer_cfg->atxfercomplete);
}
fail_sound = ast_strdupa(xfer_cfg->xferfailsound);
ast_channel_unlock(bridge_channel->chan);
/* Grab the extension to transfer to */
@ -288,36 +321,27 @@ static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridg
if (!peer) {
ast_bridge_merge_inhibit(bridge, -1);
ao2_ref(bridge, -1);
/* BUGBUG beeperr needs to be configurable from features.conf */
ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_NONE);
ast_stream_and_wait(bridge_channel->chan, fail_sound, AST_DIGIT_NONE);
ast_bridge_channel_write_unhold(bridge_channel);
return 0;
}
/* BUGBUG bridging API features does not support features.conf featuremap */
/* BUGBUG bridging API features does not support the features.conf atxfer bounce between C & B channels */
/* Setup a DTMF menu to control the transfer. */
if (ast_bridge_features_init(&caller_features)
|| ast_bridge_hangup_hook(&caller_features,
attended_transfer_complete, &transfer_code, NULL, 0)
|| ast_bridge_dtmf_hook(&caller_features,
attended_transfer && !ast_strlen_zero(attended_transfer->abort)
? attended_transfer->abort : "*1",
|| ast_bridge_dtmf_hook(&caller_features, atxfer_abort,
attended_transfer_abort, &transfer_code, NULL, 0)
|| ast_bridge_dtmf_hook(&caller_features,
attended_transfer && !ast_strlen_zero(attended_transfer->complete)
? attended_transfer->complete : "*2",
|| ast_bridge_dtmf_hook(&caller_features, atxfer_complete,
attended_transfer_complete, &transfer_code, NULL, 0)
|| ast_bridge_dtmf_hook(&caller_features,
attended_transfer && !ast_strlen_zero(attended_transfer->threeway)
? attended_transfer->threeway : "*3",
|| ast_bridge_dtmf_hook(&caller_features, atxfer_threeway,
attended_transfer_threeway, &transfer_code, NULL, 0)) {
ast_bridge_features_cleanup(&caller_features);
ast_hangup(peer);
ast_bridge_merge_inhibit(bridge, -1);
ao2_ref(bridge, -1);
/* BUGBUG beeperr needs to be configurable from features.conf */
ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_NONE);
ast_stream_and_wait(bridge_channel->chan, fail_sound, AST_DIGIT_NONE);
ast_bridge_channel_write_unhold(bridge_channel);
return 0;
}
@ -330,8 +354,7 @@ static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridg
ast_hangup(peer);
ast_bridge_merge_inhibit(bridge, -1);
ao2_ref(bridge, -1);
/* BUGBUG beeperr needs to be configurable from features.conf */
ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_NONE);
ast_stream_and_wait(bridge_channel->chan, fail_sound, AST_DIGIT_NONE);
ast_bridge_channel_write_unhold(bridge_channel);
return 0;
}
@ -345,8 +368,7 @@ static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridg
ast_hangup(peer);
ast_bridge_merge_inhibit(bridge, -1);
ao2_ref(bridge, -1);
/* BUGBUG beeperr needs to be configurable from features.conf */
ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_NONE);
ast_stream_and_wait(bridge_channel->chan, fail_sound, AST_DIGIT_NONE);
ast_bridge_channel_write_unhold(bridge_channel);
return 0;
}
@ -415,7 +437,7 @@ static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridg
if (xfer_failed) {
ast_hangup(peer);
if (!ast_check_hangup_locked(bridge_channel->chan)) {
ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_NONE);
ast_stream_and_wait(bridge_channel->chan, fail_sound, AST_DIGIT_NONE);
}
ast_bridge_channel_write_unhold(bridge_channel);
}

@ -130,6 +130,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/paths.h"
#include "asterisk/ccss.h"
#include "asterisk/data.h"
#include "asterisk/features_config.h"
/*** DOCUMENTATION
<application name="DAHDISendKeypadFacility" language="en_US">
@ -10137,15 +10138,15 @@ static int dahdi_dnd(struct dahdi_pvt *dahdichan, int flag)
return 0;
}
static int canmatch_featurecode(const char *exten)
static int canmatch_featurecode(const char *pickupexten, const char *exten)
{
int extlen = strlen(exten);
const char *pickup_ext;
if (!extlen) {
return 1;
}
pickup_ext = ast_pickup_ext();
if (extlen < strlen(pickup_ext) && !strncmp(pickup_ext, exten, extlen)) {
if (extlen < strlen(pickupexten) && !strncmp(pickupexten, exten, extlen)) {
return 1;
}
/* hardcoded features are *60, *67, *69, *70, *72, *73, *78, *79, *82, *0 */
@ -10191,6 +10192,8 @@ static void *analog_ss_thread(void *data)
int res;
int idx;
struct ast_format tmpfmt;
RAII_VAR(struct ast_features_pickup_config *, pickup_cfg, NULL, ao2_cleanup);
const char *pickupexten;
ast_mutex_lock(&ss_thread_lock);
ss_thread_count++;
@ -10210,6 +10213,17 @@ static void *analog_ss_thread(void *data)
ast_hangup(chan);
goto quit;
}
ast_channel_lock(chan);
pickup_cfg = ast_get_chan_features_pickup_config(chan);
if (!pickup_cfg) {
ast_log(LOG_ERROR, "Unable to retrieve pickup configuration options. Unable to detect call pickup extension\n");
pickupexten = "";
} else {
pickupexten = ast_strdupa(pickup_cfg->pickupexten);
}
ast_channel_unlock(chan);
if (p->dsp)
ast_dsp_digitreset(p->dsp);
switch (p->sig) {
@ -10576,7 +10590,7 @@ static void *analog_ss_thread(void *data)
memset(exten, 0, sizeof(exten));
timeout = firstdigittimeout;
} else if (!strcmp(exten,ast_pickup_ext())) {
} else if (!strcmp(exten, pickupexten)) {
/* Scan all channels and see if there are any
* ringing channels that have call groups
* that equal this channels pickup group
@ -10708,7 +10722,7 @@ static void *analog_ss_thread(void *data)
}
} else if (!ast_canmatch_extension(chan, ast_channel_context(chan), exten, 1,
S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))
&& !canmatch_featurecode(exten)) {
&& !canmatch_featurecode(pickupexten, exten)) {
ast_debug(1, "Can't match %s from '%s' in context %s\n", exten,
S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, "<Unknown Caller>"),
ast_channel_context(chan));

@ -84,6 +84,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/pktccops.h"
#include "asterisk/stasis.h"
#include "asterisk/bridging.h"
#include "asterisk/features_config.h"
/*
* Define to work around buggy dlink MGCP phone firmware which
@ -2971,9 +2972,21 @@ static void *mgcp_ss(void *data)
int res= 0;
int getforward = 0;
int loop_pause = 100;
RAII_VAR(struct ast_features_pickup_config *, pickup_cfg, NULL, ao2_cleanup);
const char *pickupexten;
len = strlen(p->dtmf_buf);
ast_channel_lock(chan);
pickup_cfg = ast_get_chan_features_pickup_config(chan);
if (!pickup_cfg) {
ast_log(LOG_ERROR, "Unable to retrieve pickup configuration options. Unable to detect call pickup extension\n");
pickupexten = "";
} else {
pickupexten = ast_strdupa(pickup_cfg->pickupexten);
}
ast_channel_unlock(chan);
while (len < AST_MAX_EXTENSION - 1) {
ast_debug(1, "Dtmf buffer '%s' for '%s@%s'\n", p->dtmf_buf, p->name, p->parent->name);
res = 1; /* Assume that we will get a digit */
@ -3065,7 +3078,7 @@ static void *mgcp_ss(void *data)
len = 0;
memset(p->dtmf_buf, 0, sizeof(p->dtmf_buf));
timeout = firstdigittimeout;
} else if (!strcmp(p->dtmf_buf,ast_pickup_ext())) {
} else if (!strcmp(p->dtmf_buf, pickupexten)) {
/* Scan all channels and see if any there
* ringing channqels with that have call groups
* that equal this channels pickup group

@ -102,6 +102,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/causes.h"
#include "asterisk/format.h"
#include "asterisk/format_cap.h"
#include "asterisk/features_config.h"
#include "chan_misdn_config.h"
#include "isdn_lib.h"
@ -10071,6 +10072,9 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data)
}
if (ch->state == MISDN_WAITING4DIGS) {
RAII_VAR(struct ast_features_pickup_config *, pickup_cfg, NULL, ao2_cleanup);
const char *pickupexten;
/* Ok, incomplete Setup, waiting till extension exists */
if (ast_strlen_zero(bc->info_dad) && ! ast_strlen_zero(bc->keypad)) {
chan_misdn_log(1, bc->port, " --> using keypad as info\n");
@ -10080,8 +10084,18 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data)
strncat(bc->dialed.number, bc->info_dad, sizeof(bc->dialed.number) - strlen(bc->dialed.number) - 1);
ast_channel_exten_set(ch->ast, bc->dialed.number);
ast_channel_lock(ch->ast);
pickup_cfg = ast_get_chan_features_pickup_config(ch->ast);
if (!pickup_cfg) {
ast_log(LOG_ERROR, "Unable to retrieve pickup configuration options. Unable to detect call pickup extension\n");
pickupexten = "";
} else {
pickupexten = ast_strdupa(pickup_cfg->pickupexten);
}
ast_channel_unlock(ch->ast);
/* Check for Pickup Request first */
if (!strcmp(ast_channel_exten(ch->ast), ast_pickup_ext())) {
if (!strcmp(ast_channel_exten(ch->ast), pickupexten)) {
if (ast_pickup_call(ch->ast)) {
hangup_chan(ch, bc);
} else {
@ -10169,6 +10183,8 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data)
int ai;
int im;
int append_msn = 0;
RAII_VAR(struct ast_features_pickup_config *, pickup_cfg, NULL, ao2_cleanup);
const char *pickupexten;
if (ch) {
switch (ch->state) {
@ -10224,6 +10240,16 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data)
return RESPONSE_RELEASE_SETUP;
}
ast_channel_lock(chan);
pickup_cfg = ast_get_chan_features_pickup_config(chan);
if (!pickup_cfg) {
ast_log(LOG_ERROR, "Unable to retrieve pickup configuration options. Unable to detect call pickup extension\n");
pickupexten = "";
} else {
pickupexten = ast_strdupa(pickup_cfg->pickupexten);
}
ast_channel_unlock(chan);
if ((exceed = add_in_calls(bc->port))) {
char tmp[16];
snprintf(tmp, sizeof(tmp), "%d", exceed);
@ -10315,7 +10341,7 @@ cb_events(enum event_e event, struct misdn_bchannel *bc, void *user_data)
}
/* Check for Pickup Request first */
if (!strcmp(ast_channel_exten(chan), ast_pickup_ext())) {
if (!strcmp(ast_channel_exten(chan), pickupexten)) {
if (!ch->noautorespond_on_setup) {
/* Sending SETUP_ACK */
misdn_lib_send_event(bc, EVENT_SETUP_ACKNOWLEDGE);

@ -296,6 +296,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/app.h"
#include "asterisk/bridging.h"
#include "asterisk/stasis_endpoints.h"
#include "asterisk/features_config.h"
/*** DOCUMENTATION
<application name="SIPDtmfMode" language="en_US">
@ -17662,6 +17663,16 @@ static enum sip_get_dest_result get_destination(struct sip_pvt *p, struct sip_re
char tmpf[256] = "", *from = NULL;
struct sip_request *req;
char *decoded_uri;
RAII_VAR(struct ast_features_pickup_config *, pickup_cfg, ast_get_chan_features_pickup_config(p->owner), ao2_cleanup);
const char *pickupexten;
if (!pickup_cfg) {
ast_log(LOG_ERROR, "Unable to retrieve pickup configuration options. Unable to detect call pickup extension\n");
pickupexten = "";
} else {
/* Don't need to duplicate since channel is locked for the duration of this function */
pickupexten = pickup_cfg->pickupexten;
}
req = oreq;
if (!req) {
@ -17772,7 +17783,7 @@ static enum sip_get_dest_result get_destination(struct sip_pvt *p, struct sip_re
return SIP_GET_DEST_EXTEN_FOUND;
}
if (ast_exists_extension(NULL, p->context, decoded_uri, 1, S_OR(p->cid_num, from))
|| !strcmp(decoded_uri, ast_pickup_ext())) {
|| !strcmp(decoded_uri, pickupexten)) {
if (!oreq) {
ast_string_field_set(p, exten, decoded_uri);
}
@ -17800,7 +17811,7 @@ static enum sip_get_dest_result get_destination(struct sip_pvt *p, struct sip_re
if (ast_test_flag(&global_flags[1], SIP_PAGE2_ALLOWOVERLAP)
&& (ast_canmatch_extension(NULL, p->context, uri, 1, S_OR(p->cid_num, from))
|| ast_canmatch_extension(NULL, p->context, decoded_uri, 1, S_OR(p->cid_num, from))
|| !strncmp(decoded_uri, ast_pickup_ext(), strlen(decoded_uri)))) {
|| !strncmp(decoded_uri, pickupexten, strlen(decoded_uri)))) {
/* Overlap dialing is enabled and we need more digits to match an extension. */
return SIP_GET_DEST_EXTEN_MATCHMORE;
}
@ -21699,7 +21710,8 @@ static void handle_request_info(struct sip_pvt *p, struct sip_request *req)
* on phone calls.
*/
struct ast_call_feature *feat = NULL;
char feat[AST_FEATURE_MAX_LEN];
int feat_res = -1;
int j;
struct ast_frame f = { AST_FRAME_DTMF, };
int suppress_warning = 0; /* Supress warning if the feature is blank */
@ -21711,43 +21723,40 @@ static void handle_request_info(struct sip_pvt *p, struct sip_request *req)
}
/* first, get the feature string, if it exists */
ast_rdlock_call_features();
if (p->relatedpeer) {
if (!strcasecmp(c, "on")) {
if (ast_strlen_zero(p->relatedpeer->record_on_feature)) {
suppress_warning = 1;
} else {
feat = ast_find_call_feature(p->relatedpeer->record_on_feature);
feat_res = ast_get_feature(p->owner, p->relatedpeer->record_on_feature, feat, sizeof(feat));
}
} else if (!strcasecmp(c, "off")) {
if (ast_strlen_zero(p->relatedpeer->record_off_feature)) {
suppress_warning = 1;
} else {
feat = ast_find_call_feature(p->relatedpeer->record_off_feature);
feat_res = ast_get_feature(p->owner, p->relatedpeer->record_off_feature, feat, sizeof(feat));
}
} else {
ast_log(LOG_ERROR, "Received INFO requesting to record with invalid value: %s\n", c);
}
}
if (!feat || ast_strlen_zero(feat->exten)) {
if (feat_res || ast_strlen_zero(feat)) {
if (!suppress_warning) {
ast_log(LOG_WARNING, "Recording requested, but no One Touch Monitor registered. (See features.conf)\n");
}
/* 403 means that we don't support this feature, so don't request it again */
transmit_response(p, "403 Forbidden", req);
ast_unlock_call_features();
return;
}
/* Send the feature code to the PBX as DTMF, just like the handset had sent it */
f.len = 100;
for (j = 0; j < strlen(feat->exten); j++) {
f.subclass.integer = feat->exten[j];
for (j = 0; j < strlen(feat); j++) {
f.subclass.integer = feat[j];
ast_queue_frame(p->owner, &f);
if (sipdebug) {
ast_verbose("* DTMF-relay event faked: %c\n", f.subclass.integer);
}
}
ast_unlock_call_features();
ast_debug(1, "Got a Request to Record the channel, state %s\n", c);
transmit_response(p, "200 OK", req);
@ -25650,6 +25659,15 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, str
if (c) { /* We have a call -either a new call or an old one (RE-INVITE) */
enum ast_channel_state c_state = ast_channel_state(c);
RAII_VAR(struct ast_features_pickup_config *, pickup_cfg, ast_get_chan_features_pickup_config(c), ao2_cleanup);
const char *pickupexten;
if (!pickup_cfg) {
ast_log(LOG_ERROR, "Unable to retrieve pickup configuration options. Unable to detect call pickup extension\n");
pickupexten = "";
} else {
pickupexten = ast_strdupa(pickup_cfg->pickupexten);
}
if (c_state != AST_STATE_UP && reinvite &&
(p->invitestate == INV_TERMINATED || p->invitestate == INV_CONFIRMED)) {
@ -25671,7 +25689,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, str
transmit_provisional_response(p, "100 Trying", req, 0);
p->invitestate = INV_PROCEEDING;
ast_setstate(c, AST_STATE_RING);
if (strcmp(p->exten, ast_pickup_ext())) { /* Call to extension -start pbx on this call */
if (strcmp(p->exten, pickupexten)) { /* Call to extension -start pbx on this call */
enum ast_pbx_result result;
result = ast_pbx_start(c);

@ -76,6 +76,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/features.h"
#include "asterisk/astobj2.h"
#include "asterisk/astdb.h"
#include "asterisk/features_config.h"
#define DEFAULTCONTEXT "default"
@ -3086,11 +3087,25 @@ static void handle_call_outgoing(struct unistimsession *s)
send_favorite_short(sub->softkey, FAV_ICON_OFFHOOK_BLACK, s);
s->device->selected = -1;
if (!sub->owner) { /* A call is already in progress ? */
RAII_VAR(struct ast_features_pickup_config *, pickup_cfg, NULL, ao2_cleanup);
const char *pickupexten;
c = unistim_new(sub, AST_STATE_DOWN, NULL); /* No, starting a new one */
if (!sub->rtp) { /* Need to start RTP before calling ast_pbx_run */
start_rtp(sub);
}
if (c && !strcmp(s->device->phone_number, ast_pickup_ext())) {
if (c) {
ast_channel_lock(c);
pickup_cfg = ast_get_chan_features_pickup_config(c);
if (!pickup_cfg) {
ast_log(LOG_ERROR, "Unable to retrieve pickup configuration options. Unable to detect call pickup extension\n");
pickupexten = "";
} else {
pickupexten = ast_strdupa(pickup_cfg->pickupexten);
}
ast_channel_unlock(c);
}
if (c && !strcmp(s->device->phone_number, pickupexten)) {
if (unistimdebug) {
ast_verb(0, "Try to pickup in unistim_new\n");
}
@ -4099,8 +4114,17 @@ static void key_main_page(struct unistimsession *pte, char keycode)
ast_mutex_unlock(&devicelock);
show_extension_page(pte);
} else { /* Pickup function */
/* XXX Is there a way to get a specific channel here? */
RAII_VAR(struct ast_features_pickup_config *, pickup_cfg,
ast_get_chan_features_pickup_config(NULL), ao2_cleanup);
if (!pickup_cfg) {
ast_log(LOG_ERROR, "Unable to retrieve pickup configuration options. Unable to detect call pickup extension\n");
break;
}
pte->device->selected = -1;
ast_copy_string(pte->device->phone_number, ast_pickup_ext(),
ast_copy_string(pte->device->phone_number, pickup_cfg->pickupexten,
sizeof(pte->device->phone_number));
handle_call_outgoing(pte);
}

@ -43,6 +43,7 @@
#include "asterisk/features.h"
#include "asterisk/cel.h"
#include "asterisk/causes.h"
#include "asterisk/features_config.h"
#include "sig_analog.h"
@ -1708,15 +1709,13 @@ static int analog_get_sub_fd(struct analog_pvt *p, enum analog_sub sub)
#define ANALOG_NEED_MFDETECT(p) (((p)->sig == ANALOG_SIG_FEATDMF) || ((p)->sig == ANALOG_SIG_FEATDMF_TA) || ((p)->sig == ANALOG_SIG_E911) || ((p)->sig == ANALOG_SIG_FGC_CAMA) || ((p)->sig == ANALOG_SIG_FGC_CAMAMF) || ((p)->sig == ANALOG_SIG_FEATB))
static int analog_canmatch_featurecode(const char *exten)
static int analog_canmatch_featurecode(const char *pickupexten, const char *exten)
{
int extlen = strlen(exten);
const char *pickup_ext;
if (!extlen) {
return 1;
}
pickup_ext = ast_pickup_ext();
if (extlen < strlen(pickup_ext) && !strncmp(pickup_ext, exten, extlen)) {
if (extlen < strlen(pickupexten) && !strncmp(pickupexten, exten, extlen)) {
return 1;
}
/* hardcoded features are *60, *67, *69, *70, *72, *73, *78, *79, *82, *0 */
@ -1756,6 +1755,8 @@ static void *__analog_ss_thread(void *data)
int res;
int idx;
struct ast_callid *callid;
RAII_VAR(struct ast_features_pickup_config *, pickup_cfg, NULL, ao2_cleanup);
const char *pickupexten;
analog_increase_ss_count();
@ -1786,6 +1787,17 @@ static void *__analog_ss_thread(void *data)
ast_hangup(chan);
goto quit;
}
ast_channel_lock(chan);
pickup_cfg = ast_get_chan_features_pickup_config(chan);
if (!pickup_cfg) {
ast_log(LOG_ERROR, "Unable to retrieve pickup configuration options. Unable to detect call pickup extension\n");
pickupexten = "";
} else {
pickupexten = ast_strdupa(pickup_cfg->pickupexten);
}
ast_channel_unlock(chan);
analog_dsp_reset_and_flush_digits(p);
switch (p->sig) {
case ANALOG_SIG_FEATD:
@ -2190,7 +2202,7 @@ static void *__analog_ss_thread(void *data)
memset(exten, 0, sizeof(exten));
timeout = analog_firstdigittimeout;
} else if (!strcmp(exten,ast_pickup_ext())) {
} else if (!strcmp(exten, pickupexten)) {
/* Scan all channels and see if there are any
* ringing channels that have call groups
* that equal this channels pickup group
@ -2334,7 +2346,7 @@ static void *__analog_ss_thread(void *data)
}
} else if (!ast_canmatch_extension(chan, ast_channel_context(chan), exten, 1,
ast_channel_caller(chan)->id.number.valid ? ast_channel_caller(chan)->id.number.str : NULL)
&& !analog_canmatch_featurecode(exten)) {
&& !analog_canmatch_featurecode(pickupexten, exten)) {
ast_debug(1, "Can't match %s from '%s' in context %s\n", exten,
ast_channel_caller(chan)->id.number.valid && ast_channel_caller(chan)->id.number.str
? ast_channel_caller(chan)->id.number.str : "<Unknown Caller>",

@ -38,6 +38,7 @@
#include "asterisk/http_websocket.h"
#include "asterisk/rtp_engine.h"
#include "asterisk/netsock2.h"
#include "asterisk/features_config.h"
#ifndef FALSE
#define FALSE 0
@ -762,8 +763,8 @@ struct sip_settings {
struct sip_proxy outboundproxy; /*!< Outbound proxy */
char default_context[AST_MAX_CONTEXT];
char default_subscribecontext[AST_MAX_CONTEXT];
char default_record_on_feature[FEATURE_MAX_LEN];
char default_record_off_feature[FEATURE_MAX_LEN];
char default_record_on_feature[AST_FEATURE_MAX_LEN];
char default_record_off_feature[AST_FEATURE_MAX_LEN];
struct ast_acl_list *contact_acl; /*! \brief Global list of addresses dynamic peers are not allowed to use */
struct ast_format_cap *caps; /*!< Supported codecs */
int tcp_enabled;

@ -927,6 +927,9 @@ enum {
AST_FEATURE_AUTOMIXMON = (1 << 6),
};
#define AST_FEATURE_DTMF_MASK (AST_FEATURE_REDIRECT | AST_FEATURE_DISCONNECT |\
AST_FEATURE_ATXFER | AST_FEATURE_AUTOMON | AST_FEATURE_PARKCALL | AST_FEATURE_AUTOMIXMON)
/*! \brief bridge configuration */
struct ast_bridge_config {
struct ast_flags features_caller;

@ -62,20 +62,6 @@ enum {
AST_FEATURE_FLAG_BYBOTH = (3 << 3),
};
struct ast_call_feature {
int feature_mask;
char *fname;
char sname[FEATURE_SNAME_LEN];
char exten[FEATURE_MAX_LEN];
char default_exten[FEATURE_MAX_LEN];
ast_feature_operation operation;
unsigned int flags;
char app[FEATURE_APP_LEN];
char app_args[FEATURE_APP_ARGS_LEN];
char moh_class[FEATURE_MOH_LEN];
AST_LIST_ENTRY(ast_call_feature) feature_entry;
};
/*!
* \brief Park a call and read back parked location
*
@ -166,9 +152,6 @@ int ast_masq_park_call_exten(struct ast_channel *park_me, struct ast_channel *pa
*/
int ast_parking_ext_valid(const char *exten_str, struct ast_channel *chan, const char *context);
/*! \brief Determine system call pickup extension */
const char *ast_pickup_ext(void);
/*!
* \brief Simulate a DTMF end on a broken bridge channel.
*
@ -221,39 +204,6 @@ int ast_pickup_call(struct ast_channel *chan);
*/
int ast_do_pickup(struct ast_channel *chan, struct ast_channel *target);
/*!
* \brief register new feature into feature_set
* \param feature an ast_call_feature object which contains a keysequence
* and a callback function which is called when this keysequence is pressed
* during a call.
*/
void ast_register_feature(struct ast_call_feature *feature);
/*!
* \brief unregister feature from feature_set
* \param feature the ast_call_feature object which was registered before
*/
void ast_unregister_feature(struct ast_call_feature *feature);
/*!
* \brief detect a feature before bridging
* \param chan
* \param features an ast_flags ptr
* \param code ptr of input code
* \param feature
* \retval ast_call_feature ptr to be set if found
*/
int ast_feature_detect(struct ast_channel *chan, struct ast_flags *features, const char *code, struct ast_call_feature *feature);
/*!
* \brief look for a call feature entry by its sname
* \param name a string ptr, should match "automon", "blindxfer", "atxfer", etc.
*/
struct ast_call_feature *ast_find_call_feature(const char *name);
void ast_rdlock_call_features(void);
void ast_unlock_call_features(void);
/*! \brief Reload call features from features.conf */
int ast_features_reload(void);

@ -0,0 +1,234 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2013, Digium, Inc.
*
* Mark Michelson <mmichelson@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
#ifndef _FEATURES_CONFIG_H
#define _FEATURES_CONFIG_H
#include "asterisk/stringfields.h"
struct ast_channel;
/*!
* \brief General features configuration items
*/
struct ast_features_general_config {
AST_DECLARE_STRING_FIELDS(
/*! Sound played when automon or automixmon features are used */
AST_STRING_FIELD(courtesytone);
);
/*! Milliseconds allowed between digit presses when entering feature code */
unsigned int featuredigittimeout;
};
/*!
* \brief Get the general configuration options for a channel
*
* \note The channel should be locked before calling this function.
* \note The returned value has its reference count incremented.
*
* If no channel is provided, then the global features configuration is returned.
*
* \param chan The channel to get configuration options for
* \retval NULL Failed to get configuration
* \retval non-NULL The general features configuration
*/
struct ast_features_general_config *ast_get_chan_features_general_config(struct ast_channel *chan);
/*!
* \brief Feature configuration relating to transfers
*/
struct ast_features_xfer_config {
AST_DECLARE_STRING_FIELDS (
/*! Sound to play when transfer succeeds */
AST_STRING_FIELD(xfersound);
/*! Sound to play when transfer fails */
AST_STRING_FIELD(xferfailsound);
/*! DTMF sequence used to abort an attempted atxfer */
AST_STRING_FIELD(atxferabort);
/*! DTMF sequence used to complete an attempted atxfer */
AST_STRING_FIELD(atxfercomplete);
/*! DTMF sequence used to turn an attempted atxfer into a three-way call */
AST_STRING_FIELD(atxferthreeway);
);
/*! Milliseconds allowed between digit presses when dialing transfer destination */
unsigned int transferdigittimeout;
/*! Milliseconds to wait for the transfer target to answer a transferred call */
unsigned int atxfernoanswertimeout;
/*! Milliseconds to wait before attempting to re-dial the transfer target */
unsigned int atxferloopdelay;
/*! Number of times to re-attempt dialing the transfer target */
unsigned int atxfercallbackretries;
/*! Determines if the call is dropped on attended transfer failure */
unsigned int atxferdropcall;
};
/*!
* \brief Get the transfer configuration options for a channel
*
* \note The channel should be locked before calling this function.
* \note The returned value has its reference count incremented.
*
* If no channel is provided, then the global transfer configuration is returned.
*
* \param chan The channel to get configuration options for
* \retval NULL Failed to get configuration
* \retval non-NULL The transfer features configuration
*/
struct ast_features_xfer_config *ast_get_chan_features_xfer_config(struct ast_channel *chan);
/*!
* \brief Configuration relating to call pickup
*/
struct ast_features_pickup_config {
AST_DECLARE_STRING_FIELDS (
/*! Digit sequence to press to pick up a ringing call */
AST_STRING_FIELD(pickupexten);
/*! Sound to play to picker when pickup succeeds */
AST_STRING_FIELD(pickupsound);
/*! Sound to play to picker when pickup fails */
AST_STRING_FIELD(pickupfailsound);
);
};
/*!
* \brief Get the pickup configuration options for a channel
*
* \note The channel should be locked before calling this function.
* \note The returned value has its reference count incremented.
*
* If no channel is provided, then the global pickup configuration is returned.
*
* \param chan The channel to get configuration options for
* \retval NULL Failed to get configuration
* \retval non-NULL The pickup features configuration
*/
struct ast_features_pickup_config *ast_get_chan_features_pickup_config(struct ast_channel *chan);
/*!
* \brief Configuration for the builtin features
*/
struct ast_featuremap_config {
AST_DECLARE_STRING_FIELDS (
/*! Blind transfer DTMF code */
AST_STRING_FIELD(blindxfer);
/*! Disconnect DTMF code */
AST_STRING_FIELD(disconnect);
/*! Automon DTMF code */
AST_STRING_FIELD(automon);
/*! Attended Transfer DTMF code */
AST_STRING_FIELD(atxfer);
/*! One-touch parking DTMF code */
AST_STRING_FIELD(parkcall);
/*! Automixmon DTMF code */
AST_STRING_FIELD(automixmon);
);
};
/*!
* \brief Get the featuremap configuration options for a channel
*
* \note The channel should be locked before calling this function.
* \note The returned value has its reference count incremented.
*
* If no channel is provided, then the global featuremap configuration is returned.
*
* \param chan The channel to get configuration options for
* \retval NULL Failed to get configuration
* \retval non-NULL The pickup features configuration
*/
struct ast_featuremap_config *ast_get_chan_featuremap_config(struct ast_channel *chan);
/*!
* \brief Get the DTMF code for a builtin feature
*
* \note The channel should be locked before calling this function
*
* If no channel is provided, then the global setting for the option is returned.
*
* \param chan The channel to get the option from
* \param feature The short name of the feature (as it appears in features.conf)
* \param[out] buf The buffer to write the DTMF value into
* \param size The size of the buffer in bytes
* \retval 0 Success
* \retval non-zero Unrecognized builtin feature name
*/
int ast_get_builtin_feature(struct ast_channel *chan, const char *feature, char *buf, size_t len);
/*!
* \brief Get the DTMF code for a call feature
*
* \note The channel should be locked before calling this function
*
* If no channel is provided, then the global setting for the option is returned.
*
* This function is like \ref ast_get_builtin_feature except that it will
* also check the applicationmap in addition to the builtin features.
*
* \param chan The channel to get the option from
* \param feature The short name of the feature
* \param[out] buf The buffer to write the DTMF value into
* \param size The size of the buffer in bytes
* \retval 0 Success
* \retval non-zero Unrecognized feature name
*/
int ast_get_feature(struct ast_channel *chan, const char *feature, char *buf, size_t len);
#define AST_FEATURE_MAX_LEN 11
/*!
* \brief An applicationmap configuration item
*/
struct ast_applicationmap_item {
AST_DECLARE_STRING_FIELDS (
/* Name of the item */
AST_STRING_FIELD(name);
/* Name Dialplan application that is invoked by the feature */
AST_STRING_FIELD(app);
/* Data to pass to the application */
AST_STRING_FIELD(app_data);
/* Music-on-hold class to play to party on which feature is not activated */
AST_STRING_FIELD(moh_class);
);
/* DTMF key sequence used to activate the feature */
char dtmf[AST_FEATURE_MAX_LEN];
/* If true, activate on party that input the sequence, otherwise activate on the other party */
unsigned int activate_on_self;
};
/*!
* \brief Get the applicationmap for a given channel.
*
* \note The channel should be locked before calling this function.
*
* This uses the value of the DYNAMIC_FEATURES channel variable to build a
* custom applicationmap for this channel. The returned container has
* applicationmap_items inside.
*
* \param chan The channel for which applicationmap is being retrieved.
* \retval NULL An error occurred or the channel has no dynamic features.
* \retval non-NULL A container of applicationmap_items pertaining to the channel.
*/
struct ao2_container *ast_get_chan_applicationmap(struct ast_channel *chan);
void ast_features_config_shutdown(void);
int ast_features_config_reload(void);
int ast_features_config_init(void);
#endif /* _FEATURES_CONFIG_H */

@ -61,6 +61,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/cli.h"
#include "asterisk/parking.h"
#include "asterisk/core_local.h"
#include "asterisk/features_config.h"
/*! All bridges container. */
static struct ao2_container *bridges;
@ -1914,6 +1915,18 @@ static void bridge_channel_feature(struct ast_bridge_channel *bridge_channel)
struct ast_bridge_hook *hook = NULL;
char dtmf[MAXIMUM_DTMF_FEATURE_STRING] = "";
size_t dtmf_len = 0;
unsigned int digit_timeout;
RAII_VAR(struct ast_features_general_config *, gen_cfg, NULL, ao2_cleanup);
ast_channel_lock(bridge_channel->chan);
gen_cfg = ast_get_chan_features_general_config(bridge_channel->chan);
if (!gen_cfg) {
ast_log(LOG_ERROR, "Unable to retrieve features configuration.\n");
ast_channel_unlock(bridge_channel->chan);
return;
}
digit_timeout = gen_cfg->featuredigittimeout;
ast_channel_unlock(bridge_channel->chan);
/* The channel is now under our control and we don't really want any begin frames to do our DTMF matching so disable 'em at the core level */
ast_set_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_END_DTMF_ONLY);
@ -1923,7 +1936,7 @@ static void bridge_channel_feature(struct ast_bridge_channel *bridge_channel)
int res;
/* If the above timed out simply exit */
res = ast_waitfordigit(bridge_channel->chan, 3000);
res = ast_waitfordigit(bridge_channel->chan, digit_timeout);
if (!res) {
ast_debug(1, "DTMF feature string collection on %p(%s) timed out\n",
bridge_channel, ast_channel_name(bridge_channel->chan));

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -96,6 +96,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/test.h"
#include "asterisk/json.h"
#include "asterisk/bridging.h"
#include "asterisk/features_config.h"
/*** DOCUMENTATION
<manager name="Ping" language="en_US">
@ -4048,8 +4049,8 @@ static int action_atxfer(struct mansession *s, const struct message *m)
const char *exten = astman_get_header(m, "Exten");
const char *context = astman_get_header(m, "Context");
struct ast_channel *chan = NULL;
struct ast_call_feature *atxfer_feature = NULL;
char *feature_code = NULL;
char feature_code[AST_FEATURE_MAX_LEN];
const char *digit;
if (ast_strlen_zero(name)) {
astman_send_error(s, m, "No channel specified");
@ -4060,31 +4061,33 @@ static int action_atxfer(struct mansession *s, const struct message *m)
return 0;
}
ast_rdlock_call_features();
atxfer_feature = ast_find_call_feature("atxfer");
ast_unlock_call_features();
if (!atxfer_feature) {
astman_send_error(s, m, "No attended transfer feature found");
if (!(chan = ast_channel_get_by_name(name))) {
astman_send_error(s, m, "Channel specified does not exist");
return 0;
}
if (!(chan = ast_channel_get_by_name(name))) {
astman_send_error(s, m, "Channel specified does not exist");
ast_channel_lock(chan);
if (ast_get_builtin_feature(chan, "atxfer", feature_code, sizeof(feature_code)) ||
ast_strlen_zero(feature_code)) {
ast_channel_unlock(chan);
astman_send_error(s, m, "No attended transfer feature code found");
ast_channel_unref(chan);
return 0;
}
ast_channel_unlock(chan);
if (!ast_strlen_zero(context)) {
pbx_builtin_setvar_helper(chan, "TRANSFER_CONTEXT", context);
}
/* BUGBUG action_atxfer() is broken because the bridge DTMF hooks need both begin and end events to match correctly. */
for (feature_code = atxfer_feature->exten; feature_code && *feature_code; ++feature_code) {
struct ast_frame f = { AST_FRAME_DTMF, .subclass.integer = *feature_code };
for (digit = feature_code; *digit; ++digit) {
struct ast_frame f = { AST_FRAME_DTMF, .subclass.integer = *digit };
ast_queue_frame(chan, &f);
}
for (feature_code = (char *)exten; feature_code && *feature_code; ++feature_code) {
struct ast_frame f = { AST_FRAME_DTMF, .subclass.integer = *feature_code };
for (digit = exten; *digit; ++digit) {
struct ast_frame f = { AST_FRAME_DTMF, .subclass.integer = *digit };
ast_queue_frame(chan, &f);
}

Loading…
Cancel
Save