Index: main/features.c =================================================================== --- main/features.c (revision 257187) +++ main/features.c (working copy) @@ -4419,25 +4419,33 @@ ast_unregister_features(); for (var = ast_variable_browse(cfg, "applicationmap"); var; var = var->next) { char *tmp_val = ast_strdupa(var->value); - char *exten, *activateon, *activatedby, *app, *app_args, *moh_class; + char *activateon; struct ast_call_feature *feature; + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(exten); + AST_APP_ARG(activatedby); + AST_APP_ARG(app); + AST_APP_ARG(app_args); + AST_APP_ARG(moh_class); + ); - /* 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,","); - activatedby = strsep(&tmp_val,","); - app = strsep(&tmp_val,","); - app_args = strsep(&tmp_val,","); - moh_class = strsep(&tmp_val,","); + AST_STANDARD_APP_ARGS(args, tmp_val); + if (strchr(args.app, '(')) { + /* New syntax */ + args.moh_class = args.app_args; + args.app_args = strchr(args.app, '('); + *args.app_args++ = '\0'; + if (args.app_args[strlen(args.app_args) - 1] == ')') { + args.app_args[strlen(args.app_args) - 1] = '\0'; + } + } - activateon = strsep(&activatedby, "/"); + activateon = strsep(&args.activatedby, "/"); /*! \todo XXX var_name or app_args ? */ - if (ast_strlen_zero(app) || ast_strlen_zero(exten) || ast_strlen_zero(activateon) || ast_strlen_zero(var->name)) { + if (ast_strlen_zero(args.app) || ast_strlen_zero(args.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); + args.app, args.exten, activateon, var->name); continue; } @@ -4449,20 +4457,23 @@ } AST_RWLIST_UNLOCK(&feature_list); - if (!(feature = ast_calloc(1, sizeof(*feature)))) - 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); + ast_copy_string(feature->app, args.app, FEATURE_APP_LEN); + ast_copy_string(feature->exten, args.exten, FEATURE_EXTEN_LEN); - if (app_args) - ast_copy_string(feature->app_args, app_args, FEATURE_APP_ARGS_LEN); + if (args.app_args) { + ast_copy_string(feature->app_args, args.app_args, FEATURE_APP_ARGS_LEN); + } - if (moh_class) - ast_copy_string(feature->moh_class, moh_class, FEATURE_MOH_LEN); - - ast_copy_string(feature->exten, exten, sizeof(feature->exten)); + if (args.moh_class) { + ast_copy_string(feature->moh_class, args.moh_class, FEATURE_MOH_LEN); + } + + ast_copy_string(feature->exten, args.exten, sizeof(feature->exten)); feature->operation = feature_exec_app; ast_set_flag(feature, AST_FEATURE_FLAG_NEEDSDTMF); @@ -4477,13 +4488,13 @@ continue; } - if (ast_strlen_zero(activatedby)) + if (ast_strlen_zero(args.activatedby)) ast_set_flag(feature, AST_FEATURE_FLAG_BYBOTH); - else if (!strcasecmp(activatedby, "caller")) + else if (!strcasecmp(args.activatedby, "caller")) ast_set_flag(feature, AST_FEATURE_FLAG_BYCALLER); - else if (!strcasecmp(activatedby, "callee")) + else if (!strcasecmp(args.activatedby, "callee")) ast_set_flag(feature, AST_FEATURE_FLAG_BYCALLEE); - else if (!strcasecmp(activatedby, "both")) + else if (!strcasecmp(args.activatedby, "both")) ast_set_flag(feature, AST_FEATURE_FLAG_BYBOTH); else { ast_log(LOG_NOTICE, "Invalid 'ActivatedBy' specification for feature '%s'," @@ -4492,8 +4503,8 @@ } ast_register_feature(feature); - - ast_verb(2, "Mapping Feature '%s' to app '%s(%s)' with code '%s'\n", var->name, app, app_args, exten); + + ast_verb(2, "Mapping Feature '%s' to app '%s(%s)' with code '%s'\n", var->name, args.app, args.app_args, args.exten); } ast_unregister_groups(); Index: configs/features.conf.sample =================================================================== --- configs/features.conf.sample (revision 257187) +++ configs/features.conf.sample (working copy) @@ -77,9 +77,11 @@ ; on the outbound channels, as well. Otherwise, only the original channel ; will have access to these features.) ; -; The syntax for declaring a dynamic feature is the following: +; The syntax for declaring a dynamic feature is any of the following: ; ; => ,[/],[,[,MOH_Class]] +; => ,[/],[,""[,MOH_Class]] +; => ,[/],([])[,MOH_Class] ; ; FeatureName -> This is the name of the feature used in when setting the ; DYNAMIC_FEATURES variable to enable usage of this feature. @@ -94,7 +96,9 @@ ; 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. +; AppArguments -> These are the arguments to be passed into the application. If you need +; commas in your arguments, you should use either the second or third +; syntax, above. ; MOH_Class -> This is the music on hold class to play while the idle ; channel waits for the feature to complete. If left blank, ; no music will be played. @@ -116,6 +120,10 @@ ;testfeature => #9,peer,Playback,tt-monkeys ;Allow both the caller and callee to play ; ;tt-monkeys to the opposite channel ; +; Set arbitrary channel variables, based upon CALLERID number (Note that the application +; argument contains commas) +;retrieveinfo => #8,peer,Set(ARRAY(CDR(mark),CDR(name))=${ODBC_FOO(${CALLERID(num)})}) +; ;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