From ae5a398322c6e2f7ac455cf577a0f3572db3e150 Mon Sep 17 00:00:00 2001 From: Tilghman Lesher <tilghman@meg.abyt.es> Date: Tue, 16 Mar 2010 23:49:35 +0000 Subject: [PATCH] Mask out previous arguments on each nested invocation of Gosub. (closes issue #16758) Reported by: wdoekes Patches: 20100316__issue16758.diff.txt uploaded by tilghman (license 14) Review: https://reviewboard.asterisk.org/r/561/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@252976 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- apps/app_stack.c | 23 +++++-- tests/test_gosub.c | 164 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+), 6 deletions(-) create mode 100644 tests/test_gosub.c diff --git a/apps/app_stack.c b/apps/app_stack.c index 08246d18af..6fc1c60923 100644 --- a/apps/app_stack.c +++ b/apps/app_stack.c @@ -344,9 +344,9 @@ static int gosub_exec(struct ast_channel *chan, const char *data) { struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL); AST_LIST_HEAD(, gosub_stack_frame) *oldlist; - struct gosub_stack_frame *newframe; + struct gosub_stack_frame *newframe, *lastframe; char argname[15], *tmp = ast_strdupa(data), *label, *endparen; - int i; + int i, max_argc = 0; AST_DECLARE_APP_ARGS(args2, AST_APP_ARG(argval)[100]; ); @@ -374,6 +374,12 @@ static int gosub_exec(struct ast_channel *chan, const char *data) stack_store->data = oldlist; AST_LIST_HEAD_INIT(oldlist); ast_channel_datastore_add(chan, stack_store); + } else { + oldlist = stack_store->data; + } + + if ((lastframe = AST_LIST_FIRST(oldlist))) { + max_argc = lastframe->arguments; } /* Separate the arguments from the label */ @@ -389,8 +395,13 @@ static int gosub_exec(struct ast_channel *chan, const char *data) } else args2.argc = 0; + /* Mask out previous arguments in this invocation */ + if (args2.argc > max_argc) { + max_argc = args2.argc; + } + /* Create the return address, but don't save it until we know that the Gosub destination exists */ - newframe = gosub_allocate_frame(chan->context, chan->exten, chan->priority + 1, args2.argc); + newframe = gosub_allocate_frame(chan->context, chan->exten, chan->priority + 1, max_argc); if (!newframe) { return -1; @@ -413,10 +424,10 @@ static int gosub_exec(struct ast_channel *chan, const char *data) } /* Now that we know for certain that we're going to a new location, set our arguments */ - for (i = 0; i < args2.argc; i++) { + for (i = 0; i < max_argc; i++) { snprintf(argname, sizeof(argname), "ARG%d", i + 1); - frame_set_var(chan, newframe, argname, args2.argval[i]); - ast_debug(1, "Setting '%s' to '%s'\n", argname, args2.argval[i]); + frame_set_var(chan, newframe, argname, i < args2.argc ? args2.argval[i] : ""); + ast_debug(1, "Setting '%s' to '%s'\n", argname, i < args2.argc ? args2.argval[i] : ""); } snprintf(argname, sizeof(argname), "%d", args2.argc); frame_set_var(chan, newframe, "ARGC", argname); diff --git a/tests/test_gosub.c b/tests/test_gosub.c new file mode 100644 index 0000000000..6efbe7851f --- /dev/null +++ b/tests/test_gosub.c @@ -0,0 +1,164 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2010, Digium, Inc. + * + * Tilghman Lesher <tlesher AT digium DOT com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! + * \file + * \brief Gosub tests + * + * \author\verbatim Tilghman Lesher <tlesher AT digium DOT com> \endverbatim + * + * \ingroup tests + */ + +/*** MODULEINFO + <depend>TEST_FRAMEWORK</depend> + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/utils.h" +#include "asterisk/module.h" +#include "asterisk/test.h" +#include "asterisk/pbx.h" + +AST_TEST_DEFINE(test_gosub) +{ + int res = AST_TEST_PASS, i; + struct ast_channel *chan; + struct ast_str *str; + struct ast_context *con; + struct testplan { + const char *app; + const char *args; + const char *expected_value; + } testplan[] = { + { "Gosub", "tests_test_gosub_virtual_context,s,1" }, + { NULL, "${EXTEN}", "s" }, + { "Gosub", "test,dne,1", (const char *) -1 }, /* This is the only invocation that should fail. */ + { NULL, "${EXTEN}", "s" }, + { "Gosub", "tests_test_gosub_virtual_context,s,1(5,5,5,5,5)" }, + { NULL, "$[0${ARG1} + 0${ARG5}]", "10" }, + { "Gosub", "tests_test_gosub_virtual_context,s,1(4,4,4,4)" }, + { NULL, "$[0${ARG1} + 0${ARG5}]", "4" }, + { NULL, "$[0${ARG1} + 0${ARG4}]", "8" }, + { "Gosub", "tests_test_gosub_virtual_context,s,1(3,3,3)" }, + { NULL, "$[0${ARG1} + 0${ARG4}]", "3" }, + { NULL, "$[0${ARG1} + 0${ARG3}]", "6" }, + { "Gosub", "tests_test_gosub_virtual_context,s,1(2,2)" }, + { NULL, "$[0${ARG1} + 0${ARG3}]", "2" }, + { NULL, "$[0${ARG1} + 0${ARG2}]", "4" }, + { "Gosub", "tests_test_gosub_virtual_context,s,1(1)" }, + { NULL, "$[0${ARG1} + 0${ARG2}]", "1" }, + { NULL, "$[0${ARG1} + 0${ARG1}]", "2" }, + { "Gosub", "tests_test_gosub_virtual_context,s,1" }, + { NULL, "$[0${ARG1} + 0${ARG1}]", "0" }, /* All arguments are correctly masked */ + { "Set", "LOCAL(foo)=5" }, + { NULL, "${foo}", "5" }, /* LOCAL() set a variable correctly */ + { NULL, "${LOCAL_PEEK(0,ARG1)}", "" }, /* LOCAL_PEEK() arguments work correctly */ + { NULL, "${LOCAL_PEEK(4,ARG1)}", "4" }, /* LOCAL_PEEK() arguments work correctly */ + { NULL, "$[0${LOCAL_PEEK(3,ARG1)} + 0${LOCAL_PEEK(5,ARG1)}]", "8" }, + { "StackPop", "" }, + { NULL, "${foo}", "" }, /* StackPop removed the variable set with LOCAL() */ + { "Return", "7" }, + { NULL, "${GOSUB_RETVAL}", "7" }, /* Return sets a return value correctly */ + { NULL, "$[0${GOSUB_RETVAL} + 0${ARG1}]", "9" }, /* Two frames less means ARG1 should have 2 */ + }; + + switch (cmd) { + case TEST_INIT: + info->name = "gosub application"; + info->category = "apps/app_gosub/"; + info->summary = "Verify functionality of gosub application"; + info->description = + "Verify functionality of gosub application"; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + if (!(chan = ast_dummy_channel_alloc())) { + ast_test_status_update(test, "Unable to allocate dummy channel\n"); + return AST_TEST_FAIL; + } + + if (!(str = ast_str_create(16))) { + ast_test_status_update(test, "Unable to allocate dynamic string buffer\n"); + ast_channel_release(chan); + return AST_TEST_FAIL; + } + + /* Create our test dialplan */ + if (!(con = ast_context_find_or_create(NULL, NULL, "tests_test_gosub_virtual_context", "test_gosub"))) { + ast_test_status_update(test, "Unable to create test dialplan context"); + ast_free(str); + ast_channel_release(chan); + return AST_TEST_FAIL; + } + + ast_add_extension2(con, 1, "s", 1, NULL, NULL, "NoOp", ast_strdup(""), ast_free_ptr, "test_gosub"); + + for (i = 0; i < ARRAY_LEN(testplan); i++) { + if (testplan[i].app == NULL) { + /* Evaluation */ + ast_str_substitute_variables(&str, 0, chan, testplan[i].args); + if (strcmp(ast_str_buffer(str), testplan[i].expected_value)) { + ast_test_status_update(test, "Evaluation of '%s' returned '%s' instead of the expected value '%s'\n", + testplan[i].args, ast_str_buffer(str), testplan[i].expected_value); + res = AST_TEST_FAIL; + } + } else { + /* Run application */ + int exec_res; + struct ast_app *app = pbx_findapp(testplan[i].app); + if (!app) { + ast_test_status_update(test, "Could not find '%s' in application listing!\n", testplan[i].app); + res = AST_TEST_FAIL; + break; + } + + if ((exec_res = pbx_exec(chan, app, testplan[i].args)) && ((const char *) exec_res != testplan[i].expected_value)) { + ast_test_status_update(test, "Application '%s' exited abnormally (with code %d)\n", testplan[i].app, exec_res); + res = AST_TEST_FAIL; + break; + } + } + } + + ast_free(str); + ast_channel_release(chan); + ast_context_remove_extension2(con, "s", 1, NULL, 0); + ast_context_destroy(con, "test_gosub"); + + return res; +} + +static int unload_module(void) +{ + AST_TEST_UNREGISTER(test_gosub); + return 0; +} + +static int load_module(void) +{ + AST_TEST_REGISTER(test_gosub); + return AST_MODULE_LOAD_SUCCESS; +} + +AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Gosub Tests"); -- GitLab