diff options
Diffstat (limited to 'main/pbx.c')
-rw-r--r-- | main/pbx.c | 269 |
1 files changed, 254 insertions, 15 deletions
diff --git a/main/pbx.c b/main/pbx.c index 905310ccf..630ec6c83 100644 --- a/main/pbx.c +++ b/main/pbx.c @@ -5105,19 +5105,21 @@ static enum ast_pbx_result __ast_pbx_run(struct ast_channel *c, int error = 0; /* set an error conditions */ struct ast_pbx *pbx; - /* A little initial setup here */ - if (ast_channel_pbx(c)) { - ast_log(LOG_WARNING, "%s already has PBX structure??\n", ast_channel_name(c)); - /* XXX and now what ? */ - ast_free(ast_channel_pbx(c)); - } - if (!(pbx = ast_calloc(1, sizeof(*pbx)))) { - return -1; - } - ast_channel_pbx_set(c, pbx); - /* Set reasonable defaults */ - ast_channel_pbx(c)->rtimeoutms = 10000; - ast_channel_pbx(c)->dtimeoutms = 5000; + if (!args || !args->use_existing_pbx) { + /* A little initial setup here */ + if (ast_channel_pbx(c)) { + ast_log(LOG_WARNING, "%s already has PBX structure??\n", ast_channel_name(c)); + /* XXX and now what ? */ + ast_free(ast_channel_pbx(c)); + } + if (!(pbx = ast_calloc(1, sizeof(*pbx)))) { + return -1; + } + ast_channel_pbx_set(c, pbx); + /* Set reasonable defaults */ + ast_channel_pbx(c)->rtimeoutms = 10000; + ast_channel_pbx(c)->dtimeoutms = 5000; + } autoloopflag = ast_test_flag(ast_channel_flags(c), AST_FLAG_IN_AUTOLOOP); /* save value to restore at the end */ ast_set_flag(ast_channel_flags(c), AST_FLAG_IN_AUTOLOOP); @@ -5390,8 +5392,11 @@ static enum ast_pbx_result __ast_pbx_run(struct ast_channel *c, } ast_set2_flag(ast_channel_flags(c), autoloopflag, AST_FLAG_IN_AUTOLOOP); ast_clear_flag(ast_channel_flags(c), AST_FLAG_BRIDGE_HANGUP_RUN); /* from one round to the next, make sure this gets cleared */ - pbx_destroy(ast_channel_pbx(c)); - ast_channel_pbx_set(c, NULL); + + if (!args || !args->use_existing_pbx) { + pbx_destroy(ast_channel_pbx(c)); + ast_channel_pbx_set(c, NULL); + } if (!args || !args->no_hangup_chan) { ast_hangup(c); @@ -10844,3 +10849,237 @@ int ast_pbx_init(void) return (hints && hintdevices && statecbs) ? 0 : -1; } + +/*! \brief return the pieces of a goto style target if it's valid. + * + * \param chan Channel on which to test target validity + * \param goto_target Goto target string. ([[context,]extension,]priority) see below for examples + * \param context Parsed context (ast_str must be pre created) + * \param exten Parsed exten (ast_str must be pre created) + * \param priority Parsed priority (ast_str must be pre created) (can be null if not needed) + * \param varshead Parsed variables from the gosub (can be null if not needed) + * + * \return -1 on failure + * \return parsed priority integer on success (> 0) + * + * \note lock channel before calling + * + * \example goto_target valid format: priority + * \example goto_target valid format: label + * \example goto_target valid format: exten,priority + * \example goto_target valid format: exten,label + * \example goto_target valid format: context,exten,priority + * \example goto_target valid format: context,exten,label + * \example goto_target <valid_format>(args) + */ +int ast_pbx_exten_parse(struct ast_channel *chan, const char *goto_target, struct ast_str *context, struct ast_str *exten, struct ast_str *priority, struct varshead *varshead) +{ + int parse_args = 0; + char *target = ast_strdupa(goto_target); /* Target must be writable for AST_STANDARD_RAW_ARGS */ + char *start_args = target; + int ipriority; + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(context); + AST_APP_ARG(exten); + AST_APP_ARG(priority); + ); + AST_DECLARE_APP_ARGS(args2, + AST_APP_ARG(argval)[100]; + ); + + ast_str_truncate(context, 0); + ast_str_truncate(exten, 0); + + /* Find the args (if any) */ + if (varshead && ((start_args = strchr(start_args, '(')) != NULL)) { + char *end_args; + + parse_args = 1; + *start_args = 0; + start_args++; + + if ((end_args = strchr(start_args, ')')) != NULL) { + *end_args = 0; + } + else { + ast_log(LOG_WARNING, "Ouch. No closing paren for Gosub parameters: '%s'?\n", goto_target); + } + } + + AST_STANDARD_RAW_ARGS(args, target); + + if (priority) { + ast_str_truncate(priority, 0); + } + + if (ast_strlen_zero(goto_target)) { + ast_log(LOG_WARNING, "goto_target cannot be empty ([[context,]extension,]priority)\n"); + return -1; + } + + if (ast_strlen_zero(args.exten)) { + /* Only a priority in this one */ + args.priority = args.context; + args.exten = (char *) ast_channel_exten(chan); + args.context = (char *) ast_channel_context(chan); + } else if (ast_strlen_zero(args.priority)) { + /* Only an extension and priority in this one */ + args.priority = args.exten; + args.exten = args.context; + args.context = (char *) ast_channel_context(chan); + } + + if (sscanf(args.priority, "%d", &ipriority) > 0) { + if (!ast_exists_extension(chan, args.context, args.exten, ipriority, S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) { + ast_log(LOG_WARNING, "priority based goto target not found: %s\n", goto_target); + ipriority = 0; + return -1; + } + } else { + if (!(ipriority = ast_findlabel_extension(chan, args.context, args.exten, args.priority, S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL)))) { + ast_log(LOG_WARNING, "label based goto target not found: %s\n", goto_target); + ipriority = 0; + return -1; + } + } + + ast_str_set(&context, 0, "%s", args.context); + ast_str_set(&exten, 0, "%s", args.exten); + + if (priority) { + ast_str_set(&priority, 0, "%s", args.priority); + } + + if (parse_args) { + int i; + struct ast_str *argument_name = ast_str_create(64); + struct ast_var_t *variable; + int argument_num = 1; + + AST_STANDARD_RAW_ARGS(args2, start_args); + + for (i = 0; i < args2.argc; i++) { + ast_str_truncate(argument_name, 0); + ast_str_append(&argument_name, 0, "ARG%d", argument_num); + + variable = ast_var_assign(ast_str_buffer(argument_name), args2.argval[i]); + AST_LIST_INSERT_TAIL(varshead, variable, entries); + argument_num++; + + ast_log(LOG_WARNING, "Setting '%s' to '%s' %p\n", ast_str_buffer(argument_name), args2.argval[i], variable); + ast_debug(1, "Setting '%s' to '%s'\n", ast_str_buffer(argument_name), args2.argval[i]); + } + + ast_free(argument_name); + } + + return ipriority; +} + +/*! \brief run dialplan on a channel, optionally restoring the channel to the previous dialplan location + * + * \param chan Channel on which to run dialplan + * \param args Gosub style args in the form of context,exten,priority(arg1,arg2,argn,...) + * \param restore_dialplan_location 1/0 whether to restore the channel's dialplan location to where it was before we were called + * + * \retval AST_PBX_SUCCESS on success + * \retval AST_PBX_ERROR on error + * + */ +enum ast_pbx_result ast_pbx_exten_run_parseargs(struct ast_channel *chan, const char *gosub_args, int restore_dialplan_location) { + struct varshead varshead; + struct ast_var_t *variable; + struct ast_str *context = ast_str_create(64); + struct ast_str *exten = ast_str_create(64); + int ipriority; + enum ast_pbx_result res = AST_PBX_ERROR; + + memset(&varshead, 0, sizeof(varshead)); + + if ((ipriority = ast_pbx_exten_parse(chan, gosub_args, context, exten, NULL, &varshead)) > 0) { + res = ast_pbx_exten_run(chan, ast_str_buffer(context), ast_str_buffer(exten), ipriority, &varshead, 1); + } + + AST_LIST_TRAVERSE_SAFE_BEGIN(&varshead, variable, entries) { + AST_LIST_REMOVE_CURRENT(entries); + ast_var_delete(variable); + } + AST_LIST_TRAVERSE_SAFE_END; + + ast_free(context); + ast_free(exten); + + return res; +} + +/*! \brief run dialplan on a channel, optionally restoring the channel to the previous dialplan location + * + * \param chan Channel on which to run dialplan + * \param context Context in which to execute + * \param exten Exten within the context + * \param priority Priority within the exten + * \param varshead Array of arguments to pass to destination. Args will be set in the form of ARG1,ARG2,ARGn,... + * \param restore_dialplan_location 1/0 whether to restore the channel's dialplan location to where it was before we were called + * + * \retval AST_PBX_HANGUP on error + * \retval AST_PBX_OK on success + * + */ +enum ast_pbx_result ast_pbx_exten_run(struct ast_channel *chan, const char *context, const char *exten, int priority, struct varshead *varshead, int restore_dialplan_location) +{ + struct ast_str *backup_context; + struct ast_str *backup_exten; + int backup_priority; + enum ast_pbx_result res; + struct ast_pbx_args run_args; + struct ast_var_t *variable; + + /* Back up current dialplan location */ + if (restore_dialplan_location) { + backup_context = ast_str_create(64); + backup_exten = ast_str_create(64); + + ast_str_set(&backup_context, 0, "%s", ast_channel_context(chan)); + ast_str_set(&backup_exten, 0, "%s", ast_channel_exten(chan)); + backup_priority = ast_channel_priority(chan); + } + + /* New dialplan location */ + ast_channel_context_set(chan, context); + ast_channel_exten_set(chan, exten); + ast_channel_priority_set(chan, priority); + + /* set args, if any */ + if (varshead) { + AST_LIST_TRAVERSE(varshead, variable, entries) { + pbx_builtin_pushvar_helper(chan, ast_var_name(variable), ast_var_value(variable)); + } + } + + memset(&run_args, 0, sizeof(run_args)); + if (ast_channel_pbx(chan)) { + run_args.use_existing_pbx = 1; + } + run_args.no_hangup_chan = 1; + res = __ast_pbx_run(chan, &run_args); + + /* Allow use of previously set variables. Ie: if there was previously ARG1,ARG2,etc set on the channel + we want access to those old values since the dialplan we ran is now finished */ + if (varshead) { + AST_LIST_TRAVERSE(varshead, variable, entries) { + pbx_builtin_setvar_helper(chan, ast_var_name(variable), NULL); + } + } + + if (restore_dialplan_location) { + /* Restore current dialplan location */ + ast_channel_context_set(chan, ast_str_buffer(backup_context)); + ast_channel_exten_set(chan, ast_str_buffer(backup_exten)); + ast_channel_priority_set(chan, backup_priority); + + ast_free(backup_context); + ast_free(backup_exten); + } + + return res; +} |