diff options
Diffstat (limited to 'apps')
-rw-r--r-- | apps/app_dial.c | 18 | ||||
-rw-r--r-- | apps/app_followme.c | 6 | ||||
-rw-r--r-- | apps/app_queue.c | 2 | ||||
-rw-r--r-- | apps/app_stack.c | 527 |
4 files changed, 453 insertions, 100 deletions
diff --git a/apps/app_dial.c b/apps/app_dial.c index 1ebad3412..002d2bef7 100644 --- a/apps/app_dial.c +++ b/apps/app_dial.c @@ -2280,7 +2280,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast if (ast_test_flag64(&opts, OPT_PREDIAL_CALLER) && !ast_strlen_zero(opt_args[OPT_ARG_PREDIAL_CALLER])) { ast_replace_subargument_delimiter(opt_args[OPT_ARG_PREDIAL_CALLER]); - ast_app_exec_sub(NULL, chan, opt_args[OPT_ARG_PREDIAL_CALLER]); + ast_app_exec_sub(NULL, chan, opt_args[OPT_ARG_PREDIAL_CALLER], 0); } /* loop through the list of dial destinations */ @@ -2550,12 +2550,18 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast if (ast_test_flag64(&opts, OPT_PREDIAL_CALLEE) && !ast_strlen_zero(opt_args[OPT_ARG_PREDIAL_CALLEE]) && !AST_LIST_EMPTY(&out_chans)) { - ast_autoservice_start(chan); + const char *predial_callee; + ast_replace_subargument_delimiter(opt_args[OPT_ARG_PREDIAL_CALLEE]); - AST_LIST_TRAVERSE(&out_chans, tmp, node) { - ast_pre_call(tmp->chan, opt_args[OPT_ARG_PREDIAL_CALLEE]); + predial_callee = ast_app_expand_sub_args(chan, opt_args[OPT_ARG_PREDIAL_CALLEE]); + if (predial_callee) { + ast_autoservice_start(chan); + AST_LIST_TRAVERSE(&out_chans, tmp, node) { + ast_pre_call(tmp->chan, predial_callee); + } + ast_autoservice_stop(chan); + ast_free((char *) predial_callee); } - ast_autoservice_stop(chan); } /* Start all outgoing calls */ @@ -2884,7 +2890,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast } } if (gosub_args) { - res9 = ast_app_exec_sub(chan, peer, gosub_args); + res9 = ast_app_exec_sub(chan, peer, gosub_args, 0); ast_free(gosub_args); } else { ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n"); diff --git a/apps/app_followme.c b/apps/app_followme.c index f44d453ad..275ed43c5 100644 --- a/apps/app_followme.c +++ b/apps/app_followme.c @@ -1386,14 +1386,15 @@ static int app_exec(struct ast_channel *chan, const char *data) if (ast_test_flag(&targs->followmeflags, FOLLOWMEFLAG_PREDIAL_CALLEE) && !ast_strlen_zero(opt_args[FOLLOWMEFLAG_ARG_PREDIAL_CALLEE])) { ast_replace_subargument_delimiter(opt_args[FOLLOWMEFLAG_ARG_PREDIAL_CALLEE]); - targs->predial_callee = opt_args[FOLLOWMEFLAG_ARG_PREDIAL_CALLEE]; + targs->predial_callee = + ast_app_expand_sub_args(chan, opt_args[FOLLOWMEFLAG_ARG_PREDIAL_CALLEE]); } /* PREDIAL: Run gosub on the caller's channel */ if (ast_test_flag(&targs->followmeflags, FOLLOWMEFLAG_PREDIAL_CALLER) && !ast_strlen_zero(opt_args[FOLLOWMEFLAG_ARG_PREDIAL_CALLER])) { ast_replace_subargument_delimiter(opt_args[FOLLOWMEFLAG_ARG_PREDIAL_CALLER]); - ast_app_exec_sub(NULL, chan, opt_args[FOLLOWMEFLAG_ARG_PREDIAL_CALLER]); + ast_app_exec_sub(NULL, chan, opt_args[FOLLOWMEFLAG_ARG_PREDIAL_CALLER], 0); } /* Forget the 'N' option if the call is already up. */ @@ -1522,6 +1523,7 @@ outrun: if (!ast_strlen_zero(targs->namerecloc)) { unlink(targs->namerecloc); } + ast_free((char *) targs->predial_callee); ast_party_connected_line_free(&targs->connected_in); ast_party_connected_line_free(&targs->connected_out); ast_free(targs); diff --git a/apps/app_queue.c b/apps/app_queue.c index 245b45173..c7cd40a91 100644 --- a/apps/app_queue.c +++ b/apps/app_queue.c @@ -5352,7 +5352,7 @@ static int try_calling(struct queue_ent *qe, const struct ast_flags opts, char * } } if (gosub_args) { - ast_app_exec_sub(qe->chan, peer, gosub_args); + ast_app_exec_sub(qe->chan, peer, gosub_args, 0); ast_free(gosub_args); } else { ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n"); diff --git a/apps/app_stack.c b/apps/app_stack.c index 508a43c4e..70fb7282d 100644 --- a/apps/app_stack.c +++ b/apps/app_stack.c @@ -200,10 +200,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") </agi> ***/ -static const char * const app_gosub = "Gosub"; -static const char * const app_gosubif = "GosubIf"; -static const char * const app_return = "Return"; -static const char * const app_pop = "StackPop"; +static const char app_gosub[] = "Gosub"; +static const char app_gosubif[] = "GosubIf"; +static const char app_return[] = "Return"; +static const char app_pop[] = "StackPop"; static void gosub_free(void *data); @@ -218,11 +218,14 @@ struct gosub_stack_frame { unsigned char arguments; struct varshead varshead; int priority; - unsigned int is_agi:1; + /*! TRUE if the return location marks the end of a special routine. */ + unsigned int is_special:1; char *context; char extension[0]; }; +AST_LIST_HEAD(gosub_stack_list, gosub_stack_frame); + static int frame_set_var(struct ast_channel *chan, struct gosub_stack_frame *frame, const char *var, const char *value) { struct ast_var_t *variables; @@ -290,8 +293,9 @@ static struct gosub_stack_frame *gosub_allocate_frame(const char *context, const static void gosub_free(void *data) { - AST_LIST_HEAD(, gosub_stack_frame) *oldlist = data; + struct gosub_stack_list *oldlist = data; struct gosub_stack_frame *oldframe; + AST_LIST_LOCK(oldlist); while ((oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries))) { gosub_release_frame(NULL, oldframe); @@ -305,7 +309,8 @@ static int pop_exec(struct ast_channel *chan, const char *data) { struct ast_datastore *stack_store; struct gosub_stack_frame *oldframe; - AST_LIST_HEAD(, gosub_stack_frame) *oldlist; + struct gosub_stack_list *oldlist; + int res = 0; ast_channel_lock(chan); if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) { @@ -316,23 +321,30 @@ static int pop_exec(struct ast_channel *chan, const char *data) oldlist = stack_store->data; AST_LIST_LOCK(oldlist); - oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries); - AST_LIST_UNLOCK(oldlist); - + oldframe = AST_LIST_FIRST(oldlist); if (oldframe) { - gosub_release_frame(chan, oldframe); + if (oldframe->is_special) { + ast_debug(1, "%s attempted to pop special return location.\n", app_pop); + + /* Abort the special routine dialplan execution. Dialplan programming error. */ + res = -1; + } else { + AST_LIST_REMOVE_HEAD(oldlist, entries); + gosub_release_frame(chan, oldframe); + } } else { ast_debug(1, "%s called with an empty gosub stack\n", app_pop); } + AST_LIST_UNLOCK(oldlist); ast_channel_unlock(chan); - return 0; + return res; } static int return_exec(struct ast_channel *chan, const char *data) { struct ast_datastore *stack_store; struct gosub_stack_frame *oldframe; - AST_LIST_HEAD(, gosub_stack_frame) *oldlist; + struct gosub_stack_list *oldlist; const char *retval = data; int res = 0; @@ -352,12 +364,24 @@ static int return_exec(struct ast_channel *chan, const char *data) ast_log(LOG_ERROR, "Return without Gosub: stack is empty\n"); ast_channel_unlock(chan); return -1; - } else if (oldframe->is_agi) { - /* Exit from AGI */ + } + if (oldframe->is_special) { + /* Exit from special routine. */ res = -1; } - ast_explicit_goto(chan, oldframe->context, oldframe->extension, oldframe->priority); + /* + * We cannot use ast_explicit_goto() because we MUST restore + * what was there before. Channels that do not have a PBX may + * not have the context or exten set. + */ + ast_channel_context_set(chan, oldframe->context); + ast_channel_exten_set(chan, oldframe->extension); + if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP)) { + --oldframe->priority; + } + ast_channel_priority_set(chan, oldframe->priority); + gosub_release_frame(chan, oldframe); /* Set a return value, if any */ @@ -366,10 +390,95 @@ static int return_exec(struct ast_channel *chan, const char *data) return res; } +/*! + * \internal + * \brief Add missing context and/or exten to Gosub application argument string. + * \since 11.0 + * + * \param chan Channel to obtain context/exten. + * \param args Gosub application argument string. + * + * \details + * Fills in the optional context and exten from the given channel. + * Convert: [[context,]exten,]priority[(arg1[,...][,argN])] + * To: context,exten,priority[(arg1[,...][,argN])] + * + * \retval expanded Gosub argument string on success. Must be freed. + * \retval NULL on error. + * + * \note The parsing needs to be kept in sync with the + * gosub_exec() argument format. + */ +static const char *expand_gosub_args(struct ast_channel *chan, const char *args) +{ + int len; + char *parse; + char *label; + char *new_args; + const char *context; + const char *exten; + const char *pri; + + /* Separate the context,exten,pri from the optional routine arguments. */ + parse = ast_strdupa(args); + label = strsep(&parse, "("); + if (parse) { + char *endparen; + + endparen = strrchr(parse, ')'); + if (endparen) { + *endparen = '\0'; + } else { + ast_log(LOG_WARNING, "Ouch. No closing paren: '%s'?\n", args); + } + } + + /* Split context,exten,pri */ + context = strsep(&label, ","); + exten = strsep(&label, ","); + pri = strsep(&label, ","); + if (!exten) { + /* Only a priority in this one */ + pri = context; + exten = NULL; + context = NULL; + } else if (!pri) { + /* Only an extension and priority in this one */ + pri = exten; + exten = context; + context = NULL; + } + + ast_channel_lock(chan); + if (ast_strlen_zero(exten)) { + exten = ast_channel_exten(chan); + } + if (ast_strlen_zero(context)) { + context = ast_channel_context(chan); + } + len = strlen(context) + strlen(exten) + strlen(pri) + 3; + if (!ast_strlen_zero(parse)) { + len += 2 + strlen(parse); + } + new_args = ast_malloc(len); + if (new_args) { + if (ast_strlen_zero(parse)) { + snprintf(new_args, len, "%s,%s,%s", context, exten, pri); + } else { + snprintf(new_args, len, "%s,%s,%s(%s)", context, exten, pri, parse); + } + } + ast_channel_unlock(chan); + + ast_debug(4, "Gosub args:%s new_args:%s\n", args, new_args ? new_args : ""); + + return new_args; +} + static int gosub_exec(struct ast_channel *chan, const char *data) { struct ast_datastore *stack_store; - AST_LIST_HEAD(, gosub_stack_frame) *oldlist; + struct gosub_stack_list *oldlist; struct gosub_stack_frame *newframe; struct gosub_stack_frame *lastframe; char argname[15]; @@ -560,7 +669,7 @@ static int gosubif_exec(struct ast_channel *chan, const char *data) static int local_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) { struct ast_datastore *stack_store; - AST_LIST_HEAD(, gosub_stack_frame) *oldlist; + struct gosub_stack_list *oldlist; struct gosub_stack_frame *frame; struct ast_var_t *variables; @@ -595,7 +704,7 @@ static int local_read(struct ast_channel *chan, const char *cmd, char *data, cha static int local_write(struct ast_channel *chan, const char *cmd, char *var, const char *value) { struct ast_datastore *stack_store; - AST_LIST_HEAD(, gosub_stack_frame) *oldlist; + struct gosub_stack_list *oldlist; struct gosub_stack_frame *frame; ast_channel_lock(chan); @@ -662,7 +771,7 @@ static struct ast_custom_function peek_function = { static int stackpeek_read(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **str, ssize_t len) { struct ast_datastore *stack_store; - AST_LIST_HEAD(, gosub_stack_frame) *oldlist; + struct gosub_stack_list *oldlist; struct gosub_stack_frame *frame; int n; AST_DECLARE_APP_ARGS(args, @@ -729,6 +838,7 @@ static int stackpeek_read(struct ast_channel *chan, const char *cmd, char *data, break; default: ast_log(LOG_ERROR, "Unknown argument '%s' to STACK_PEEK\n", args.which); + break; } AST_LIST_UNLOCK(oldlist); @@ -742,11 +852,211 @@ static struct ast_custom_function stackpeek_function = { .read2 = stackpeek_read, }; +/*! + * \internal + * \brief Pop stack frames until remove a special return location. + * \since 11.0 + * + * \param chan Channel to balance stack on. + * + * \note The channel is already locked when called. + * + * \return Nothing + */ +static void balance_stack(struct ast_channel *chan) +{ + struct ast_datastore *stack_store; + struct gosub_stack_list *oldlist; + struct gosub_stack_frame *oldframe; + int found; + + stack_store = ast_channel_datastore_find(chan, &stack_info, NULL); + if (!stack_store) { + ast_log(LOG_WARNING, "No %s stack allocated.\n", app_gosub); + return; + } + + oldlist = stack_store->data; + AST_LIST_LOCK(oldlist); + do { + oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries); + if (!oldframe) { + break; + } + found = oldframe->is_special; + gosub_release_frame(chan, oldframe); + } while (!found); + AST_LIST_UNLOCK(oldlist); +} + +/*! + * \internal + * \brief Run a subroutine on a channel. + * \since 11.0 + * + * \note Absolutely _NO_ channel locks should be held before calling this function. + * + * \param chan Channel to execute subroutine on. + * \param sub_args Gosub application argument string. + * \param ignore_hangup TRUE if a hangup does not stop execution of the routine. + * + * \retval 0 success + * \retval -1 on error + */ +static int gosub_run(struct ast_channel *chan, const char *sub_args, int ignore_hangup) +{ + const char *saved_context; + const char *saved_exten; + int saved_priority; + int saved_hangup_flags; + int saved_autoloopflag; + int res; + + ast_channel_lock(chan); + + ast_verb(3, "%s Internal %s(%s) start\n", + ast_channel_name(chan), app_gosub, sub_args); + + /* Save non-hangup softhangup flags. */ + saved_hangup_flags = ast_channel_softhangup_internal_flag(chan) + & (AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE); + if (saved_hangup_flags) { + ast_channel_clear_softhangup(chan, + AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE); + } + + /* Save autoloop flag */ + saved_autoloopflag = ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP); + ast_set_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP); + + /* Save current dialplan location */ + saved_context = ast_strdupa(ast_channel_context(chan)); + saved_exten = ast_strdupa(ast_channel_exten(chan)); + saved_priority = ast_channel_priority(chan); + + ast_debug(4, "%s Original location: %s,%s,%d\n", ast_channel_name(chan), + saved_context, saved_exten, saved_priority); + + ast_channel_unlock(chan); + res = gosub_exec(chan, sub_args); + ast_debug(4, "%s exited with status %d\n", app_gosub, res); + ast_channel_lock(chan); + if (!res) { + struct ast_datastore *stack_store; + + /* Mark the return location as special. */ + stack_store = ast_channel_datastore_find(chan, &stack_info, NULL); + if (!stack_store) { + /* Should never happen! */ + ast_log(LOG_ERROR, "No %s stack!\n", app_gosub); + res = -1; + } else { + struct gosub_stack_list *oldlist; + struct gosub_stack_frame *cur; + + oldlist = stack_store->data; + cur = AST_LIST_FIRST(oldlist); + cur->is_special = 1; + } + } + if (!res) { + int found = 0; /* set if we find at least one match */ + + /* + * Run gosub body autoloop. + * + * Note that this loop is inverted from the normal execution + * loop because we just executed the Gosub application as the + * first extension of the autoloop. + */ + do { + /* Check for hangup. */ + if (ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_UNBRIDGE) { + saved_hangup_flags |= AST_SOFTHANGUP_UNBRIDGE; + ast_channel_clear_softhangup(chan, AST_SOFTHANGUP_UNBRIDGE); + } + if (ast_check_hangup(chan)) { + if (ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO) { + ast_log(LOG_ERROR, "%s An async goto just messed up our execution location.\n", + ast_channel_name(chan)); + break; + } + if (!ignore_hangup) { + break; + } + } + + /* Next dialplan priority. */ + ast_channel_priority_set(chan, ast_channel_priority(chan) + 1); + + ast_channel_unlock(chan); + res = ast_spawn_extension(chan, ast_channel_context(chan), + ast_channel_exten(chan), ast_channel_priority(chan), + S_COR(ast_channel_caller(chan)->id.number.valid, + ast_channel_caller(chan)->id.number.str, NULL), + &found, 1); + ast_channel_lock(chan); + } while (!res); + if (found && res) { + /* Something bad happened, or a hangup has been requested. */ + ast_debug(1, "Spawn extension (%s,%s,%d) exited with %d on '%s'\n", + ast_channel_context(chan), ast_channel_exten(chan), + ast_channel_priority(chan), res, ast_channel_name(chan)); + ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", + ast_channel_context(chan), ast_channel_exten(chan), + ast_channel_priority(chan), ast_channel_name(chan)); + } + + /* Did the routine return? */ + if (ast_channel_priority(chan) == saved_priority + && !strcmp(ast_channel_context(chan), saved_context) + && !strcmp(ast_channel_exten(chan), saved_exten)) { + ast_verb(3, "%s Internal %s(%s) complete GOSUB_RETVAL=%s\n", + ast_channel_name(chan), app_gosub, sub_args, + S_OR(pbx_builtin_getvar_helper(chan, "GOSUB_RETVAL"), "")); + } else { + ast_log(LOG_NOTICE, "%s Abnormal '%s(%s)' exit. Popping routine return locations.\n", + ast_channel_name(chan), app_gosub, sub_args); + balance_stack(chan); + pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", ""); + } + + /* We executed the requested subroutine to the best of our ability. */ + res = 0; + } + + ast_debug(4, "%s Ending location: %s,%s,%d\n", ast_channel_name(chan), + ast_channel_context(chan), ast_channel_exten(chan), + ast_channel_priority(chan)); + + /* Restore dialplan location */ + if (!(ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO)) { + ast_channel_context_set(chan, saved_context); + ast_channel_exten_set(chan, saved_exten); + ast_channel_priority_set(chan, saved_priority); + } + + /* Restore autoloop flag */ + ast_set2_flag(ast_channel_flags(chan), saved_autoloopflag, AST_FLAG_IN_AUTOLOOP); + + /* Restore non-hangup softhangup flags. */ + if (saved_hangup_flags) { + ast_softhangup_nolock(chan, saved_hangup_flags); + } + + ast_channel_unlock(chan); + + return res; +} + static int handle_gosub(struct ast_channel *chan, AGI *agi, int argc, const char * const *argv) { - int old_priority, priority; - char old_context[AST_MAX_CONTEXT], old_extension[AST_MAX_EXTENSION]; - struct ast_app *theapp; + int res; + int priority; + int old_autoloopflag; + int old_priority; + const char *old_context; + const char *old_extension; char *gosub_args; if (argc < 4 || argc > 5) { @@ -770,80 +1080,125 @@ static int handle_gosub(struct ast_channel *chan, AGI *agi, int argc, const char return RESULT_FAILURE; } - /* Save previous location, since we're going to change it */ - ast_copy_string(old_context, ast_channel_context(chan), sizeof(old_context)); - ast_copy_string(old_extension, ast_channel_exten(chan), sizeof(old_extension)); - old_priority = ast_channel_priority(chan); - - if (!(theapp = pbx_findapp("Gosub"))) { - ast_log(LOG_ERROR, "Gosub() cannot be found in the list of loaded applications\n"); - ast_agi_send(agi->fd, chan, "503 result=-2 Gosub is not loaded\n"); - return RESULT_FAILURE; - } - - /* Apparently, if you run ast_pbx_run on a channel that already has a pbx - * structure, you need to add 1 to the priority to get it to go to the - * right place. But if it doesn't have a pbx structure, then leaving off - * the 1 is the right thing to do. See how this code differs when we - * call a Gosub for the CALLEE channel in Dial or Queue. - */ if (argc == 5) { - if (asprintf(&gosub_args, "%s,%s,%d(%s)", argv[1], argv[2], priority + (ast_channel_pbx(chan) ? 1 : 0), argv[4]) < 0) { + if (asprintf(&gosub_args, "%s,%s,%d(%s)", argv[1], argv[2], priority, argv[4]) < 0) { ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno)); gosub_args = NULL; } } else { - if (asprintf(&gosub_args, "%s,%s,%d", argv[1], argv[2], priority + (ast_channel_pbx(chan) ? 1 : 0)) < 0) { + if (asprintf(&gosub_args, "%s,%s,%d", argv[1], argv[2], priority) < 0) { ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno)); gosub_args = NULL; } } + if (!gosub_args) { + ast_agi_send(agi->fd, chan, "503 result=-2 Memory allocation failure\n"); + return RESULT_FAILURE; + } + + ast_channel_lock(chan); - if (gosub_args) { - int res; + ast_verb(3, "%s AGI %s(%s) start\n", ast_channel_name(chan), app_gosub, gosub_args); - ast_debug(1, "Trying gosub with arguments '%s'\n", gosub_args); + /* Save autoloop flag */ + old_autoloopflag = ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP); + ast_set_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP); + + /* Save previous location, since we're going to change it */ + old_context = ast_strdupa(ast_channel_context(chan)); + old_extension = ast_strdupa(ast_channel_exten(chan)); + old_priority = ast_channel_priority(chan); + + ast_debug(4, "%s Original location: %s,%s,%d\n", ast_channel_name(chan), + old_context, old_extension, old_priority); + ast_channel_unlock(chan); - if ((res = pbx_exec(chan, theapp, gosub_args)) == 0) { - struct ast_pbx *pbx = ast_channel_pbx(chan); - struct ast_pbx_args args; - struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL); - AST_LIST_HEAD(,gosub_stack_frame) *oldlist; + res = gosub_exec(chan, gosub_args); + if (!res) { + struct ast_datastore *stack_store; + + /* Mark the return location as special. */ + ast_channel_lock(chan); + stack_store = ast_channel_datastore_find(chan, &stack_info, NULL); + if (!stack_store) { + /* Should never happen! */ + ast_log(LOG_ERROR, "No %s stack!\n", app_gosub); + res = -1; + } else { + struct gosub_stack_list *oldlist; struct gosub_stack_frame *cur; - if (!stack_store) { - ast_log(LOG_WARNING, "No GoSub stack remaining after AGI GoSub execution.\n"); - ast_free(gosub_args); - return RESULT_FAILURE; - } + oldlist = stack_store->data; cur = AST_LIST_FIRST(oldlist); - cur->is_agi = 1; - - memset(&args, 0, sizeof(args)); - args.no_hangup_chan = 1; - /* Suppress warning about PBX already existing */ - ast_channel_pbx_set(chan, NULL); - ast_agi_send(agi->fd, chan, "100 result=0 Trying...\n"); - ast_pbx_run_args(chan, &args); - ast_agi_send(agi->fd, chan, "200 result=0 Gosub complete\n"); - if (ast_channel_pbx(chan)) { - ast_free(ast_channel_pbx(chan)); - } - ast_channel_pbx_set(chan, pbx); + cur->is_special = 1; + } + ast_channel_unlock(chan); + } + if (!res) { + struct ast_pbx *pbx; + struct ast_pbx_args args; + int abnormal_exit; + + memset(&args, 0, sizeof(args)); + args.no_hangup_chan = 1; + + ast_channel_lock(chan); + + /* Next dialplan priority. */ + ast_channel_priority_set(chan, ast_channel_priority(chan) + 1); + + /* Suppress warning about PBX already existing */ + pbx = ast_channel_pbx(chan); + ast_channel_pbx_set(chan, NULL); + ast_channel_unlock(chan); + + ast_agi_send(agi->fd, chan, "100 result=0 Trying...\n"); + ast_pbx_run_args(chan, &args); + + ast_channel_lock(chan); + ast_free(ast_channel_pbx(chan)); + ast_channel_pbx_set(chan, pbx); + + /* Did the routine return? */ + if (ast_channel_priority(chan) == old_priority + && !strcmp(ast_channel_context(chan), old_context) + && !strcmp(ast_channel_exten(chan), old_extension)) { + ast_verb(3, "%s AGI %s(%s) complete GOSUB_RETVAL=%s\n", + ast_channel_name(chan), app_gosub, gosub_args, + S_OR(pbx_builtin_getvar_helper(chan, "GOSUB_RETVAL"), "")); + abnormal_exit = 0; } else { - ast_agi_send(agi->fd, chan, "200 result=%d Gosub failed\n", res); + ast_log(LOG_NOTICE, "%s Abnormal AGI %s(%s) exit. Popping routine return locations.\n", + ast_channel_name(chan), app_gosub, gosub_args); + balance_stack(chan); + pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", ""); + abnormal_exit = 1; } - ast_free(gosub_args); + ast_channel_unlock(chan); + + ast_agi_send(agi->fd, chan, "200 result=0 Gosub complete%s\n", + abnormal_exit ? " (abnormal exit)" : ""); } else { - ast_agi_send(agi->fd, chan, "503 result=-2 Memory allocation failure\n"); - return RESULT_FAILURE; + ast_agi_send(agi->fd, chan, "200 result=%d Gosub failed\n", res); } + /* Must use free because the memory was allocated by asprintf(). */ + free(gosub_args); + + ast_channel_lock(chan); + ast_debug(4, "%s Ending location: %s,%s,%d\n", ast_channel_name(chan), + ast_channel_context(chan), ast_channel_exten(chan), + ast_channel_priority(chan)); + /* Restore previous location */ ast_channel_context_set(chan, old_context); ast_channel_exten_set(chan, old_extension); ast_channel_priority_set(chan, old_priority); + /* Restore autoloop flag */ + ast_set2_flag(ast_channel_flags(chan), old_autoloopflag, AST_FLAG_IN_AUTOLOOP); + ast_channel_unlock(chan); + return RESULT_SUCCESS; } @@ -852,7 +1207,7 @@ static struct agi_command gosub_agi_command = static int unload_module(void) { - struct ast_context *con; + ast_install_stack_functions(NULL); ast_agi_unregister(ast_module_info->self, &gosub_agi_command); @@ -864,29 +1219,16 @@ 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"); - } + /* Setup the stack application callback functions. */ + static struct ast_app_stack_funcs funcs = { + .run_sub = gosub_run, + .expand_sub_args = expand_gosub_args, + }; ast_agi_register(ast_module_info->self, &gosub_agi_command); @@ -898,6 +1240,9 @@ static int load_module(void) ast_custom_function_register(&peek_function); ast_custom_function_register(&stackpeek_function); + funcs.module = ast_module_info->self, + ast_install_stack_functions(&funcs); + return 0; } |