Merge my applicationmap_fixup branch to address the issues described in this
post to the asterisk-dev mailing list: http://lists.digium.com/pipermail/asterisk-dev/2006-August/022174.html This implements full control over both which channel(s) can activate a dynamic feature, as well as which channel to run the application on. I also updated the documentation on the applicationmap in features.conf.sample in hopes that the configuration is more clear. git-svn-id: http://svn.digium.com/svn/asterisk/trunk@39109 f38db490-d61c-443f-a65b-d21fe96a405b
This commit is contained in:
parent
0ae26ab60c
commit
66421e3fc5
|
@ -42,9 +42,44 @@ context => parkedcalls ; Which context parked calls are in
|
|||
; Note that the DYNAMIC_FEATURES channel variable must be set to use the features
|
||||
; defined here. The value of DYNAMIC_FEATURES should be the names of the features
|
||||
; to allow the channel to use separated by '#'. For example:
|
||||
;
|
||||
; Set(DYNAMIC_FEATURES=myfeature1#myfeature2#myfeature3)
|
||||
;
|
||||
;testfeature => #9,callee,Playback,tt-monkeys ;Play tt-monkeys to
|
||||
;callee if #9 was pressed
|
||||
;pauseMonitor => #1,caller,Pausemonitor ;Pause monitoring on channel
|
||||
;unpauseMonitor => #3,caller,UnPauseMonitor ;Unpause monitoring on channel
|
||||
;
|
||||
; The syntax for declaring a dynaic feature is the following:
|
||||
;
|
||||
;<FeatureName> => <DTMF_sequence>,<ActivateOn>[/<ActivatedBy>],<Application>[,<AppArguments>]
|
||||
;
|
||||
; FeatureName -> This is the name of the feature used in when setting the
|
||||
; DYNAMIC_FEATURES variable to enable usage of this feature.
|
||||
; DTMF_sequence -> This is the key sequence used to activate this feature.
|
||||
; ActivateOn -> This is the channel of the call that the application will be executed
|
||||
; on. Valid values are "self" and "peer". "self" means run the
|
||||
; application on the same channel that activated the feature. "peer"
|
||||
; means run the application on the opposite channel from the one that
|
||||
; has activated the feature.
|
||||
; ActivatedBy -> This is which channel is allowed to activated this feature. Valid
|
||||
; values are "caller", "callee", and "both". "both" is the default.
|
||||
; The "caller" is the channel that executed the Dial application, while
|
||||
; the "callee" is the channel called by the Dial application.
|
||||
; Application -> This is the application to execute.
|
||||
; AppArguments -> These are the arguments to be passed into the application.
|
||||
;
|
||||
;
|
||||
; IMPORTANT NOTE: The applicationmap is not intended to be used for all Asterisk
|
||||
; applications. When applications are used in extensions.conf, they are executed
|
||||
; by the PBX core. In this case, these applications are executed outside of the
|
||||
; PBX core, so it does *not* make sense to use any application which has any
|
||||
; concept of dialplan flow. Examples of this would be things like Macro, Goto,
|
||||
; Background, WaitExten, and many more.
|
||||
;
|
||||
;
|
||||
; Example Usage:
|
||||
;
|
||||
;testfeature => #9,peer,Playback,tt-monkeys ;Allow both the caller and callee to play
|
||||
; ;tt-monkeys to the opposite channel
|
||||
;
|
||||
;pauseMonitor => #1,self/callee,Pausemonitor ;Allow the callee to pause monitoring
|
||||
; ;on their channel
|
||||
;unpauseMonitor => #3,self/callee,UnPauseMonitor ;Allow the callee to unpause monitoring
|
||||
; ;on their channel
|
||||
|
|
|
@ -416,16 +416,14 @@ struct ast_channel {
|
|||
|
||||
/* @} */
|
||||
|
||||
#define AST_FEATURE_PLAY_WARNING (1 << 0)
|
||||
#define AST_FEATURE_REDIRECT (1 << 1)
|
||||
#define AST_FEATURE_DISCONNECT (1 << 2)
|
||||
#define AST_FEATURE_ATXFER (1 << 3)
|
||||
#define AST_FEATURE_AUTOMON (1 << 4)
|
||||
#define AST_FEATURE_PARKCALL (1 << 5)
|
||||
|
||||
#define AST_FEATURE_FLAG_NEEDSDTMF (1 << 0)
|
||||
#define AST_FEATURE_FLAG_CALLEE (1 << 1)
|
||||
#define AST_FEATURE_FLAG_CALLER (1 << 2)
|
||||
enum {
|
||||
AST_FEATURE_PLAY_WARNING = (1 << 0),
|
||||
AST_FEATURE_REDIRECT = (1 << 1),
|
||||
AST_FEATURE_DISCONNECT = (1 << 2),
|
||||
AST_FEATURE_ATXFER = (1 << 3),
|
||||
AST_FEATURE_AUTOMON = (1 << 4),
|
||||
AST_FEATURE_PARKCALL = (1 << 5),
|
||||
};
|
||||
|
||||
struct ast_bridge_config {
|
||||
struct ast_flags features_caller;
|
||||
|
|
|
@ -75,6 +75,15 @@ static void FREE(void *ptr)
|
|||
|
||||
#define AST_MAX_WATCHERS 256
|
||||
|
||||
enum {
|
||||
AST_FEATURE_FLAG_NEEDSDTMF = (1 << 0),
|
||||
AST_FEATURE_FLAG_ONPEER = (1 << 1),
|
||||
AST_FEATURE_FLAG_ONSELF = (1 << 2),
|
||||
AST_FEATURE_FLAG_BYCALLEE = (1 << 3),
|
||||
AST_FEATURE_FLAG_BYCALLER = (1 << 4),
|
||||
AST_FEATURE_FLAG_BYBOTH = (3 << 3),
|
||||
};
|
||||
|
||||
static char *parkedcall = "ParkedCall";
|
||||
|
||||
static int parkaddhints = 0; /*!< Add parking hints automatically */
|
||||
|
@ -938,11 +947,12 @@ static int feature_exec_app(struct ast_channel *chan, struct ast_channel *peer,
|
|||
{
|
||||
struct ast_app *app;
|
||||
struct ast_call_feature *feature;
|
||||
struct ast_channel *work;
|
||||
int res;
|
||||
|
||||
AST_LIST_LOCK(&feature_list);
|
||||
AST_LIST_TRAVERSE(&feature_list,feature,feature_entry) {
|
||||
if (!strcasecmp(feature->exten,code))
|
||||
AST_LIST_TRAVERSE(&feature_list, feature, feature_entry) {
|
||||
if (!strcasecmp(feature->exten, code))
|
||||
break;
|
||||
}
|
||||
AST_LIST_UNLOCK(&feature_list);
|
||||
|
@ -951,21 +961,31 @@ static int feature_exec_app(struct ast_channel *chan, struct ast_channel *peer,
|
|||
ast_log(LOG_NOTICE, "Found feature before, but at execing we've lost it??\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
app = pbx_findapp(feature->app);
|
||||
if (app) {
|
||||
struct ast_channel *work = ast_test_flag(feature,AST_FEATURE_FLAG_CALLEE) ? peer : chan;
|
||||
res = pbx_exec(work, app, feature->app_args);
|
||||
if (res == AST_PBX_KEEPALIVE)
|
||||
return FEATURE_RETURN_PBX_KEEPALIVE;
|
||||
else if (res == AST_PBX_NO_HANGUP_PEER)
|
||||
return FEATURE_RETURN_NO_HANGUP_PEER;
|
||||
else if (res)
|
||||
return FEATURE_RETURN_SUCCESSBREAK;
|
||||
|
||||
if (sense == FEATURE_SENSE_CHAN) {
|
||||
if (!ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLER))
|
||||
return FEATURE_RETURN_PASSDIGITS;
|
||||
work = ast_test_flag(feature, AST_FEATURE_FLAG_ONSELF) ? chan : peer;
|
||||
} else {
|
||||
if (!ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLEE))
|
||||
return FEATURE_RETURN_PASSDIGITS;
|
||||
work = ast_test_flag(feature, AST_FEATURE_FLAG_ONSELF) ? peer : chan;
|
||||
}
|
||||
|
||||
if (!(app = pbx_findapp(feature->app))) {
|
||||
ast_log(LOG_WARNING, "Could not find application (%s)\n", feature->app);
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* XXX Should we service the other channel while this runs? */
|
||||
res = pbx_exec(work, app, feature->app_args);
|
||||
|
||||
if (res == AST_PBX_KEEPALIVE)
|
||||
return FEATURE_RETURN_PBX_KEEPALIVE;
|
||||
else if (res == AST_PBX_NO_HANGUP_PEER)
|
||||
return FEATURE_RETURN_NO_HANGUP_PEER;
|
||||
else if (res)
|
||||
return FEATURE_RETURN_SUCCESSBREAK;
|
||||
|
||||
return FEATURE_RETURN_SUCCESS; /*! \todo XXX should probably return res */
|
||||
}
|
||||
|
@ -1035,10 +1055,7 @@ static int ast_feature_interpret(struct ast_channel *chan, struct ast_channel *p
|
|||
if (!strcmp(feature->exten, code)) {
|
||||
if (option_verbose > 2)
|
||||
ast_verbose(VERBOSE_PREFIX_3 " Feature Found: %s exten: %s\n",feature->sname, tok);
|
||||
if (sense == FEATURE_SENSE_CHAN)
|
||||
res = feature->operation(chan, peer, config, code, sense);
|
||||
else
|
||||
res = feature->operation(peer, chan, config, code, sense);
|
||||
res = feature->operation(chan, peer, config, code, sense);
|
||||
break;
|
||||
} else if (!strncmp(feature->exten, code, strlen(code))) {
|
||||
res = FEATURE_RETURN_STOREDIGITS;
|
||||
|
@ -1076,9 +1093,9 @@ static void set_config_flags(struct ast_channel *chan, struct ast_channel *peer,
|
|||
/* while we have a feature */
|
||||
while ((tok = strsep(&tmp, "#"))) {
|
||||
if ((feature = find_feature(tok)) && ast_test_flag(feature, AST_FEATURE_FLAG_NEEDSDTMF)) {
|
||||
if (ast_test_flag(feature, AST_FEATURE_FLAG_CALLER))
|
||||
if (ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLER))
|
||||
ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_0);
|
||||
if (ast_test_flag(feature, AST_FEATURE_FLAG_CALLEE))
|
||||
if (ast_test_flag(feature, AST_FEATURE_FLAG_BYCALLEE))
|
||||
ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_1);
|
||||
}
|
||||
}
|
||||
|
@ -2147,73 +2164,76 @@ static int load_config(void)
|
|||
/* Map a key combination to an application*/
|
||||
ast_unregister_features();
|
||||
for (var = ast_variable_browse(cfg, "applicationmap"); var; var = var->next) {
|
||||
char *tmp_val = ast_strdup(var->value);
|
||||
char *exten, *party=NULL, *app=NULL, *app_args=NULL;
|
||||
|
||||
if (!tmp_val) {
|
||||
/*! \todo XXX No memory. We should probably break, but at least we do not
|
||||
* insist on this entry or we could be stuck in an
|
||||
* infinite loop.
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
char *tmp_val = ast_strdupa(var->value);
|
||||
char *exten, *activateon, *activatedby, *app, *app_args;
|
||||
struct ast_call_feature *feature;
|
||||
|
||||
/* strsep() sets the argument to NULL if match not found, and it
|
||||
* is safe to use it with a NULL argument, so we don't check
|
||||
* between calls.
|
||||
*/
|
||||
exten = strsep(&tmp_val,",");
|
||||
party = strsep(&tmp_val,",");
|
||||
activatedby = strsep(&tmp_val,",");
|
||||
app = strsep(&tmp_val,",");
|
||||
app_args = strsep(&tmp_val,",");
|
||||
|
||||
activateon = strsep(&activatedby, "/");
|
||||
|
||||
/*! \todo XXX var_name or app_args ? */
|
||||
if (ast_strlen_zero(app) || ast_strlen_zero(exten) || ast_strlen_zero(party) || ast_strlen_zero(var->name)) {
|
||||
ast_log(LOG_NOTICE, "Please check the feature Mapping Syntax, either extension, name, or app aren't provided %s %s %s %s\n",app,exten,party,var->name);
|
||||
free(tmp_val);
|
||||
if (ast_strlen_zero(app) || ast_strlen_zero(exten) || ast_strlen_zero(activateon) || ast_strlen_zero(var->name)) {
|
||||
ast_log(LOG_NOTICE, "Please check the feature Mapping Syntax, either extension, name, or app aren't provided %s %s %s %s\n",
|
||||
app, exten, activateon, var->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
{
|
||||
struct ast_call_feature *feature;
|
||||
int mallocd = 0;
|
||||
|
||||
if (!(feature = find_feature(var->name))) {
|
||||
mallocd = 1;
|
||||
|
||||
if (!(feature = ast_calloc(1, sizeof(*feature)))) {
|
||||
free(tmp_val);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
ast_copy_string(feature->sname,var->name,FEATURE_SNAME_LEN);
|
||||
ast_copy_string(feature->app,app,FEATURE_APP_LEN);
|
||||
ast_copy_string(feature->exten, exten,FEATURE_EXTEN_LEN);
|
||||
free(tmp_val);
|
||||
|
||||
if (app_args)
|
||||
ast_copy_string(feature->app_args,app_args,FEATURE_APP_ARGS_LEN);
|
||||
|
||||
ast_copy_string(feature->exten, exten,sizeof(feature->exten));
|
||||
feature->operation=feature_exec_app;
|
||||
ast_set_flag(feature,AST_FEATURE_FLAG_NEEDSDTMF);
|
||||
|
||||
if (!strcasecmp(party,"caller"))
|
||||
ast_set_flag(feature,AST_FEATURE_FLAG_CALLER);
|
||||
else if (!strcasecmp(party, "callee"))
|
||||
ast_set_flag(feature,AST_FEATURE_FLAG_CALLEE);
|
||||
else {
|
||||
ast_log(LOG_NOTICE, "Invalid party specification for feature '%s', must be caller, or callee\n", var->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
ast_register_feature(feature);
|
||||
/* XXX do we need to free it if mallocd ? */
|
||||
|
||||
if (option_verbose >=1)
|
||||
ast_verbose(VERBOSE_PREFIX_2 "Mapping Feature '%s' to app '%s' with code '%s'\n", var->name, app, exten);
|
||||
if ((feature = find_feature(var->name))) {
|
||||
ast_log(LOG_WARNING, "Dynamic Feature '%s' specified more than once!\n", var->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(feature = ast_calloc(1, sizeof(*feature))))
|
||||
continue;
|
||||
|
||||
ast_copy_string(feature->sname, var->name, FEATURE_SNAME_LEN);
|
||||
ast_copy_string(feature->app, app, FEATURE_APP_LEN);
|
||||
ast_copy_string(feature->exten, exten, FEATURE_EXTEN_LEN);
|
||||
|
||||
if (app_args)
|
||||
ast_copy_string(feature->app_args, app_args, FEATURE_APP_ARGS_LEN);
|
||||
|
||||
ast_copy_string(feature->exten, exten, sizeof(feature->exten));
|
||||
feature->operation = feature_exec_app;
|
||||
ast_set_flag(feature, AST_FEATURE_FLAG_NEEDSDTMF);
|
||||
|
||||
/* Allow caller and calle to be specified for backwards compatability */
|
||||
if (!strcasecmp(activateon, "self") || !strcasecmp(activateon, "caller"))
|
||||
ast_set_flag(feature, AST_FEATURE_FLAG_ONSELF);
|
||||
else if (!strcasecmp(activateon, "peer") || !strcasecmp(activateon, "callee"))
|
||||
ast_set_flag(feature, AST_FEATURE_FLAG_ONPEER);
|
||||
else {
|
||||
ast_log(LOG_NOTICE, "Invalid 'ActivateOn' specification for feature '%s',"
|
||||
" must be 'self', or 'peer'\n", var->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ast_strlen_zero(activatedby))
|
||||
ast_set_flag(feature, AST_FEATURE_FLAG_BYBOTH);
|
||||
else if (!strcasecmp(activatedby, "caller"))
|
||||
ast_set_flag(feature, AST_FEATURE_FLAG_BYCALLER);
|
||||
else if (!strcasecmp(activatedby, "callee"))
|
||||
ast_set_flag(feature, AST_FEATURE_FLAG_BYCALLEE);
|
||||
else if (!strcasecmp(activatedby, "both"))
|
||||
ast_set_flag(feature, AST_FEATURE_FLAG_BYBOTH);
|
||||
else {
|
||||
ast_log(LOG_NOTICE, "Invalid 'ActivatedBy' specification for feature '%s',"
|
||||
" must be 'caller', or 'callee', or 'both'\n", var->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
ast_register_feature(feature);
|
||||
|
||||
if (option_verbose >= 1)
|
||||
ast_verbose(VERBOSE_PREFIX_2 "Mapping Feature '%s' to app '%s(%s)' with code '%s'\n", var->name, app, app_args, exten);
|
||||
}
|
||||
}
|
||||
ast_config_destroy(cfg);
|
||||
|
|
Reference in New Issue