From 3a874139d4ae0f38f5d2ffaf470b62d4f120dc2d Mon Sep 17 00:00:00 2001 From: Richard Mudgett Date: Fri, 20 Apr 2012 23:29:56 +0000 Subject: Fix connected-line/redirecting interception gosubs executing more than intended. * Redo ast_app_run_sub()/ast_app_exec_sub() to use a known return point so execution will stop after the routine returns there. (s@gosub_virtual_context:1) * Create ast_app_exec_macro() and ast_app_exec_sub() to run the macro and gosub application respectively with the parameter string already created. git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@362962 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- apps/app_stack.c | 21 ++++++ include/asterisk/app.h | 94 ++++++++++++++++++------ main/app.c | 196 ++++++++++++++++++++++++++++++++++++------------- 3 files changed, 240 insertions(+), 71 deletions(-) diff --git a/apps/app_stack.c b/apps/app_stack.c index 8f7ab0e78..355591e74 100644 --- a/apps/app_stack.c +++ b/apps/app_stack.c @@ -801,6 +801,8 @@ static struct agi_command gosub_agi_command = static int unload_module(void) { + struct ast_context *con; + ast_agi_unregister(ast_module_info->self, &gosub_agi_command); ast_unregister_application(app_return); @@ -811,11 +813,30 @@ static int unload_module(void) ast_custom_function_unregister(&peek_function); ast_custom_function_unregister(&stackpeek_function); + con = ast_context_find("gosub_virtual_context"); + if (con) { + /* leave nothing behind */ + ast_context_remove_extension2(con, "s", 1, NULL, 0); + ast_context_destroy(con, "app_stack"); + } + return 0; } static int load_module(void) { + struct ast_context *con; + + /* Create internal gosub return target to indicate successful completion. */ + con = ast_context_find_or_create(NULL, NULL, "gosub_virtual_context", "app_stack"); + if (!con) { + ast_log(LOG_ERROR, "'gosub_virtual_context' does not exist and unable to create\n"); + } else { + ast_add_extension2(con, 1, "s", 1, NULL, NULL, "NoOp", + ast_strdup("Internal Gosub call complete GOSUB_RETVAL=${GOSUB_RETVAL}"), + ast_free_ptr, "app_stack"); + } + ast_agi_register(ast_module_info->self, &gosub_agi_command); ast_register_application_xml(app_pop, pop_exec); diff --git a/include/asterisk/app.h b/include/asterisk/app.h index a9b0d3441..edf1c1c37 100644 --- a/include/asterisk/app.h +++ b/include/asterisk/app.h @@ -114,45 +114,97 @@ int ast_app_getdata(struct ast_channel *c, const char *prompt, char *s, int maxl /*! \brief Full version with audiofd and controlfd. NOTE: returns '2' on ctrlfd available, not '1' like other full functions */ int ast_app_getdata_full(struct ast_channel *c, const char *prompt, char *s, int maxlen, int timeout, int audiofd, int ctrlfd); +/*! + * \brief Run a macro on a channel, placing an optional second channel into autoservice. + * \since 11.0 + * + * \details + * This is a shorthand method that makes it very easy to run a + * macro on any given channel. It is perfectly reasonable to + * supply a NULL autoservice_chan here in case there is no + * channel to place into autoservice. + * + * \note It is very important that the autoservice_chan is not + * locked prior to calling. Otherwise, a deadlock could result. + * + * \param autoservice_chan A channel to place into autoservice while the macro is run + * \param macro_chan Channel to execute macro on. + * \param macro_args Macro application argument string. + * + * \retval 0 success + * \retval -1 on error + */ +int ast_app_exec_macro(struct ast_channel *autoservice_chan, struct ast_channel *macro_chan, const char *macro_args); + /*! * \since 1.8 - * \brief Run a macro on a channel, placing a second channel into autoservice. + * \brief Run a macro on a channel, placing an optional second channel into autoservice. * - * This is a shorthand method that makes it very easy to run a macro on any given - * channel. It is perfectly reasonable to supply a NULL autoservice_chan here in case - * there is no channel to place into autoservice. It is very important that the - * autoservice_chan parameter is not locked prior to calling ast_app_run_macro. A - * deadlock could result, otherwise. + * \details + * This is a shorthand method that makes it very easy to run a + * macro on any given channel. It is perfectly reasonable to + * supply a NULL autoservice_chan here in case there is no + * channel to place into autoservice. + * + * \note It is very important that the autoservice_chan is not + * locked prior to calling. Otherwise, a deadlock could result. * * \param autoservice_chan A channel to place into autoservice while the macro is run - * \param macro_chan The channel to run the macro on - * \param macro_name The name of the macro to run - * \param macro_args The arguments to pass to the macro + * \param macro_chan Channel to execute macro on. + * \param macro_name The name of the macro to run. + * \param macro_args The arguments to pass to the macro. + * * \retval 0 success - * \retval -1 failure + * \retval -1 on error */ int ast_app_run_macro(struct ast_channel *autoservice_chan, struct ast_channel *macro_chan, const char *macro_name, const char *macro_args); /*! * \since 11 - * \brief Run a subroutine on a channel, placing a second channel into autoservice. + * \brief Run a subroutine on a channel, placing an optional second channel into autoservice. + * + * \details + * This is a shorthand method that makes it very easy to run a + * subroutine on any given channel. It is perfectly reasonable + * to supply a NULL autoservice_chan here in case there is no + * channel to place into autoservice. * - * This is a shorthand method that makes it very easy to run a subroutine on any given - * channel. It is perfectly reasonable to supply a NULL autoservice_chan here in case - * there is no channel to place into autoservice. It is very important that the - * autoservice_chan parameter is not locked prior to calling ast_app_run_sub. A - * deadlock could result, otherwise. + * \note It is very important that the autoservice_chan is not + * locked prior to calling. Otherwise, a deadlock could result. * * \param autoservice_chan A channel to place into autoservice while the subroutine is run - * \param sub_chan The channel to run the subroutine on - * \param name The name of the subroutine to run - * \param args The arguments to pass to the subroutien + * \param sub_chan Channel to execute subroutine on. + * \param sub_args Gosub application argument string. + * + * \retval 0 success + * \retval -1 on error + */ +int ast_app_exec_sub(struct ast_channel *autoservice_chan, struct ast_channel *sub_chan, const char *sub_args); + +/*! + * \since 11 + * \brief Run a subroutine on a channel, placing an optional second channel into autoservice. + * + * \details + * This is a shorthand method that makes it very easy to run a + * subroutine on any given channel. It is perfectly reasonable + * to supply a NULL autoservice_chan here in case there is no + * channel to place into autoservice. + * + * \note It is very important that the autoservice_chan is not + * locked prior to calling. Otherwise, a deadlock could result. + * + * \param autoservice_chan A channel to place into autoservice while the subroutine is run + * \param sub_chan Channel to execute subroutine on. + * \param sub_location The location of the subroutine to run. + * \param sub_args The arguments to pass to the subroutine. + * * \retval 0 success - * \retval -1 failure + * \retval -1 on error */ int ast_app_run_sub(struct ast_channel *autoservice_chan, - struct ast_channel *sub_chan, const char *name, const char *args); + struct ast_channel *sub_chan, const char *sub_location, const char *sub_args); /*! * \brief Set voicemail function callbacks diff --git a/main/app.c b/main/app.c index 0634bb819..2bcf5f38f 100644 --- a/main/app.c +++ b/main/app.c @@ -247,79 +247,175 @@ int ast_app_getdata_full(struct ast_channel *c, const char *prompt, char *s, int return res; } -static int app_exec_dialplan(struct ast_channel *autoservice_chan, struct ast_channel *exec_chan, const char * const args, int use_gosub) +int ast_app_exec_macro(struct ast_channel *autoservice_chan, struct ast_channel *macro_chan, const char *macro_args) { - - struct ast_app *app; + struct ast_app *macro_app; int res; - char * app_type = use_gosub ? "GoSub" : "Macro"; - app = pbx_findapp(app_type); - if (!app) { - ast_log(LOG_WARNING, "Cannot run '%s' because the '%s' application is not available\n", args, app_type); + macro_app = pbx_findapp("Macro"); + if (!macro_app) { + ast_log(LOG_WARNING, + "Cannot run 'Macro(%s)'. The application is not available.\n", macro_args); return -1; } if (autoservice_chan) { ast_autoservice_start(autoservice_chan); } - res = pbx_exec(exec_chan, app, args); - if (use_gosub && !res) { - struct ast_pbx_args gosub_args = {{0}}; - struct ast_pbx *pbx = ast_channel_pbx(exec_chan); - /* supress warning about a pbx already being on the channel */ - ast_channel_pbx_set(exec_chan, NULL); - gosub_args.no_hangup_chan = 1; - ast_pbx_run_args(exec_chan, &gosub_args); - if (ast_channel_pbx(exec_chan)) { - ast_free(ast_channel_pbx(exec_chan)); - } - ast_channel_pbx_set(exec_chan, pbx); + + ast_debug(4, "%s Original location: %s,%s,%d\n", ast_channel_name(macro_chan), + ast_channel_context(macro_chan), ast_channel_exten(macro_chan), + ast_channel_priority(macro_chan)); + + res = pbx_exec(macro_chan, macro_app, macro_args); + ast_debug(4, "Macro exited with status %d\n", res); + + /* + * Assume anything negative from Macro is an error. + * Anything else is success. + */ + if (res < 0) { + res = -1; + } else { + res = 0; } + + ast_debug(4, "%s Ending location: %s,%s,%d\n", ast_channel_name(macro_chan), + ast_channel_context(macro_chan), ast_channel_exten(macro_chan), + ast_channel_priority(macro_chan)); + if (autoservice_chan) { ast_autoservice_stop(autoservice_chan); } return res; } -int ast_app_run_macro(struct ast_channel *autoservice_chan, struct ast_channel *macro_chan, const char *name, const char *args) +int ast_app_run_macro(struct ast_channel *autoservice_chan, struct ast_channel *macro_chan, const char *macro_name, const char *macro_args) { - char buf[1024]; - snprintf(buf, sizeof(buf), "%s%s%s", name, ast_strlen_zero(args) ? "" : ",", S_OR(args, "")); - return app_exec_dialplan(autoservice_chan, macro_chan, buf, 0); + int res; + char *args_str; + size_t args_len; + + if (ast_strlen_zero(macro_args)) { + return ast_app_exec_macro(autoservice_chan, macro_chan, macro_name); + } + + /* Create the Macro application argument string. */ + args_len = strlen(macro_name) + strlen(macro_args) + 2; + args_str = ast_malloc(args_len); + if (!args_str) { + return -1; + } + snprintf(args_str, args_len, "%s,%s", macro_name, macro_args); + + res = ast_app_exec_macro(autoservice_chan, macro_chan, args_str); + ast_free(args_str); + return res; } -int ast_app_run_sub(struct ast_channel *autoservice_chan, struct ast_channel *sub_chan, const char *location, const char *args) +int ast_app_exec_sub(struct ast_channel *autoservice_chan, struct ast_channel *sub_chan, const char *sub_args) { - char buf[1024]; - size_t offset = snprintf(buf, sizeof(buf), "%s", location); + struct ast_app *sub_app; + const char *saved_context; + const char *saved_exten; + int saved_priority; + int saved_autoloopflag; + int res; - /* need to bump the priority by one if we already have a pbx */ - if (ast_channel_pbx(sub_chan)) { - int iprio; - const char *priority = location; - const char *next = strchr(priority,','); + sub_app = pbx_findapp("Gosub"); + if (!sub_app) { + ast_log(LOG_WARNING, + "Cannot run 'Gosub(%s)'. The application is not available.\n", sub_args); + return -1; + } + if (autoservice_chan) { + ast_autoservice_start(autoservice_chan); + } - /* jump to the priority portion of the location */ - if (next) { - priority = next + 1; - } - next = strchr(priority,','); - if (next) { - priority = next + 1; - } - /* if the priority isn't numeric, it's as if we never took this branch... */ - if (sscanf(priority, "%d", &iprio)) { - offset = priority - location; - iprio++; - if (offset < sizeof(buf)) { - offset += snprintf(buf + offset, sizeof(buf) - offset, "%d", iprio); - } - } + ast_channel_lock(sub_chan); + + /* Save current dialplan location */ + saved_context = ast_strdupa(ast_channel_context(sub_chan)); + saved_exten = ast_strdupa(ast_channel_exten(sub_chan)); + saved_priority = ast_channel_priority(sub_chan); + + /* + * Save flag to restore at the end so we don't have to play with + * the priority in the gosub location string. + */ + saved_autoloopflag = ast_test_flag(ast_channel_flags(sub_chan), AST_FLAG_IN_AUTOLOOP); + ast_clear_flag(ast_channel_flags(sub_chan), AST_FLAG_IN_AUTOLOOP); + + /* Set known location for Gosub to return - 1 */ + ast_channel_context_set(sub_chan, "gosub_virtual_context"); + ast_channel_exten_set(sub_chan, "s"); + ast_channel_priority_set(sub_chan, 1 - 1); + + ast_debug(4, "%s Original location: %s,%s,%d\n", ast_channel_name(sub_chan), + saved_context, saved_exten, saved_priority); + + ast_channel_unlock(sub_chan); + res = pbx_exec(sub_chan, sub_app, sub_args); + ast_debug(4, "Gosub exited with status %d\n", res); + ast_channel_lock(sub_chan); + if (!res) { + struct ast_pbx_args gosub_args = {{0}}; + struct ast_pbx *saved_pbx; + + /* supress warning about a pbx already being on the channel */ + saved_pbx = ast_channel_pbx(sub_chan); + ast_channel_pbx_set(sub_chan, NULL); + + ast_channel_unlock(sub_chan); + gosub_args.no_hangup_chan = 1; + ast_pbx_run_args(sub_chan, &gosub_args); + ast_channel_lock(sub_chan); + + /* Restore pbx. */ + ast_free(ast_channel_pbx(sub_chan)); + ast_channel_pbx_set(sub_chan, saved_pbx); + } + + ast_debug(4, "%s Ending location: %s,%s,%d\n", ast_channel_name(sub_chan), + ast_channel_context(sub_chan), ast_channel_exten(sub_chan), + ast_channel_priority(sub_chan)); + + /* Restore flag */ + ast_set2_flag(ast_channel_flags(sub_chan), saved_autoloopflag, AST_FLAG_IN_AUTOLOOP); + + /* Restore dialplan location */ + ast_channel_context_set(sub_chan, saved_context); + ast_channel_exten_set(sub_chan, saved_exten); + ast_channel_priority_set(sub_chan, saved_priority); + + ast_channel_unlock(sub_chan); + + if (autoservice_chan) { + ast_autoservice_stop(autoservice_chan); + } + return res; +} + +int ast_app_run_sub(struct ast_channel *autoservice_chan, struct ast_channel *sub_chan, const char *sub_location, const char *sub_args) +{ + int res; + char *args_str; + size_t args_len; + + if (ast_strlen_zero(sub_args)) { + return ast_app_exec_sub(autoservice_chan, sub_chan, sub_location); } - if (!ast_strlen_zero(args) && offset < sizeof(buf)) { - snprintf(buf + offset, sizeof(buf) - offset, "(%s)", args); + + /* Create the Gosub application argument string. */ + args_len = strlen(sub_location) + strlen(sub_args) + 3; + args_str = ast_malloc(args_len); + if (!args_str) { + return -1; } - return app_exec_dialplan(autoservice_chan, sub_chan, buf, 1); + snprintf(args_str, args_len, "%s(%s)", sub_location, sub_args); + + res = ast_app_exec_sub(autoservice_chan, sub_chan, args_str); + ast_free(args_str); + return res; } static int (*ast_has_voicemail_func)(const char *mailbox, const char *folder) = NULL; -- cgit v1.2.3