2005-11-08 23:37:53 +00:00
|
|
|
/*
|
|
|
|
|
* Asterisk -- An open source telephony toolkit.
|
|
|
|
|
*
|
2006-09-26 17:25:27 +00:00
|
|
|
* Copyright (c) 2004-2006 Tilghman Lesher <app_stack_v003@the-tilghman.com>.
|
2005-11-08 23:37:53 +00:00
|
|
|
*
|
|
|
|
|
* This code is released by the author with no restrictions on usage.
|
|
|
|
|
*
|
|
|
|
|
* 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 Stack applications Gosub, Return, etc.
|
2005-12-30 21:18:06 +00:00
|
|
|
*
|
2006-09-26 17:25:27 +00:00
|
|
|
* \author Tilghman Lesher <app_stack_v003@the-tilghman.com>
|
2005-11-08 23:37:53 +00:00
|
|
|
*
|
|
|
|
|
* \ingroup applications
|
|
|
|
|
*/
|
|
|
|
|
|
2008-11-03 00:52:05 +00:00
|
|
|
/*** MODULEINFO
|
|
|
|
|
<use>res_agi</use>
|
|
|
|
|
***/
|
|
|
|
|
|
2006-06-07 18:54:56 +00:00
|
|
|
#include "asterisk.h"
|
|
|
|
|
|
|
|
|
|
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|
|
|
|
|
2005-11-08 23:37:53 +00:00
|
|
|
#include "asterisk/pbx.h"
|
|
|
|
|
#include "asterisk/module.h"
|
2006-09-26 17:25:27 +00:00
|
|
|
#include "asterisk/app.h"
|
2007-06-20 04:36:23 +00:00
|
|
|
#include "asterisk/manager.h"
|
2007-11-22 03:50:04 +00:00
|
|
|
#include "asterisk/channel.h"
|
2008-11-03 00:52:05 +00:00
|
|
|
|
|
|
|
|
/* usage of AGI is optional, so indicate that to the header file */
|
|
|
|
|
#define ASTERISK_AGI_OPTIONAL
|
2008-05-30 16:11:31 +00:00
|
|
|
#include "asterisk/agi.h"
|
2005-11-08 23:37:53 +00:00
|
|
|
|
2008-06-05 16:01:32 +00:00
|
|
|
|
2005-11-08 23:37:53 +00:00
|
|
|
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 const char *gosub_synopsis = "Jump to label, saving return address";
|
2006-05-30 16:01:50 +00:00
|
|
|
static const char *gosubif_synopsis = "Conditionally jump to label, saving return address";
|
2005-11-08 23:37:53 +00:00
|
|
|
static const char *return_synopsis = "Return from gosub routine";
|
|
|
|
|
static const char *pop_synopsis = "Remove one address from gosub stack";
|
|
|
|
|
|
|
|
|
|
static const char *gosub_descrip =
|
2007-11-06 19:04:45 +00:00
|
|
|
" Gosub([[context,]exten,]priority[(arg1[,...][,argN])]):\n"
|
|
|
|
|
"Jumps to the label specified, saving the return address.\n";
|
2005-11-08 23:37:53 +00:00
|
|
|
static const char *gosubif_descrip =
|
2007-11-06 19:04:45 +00:00
|
|
|
" GosubIf(condition?labeliftrue[(arg1[,...])][:labeliffalse[(arg1[,...])]]):\n"
|
|
|
|
|
"If the condition is true, then jump to labeliftrue. If false, jumps to\n"
|
2005-11-08 23:37:53 +00:00
|
|
|
"labeliffalse, if specified. In either case, a jump saves the return point\n"
|
2006-01-03 18:17:42 +00:00
|
|
|
"in the dialplan, to be returned to with a Return.\n";
|
2005-11-08 23:37:53 +00:00
|
|
|
static const char *return_descrip =
|
2007-11-06 19:04:45 +00:00
|
|
|
" Return([return-value]):\n"
|
|
|
|
|
"Jumps to the last label on the stack, removing it. The return value, if\n"
|
2006-10-02 04:17:57 +00:00
|
|
|
"any, is saved in the channel variable GOSUB_RETVAL.\n";
|
2005-11-08 23:37:53 +00:00
|
|
|
static const char *pop_descrip =
|
2007-11-06 19:04:45 +00:00
|
|
|
" StackPop():\n"
|
|
|
|
|
"Removes last label on the stack, discarding it.\n";
|
2005-11-08 23:37:53 +00:00
|
|
|
|
2007-03-03 16:43:36 +00:00
|
|
|
static void gosub_free(void *data);
|
|
|
|
|
|
|
|
|
|
static struct ast_datastore_info stack_info = {
|
|
|
|
|
.type = "GOSUB",
|
|
|
|
|
.destroy = gosub_free,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct gosub_stack_frame {
|
|
|
|
|
AST_LIST_ENTRY(gosub_stack_frame) entries;
|
|
|
|
|
/* 100 arguments is all that we support anyway, but this will handle up to 255 */
|
|
|
|
|
unsigned char arguments;
|
2007-06-20 04:36:23 +00:00
|
|
|
struct varshead varshead;
|
2007-03-03 16:43:36 +00:00
|
|
|
int priority;
|
|
|
|
|
char *context;
|
|
|
|
|
char extension[0];
|
|
|
|
|
};
|
|
|
|
|
|
2007-06-20 13:00:45 +00:00
|
|
|
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;
|
|
|
|
|
int found = 0;
|
|
|
|
|
|
|
|
|
|
/* Does this variable already exist? */
|
|
|
|
|
AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
|
|
|
|
|
if (!strcmp(var, ast_var_name(variables))) {
|
|
|
|
|
found = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!ast_strlen_zero(value)) {
|
|
|
|
|
if (!found) {
|
|
|
|
|
variables = ast_var_assign(var, "");
|
|
|
|
|
AST_LIST_INSERT_HEAD(&frame->varshead, variables, entries);
|
|
|
|
|
pbx_builtin_pushvar_helper(chan, var, value);
|
|
|
|
|
} else
|
|
|
|
|
pbx_builtin_setvar_helper(chan, var, value);
|
|
|
|
|
|
2008-01-10 00:12:35 +00:00
|
|
|
manager_event(EVENT_FLAG_DIALPLAN, "VarSet",
|
2007-06-20 13:00:45 +00:00
|
|
|
"Channel: %s\r\n"
|
|
|
|
|
"Variable: LOCAL(%s)\r\n"
|
|
|
|
|
"Value: %s\r\n"
|
|
|
|
|
"Uniqueid: %s\r\n",
|
|
|
|
|
chan->name, var, value, chan->uniqueid);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2007-03-03 16:43:36 +00:00
|
|
|
static void gosub_release_frame(struct ast_channel *chan, struct gosub_stack_frame *frame)
|
2005-11-08 23:37:53 +00:00
|
|
|
{
|
2007-06-20 04:36:23 +00:00
|
|
|
struct ast_var_t *vardata;
|
2006-09-26 17:25:27 +00:00
|
|
|
|
2007-03-03 16:43:36 +00:00
|
|
|
/* If chan is not defined, then we're calling it as part of gosub_free,
|
|
|
|
|
* and the channel variables will be deallocated anyway. Otherwise, we're
|
|
|
|
|
* just releasing a single frame, so we need to clean up the arguments for
|
|
|
|
|
* that frame, so that we re-expose the variables from the previous frame
|
|
|
|
|
* that were hidden by this one.
|
|
|
|
|
*/
|
2007-06-20 05:47:05 +00:00
|
|
|
while ((vardata = AST_LIST_REMOVE_HEAD(&frame->varshead, entries))) {
|
|
|
|
|
if (chan)
|
|
|
|
|
pbx_builtin_setvar_helper(chan, ast_var_name(vardata), NULL);
|
2007-06-20 04:36:23 +00:00
|
|
|
ast_var_delete(vardata);
|
2007-06-20 05:47:05 +00:00
|
|
|
}
|
2007-06-20 04:36:23 +00:00
|
|
|
|
2007-03-03 16:43:36 +00:00
|
|
|
ast_free(frame);
|
|
|
|
|
}
|
2006-09-26 17:25:27 +00:00
|
|
|
|
2007-03-03 16:43:36 +00:00
|
|
|
static struct gosub_stack_frame *gosub_allocate_frame(const char *context, const char *extension, int priority, unsigned char arguments)
|
|
|
|
|
{
|
|
|
|
|
struct gosub_stack_frame *new = NULL;
|
|
|
|
|
int len_extension = strlen(extension), len_context = strlen(context);
|
|
|
|
|
|
|
|
|
|
if ((new = ast_calloc(1, sizeof(*new) + 2 + len_extension + len_context))) {
|
2007-06-20 04:36:23 +00:00
|
|
|
AST_LIST_HEAD_INIT_NOLOCK(&new->varshead);
|
2007-03-03 16:43:36 +00:00
|
|
|
strcpy(new->extension, extension);
|
|
|
|
|
new->context = new->extension + len_extension + 1;
|
|
|
|
|
strcpy(new->context, context);
|
|
|
|
|
new->priority = priority;
|
|
|
|
|
new->arguments = arguments;
|
|
|
|
|
}
|
|
|
|
|
return new;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void gosub_free(void *data)
|
|
|
|
|
{
|
|
|
|
|
AST_LIST_HEAD(, gosub_stack_frame) *oldlist = data;
|
|
|
|
|
struct gosub_stack_frame *oldframe;
|
|
|
|
|
AST_LIST_LOCK(oldlist);
|
|
|
|
|
while ((oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries))) {
|
|
|
|
|
gosub_release_frame(NULL, oldframe);
|
|
|
|
|
}
|
|
|
|
|
AST_LIST_UNLOCK(oldlist);
|
|
|
|
|
AST_LIST_HEAD_DESTROY(oldlist);
|
|
|
|
|
ast_free(oldlist);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int pop_exec(struct ast_channel *chan, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
|
|
|
|
|
struct gosub_stack_frame *oldframe;
|
|
|
|
|
AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
|
|
|
|
|
|
|
|
|
|
if (!stack_store) {
|
|
|
|
|
ast_log(LOG_WARNING, "%s called with no gosub stack allocated.\n", app_pop);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
oldlist = stack_store->data;
|
|
|
|
|
AST_LIST_LOCK(oldlist);
|
|
|
|
|
oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
|
|
|
|
|
AST_LIST_UNLOCK(oldlist);
|
|
|
|
|
|
2007-06-14 19:39:12 +00:00
|
|
|
if (oldframe) {
|
2007-03-03 16:43:36 +00:00
|
|
|
gosub_release_frame(chan, oldframe);
|
2007-06-14 19:39:12 +00:00
|
|
|
} else {
|
|
|
|
|
ast_debug(1, "%s called with an empty gosub stack\n", app_pop);
|
|
|
|
|
}
|
2005-11-08 23:37:53 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int return_exec(struct ast_channel *chan, void *data)
|
|
|
|
|
{
|
2007-03-03 16:43:36 +00:00
|
|
|
struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
|
|
|
|
|
struct gosub_stack_frame *oldframe;
|
|
|
|
|
AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
|
|
|
|
|
char *retval = data;
|
2005-11-08 23:37:53 +00:00
|
|
|
|
2007-03-03 16:43:36 +00:00
|
|
|
if (!stack_store) {
|
|
|
|
|
ast_log(LOG_ERROR, "Return without Gosub: stack is unallocated\n");
|
2005-11-08 23:37:53 +00:00
|
|
|
return -1;
|
2006-09-26 17:25:27 +00:00
|
|
|
}
|
|
|
|
|
|
2007-03-03 16:43:36 +00:00
|
|
|
oldlist = stack_store->data;
|
|
|
|
|
AST_LIST_LOCK(oldlist);
|
|
|
|
|
oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
|
|
|
|
|
AST_LIST_UNLOCK(oldlist);
|
2006-09-26 17:25:27 +00:00
|
|
|
|
2007-03-03 16:43:36 +00:00
|
|
|
if (!oldframe) {
|
|
|
|
|
ast_log(LOG_ERROR, "Return without Gosub: stack is empty\n");
|
2005-11-08 23:37:53 +00:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2007-03-03 16:43:36 +00:00
|
|
|
ast_explicit_goto(chan, oldframe->context, oldframe->extension, oldframe->priority);
|
|
|
|
|
gosub_release_frame(chan, oldframe);
|
2006-09-26 17:25:27 +00:00
|
|
|
|
|
|
|
|
/* Set a return value, if any */
|
|
|
|
|
pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", S_OR(retval, ""));
|
2005-11-08 23:37:53 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int gosub_exec(struct ast_channel *chan, void *data)
|
|
|
|
|
{
|
2007-03-03 16:43:36 +00:00
|
|
|
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;
|
2006-10-03 15:50:25 +00:00
|
|
|
char argname[15], *tmp = ast_strdupa(data), *label, *endparen;
|
2006-09-26 17:25:27 +00:00
|
|
|
int i;
|
|
|
|
|
AST_DECLARE_APP_ARGS(args2,
|
|
|
|
|
AST_APP_ARG(argval)[100];
|
|
|
|
|
);
|
2005-11-08 23:37:53 +00:00
|
|
|
|
|
|
|
|
if (ast_strlen_zero(data)) {
|
2008-05-30 16:11:31 +00:00
|
|
|
ast_log(LOG_ERROR, "%s requires an argument: %s([[context,]exten,]priority[(arg1[,...][,argN])])\n", app_gosub, app_gosub);
|
2005-11-08 23:37:53 +00:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2007-03-03 16:43:36 +00:00
|
|
|
if (!stack_store) {
|
2007-06-14 19:39:12 +00:00
|
|
|
ast_debug(1, "Channel %s has no datastore, so we're allocating one.\n", chan->name);
|
2007-03-03 16:43:36 +00:00
|
|
|
stack_store = ast_channel_datastore_alloc(&stack_info, NULL);
|
|
|
|
|
if (!stack_store) {
|
|
|
|
|
ast_log(LOG_ERROR, "Unable to allocate new datastore. Gosub will fail.\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
oldlist = ast_calloc(1, sizeof(*oldlist));
|
|
|
|
|
if (!oldlist) {
|
|
|
|
|
ast_log(LOG_ERROR, "Unable to allocate datastore list head. Gosub will fail.\n");
|
|
|
|
|
ast_channel_datastore_free(stack_store);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stack_store->data = oldlist;
|
|
|
|
|
AST_LIST_HEAD_INIT(oldlist);
|
|
|
|
|
ast_channel_datastore_add(chan, stack_store);
|
|
|
|
|
}
|
|
|
|
|
|
2006-10-02 04:17:57 +00:00
|
|
|
/* Separate the arguments from the label */
|
2006-10-03 15:50:25 +00:00
|
|
|
/* NOTE: you cannot use ast_app_separate_args for this, because '(' cannot be used as a delimiter. */
|
|
|
|
|
label = strsep(&tmp, "(");
|
|
|
|
|
if (tmp) {
|
|
|
|
|
endparen = strrchr(tmp, ')');
|
2006-10-02 04:17:57 +00:00
|
|
|
if (endparen)
|
|
|
|
|
*endparen = '\0';
|
2006-10-03 15:50:25 +00:00
|
|
|
else
|
|
|
|
|
ast_log(LOG_WARNING, "Ouch. No closing paren: '%s'?\n", (char *)data);
|
|
|
|
|
AST_STANDARD_APP_ARGS(args2, tmp);
|
|
|
|
|
} else
|
|
|
|
|
args2.argc = 0;
|
2006-09-26 17:25:27 +00:00
|
|
|
|
2006-10-02 04:17:57 +00:00
|
|
|
/* Create the return address, but don't save it until we know that the Gosub destination exists */
|
2007-03-03 16:43:36 +00:00
|
|
|
newframe = gosub_allocate_frame(chan->context, chan->exten, chan->priority + 1, args2.argc);
|
2005-11-08 23:37:53 +00:00
|
|
|
|
2008-12-03 18:41:28 +00:00
|
|
|
if (!newframe) {
|
2007-06-20 13:00:45 +00:00
|
|
|
return -1;
|
2008-12-03 18:41:28 +00:00
|
|
|
}
|
2007-06-20 13:00:45 +00:00
|
|
|
|
2006-10-03 15:50:25 +00:00
|
|
|
if (ast_parseable_goto(chan, label)) {
|
|
|
|
|
ast_log(LOG_ERROR, "Gosub address is invalid: '%s'\n", (char *)data);
|
2007-03-03 16:43:36 +00:00
|
|
|
ast_free(newframe);
|
2005-11-08 23:37:53 +00:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-05 23:26:45 +00:00
|
|
|
if (!ast_exists_extension(chan, chan->context, chan->exten, ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP) ? chan->priority + 1 : chan->priority, chan->cid.cid_num)) {
|
2008-12-03 18:41:28 +00:00
|
|
|
ast_log(LOG_ERROR, "Attempt to reach a non-existent destination for gosub: (Context:%s, Extension:%s, Priority:%d)\n",
|
|
|
|
|
chan->context, chan->exten, chan->priority);
|
|
|
|
|
ast_copy_string(chan->context, newframe->context, sizeof(chan->context));
|
|
|
|
|
ast_copy_string(chan->exten, newframe->extension, sizeof(chan->exten));
|
|
|
|
|
chan->priority = newframe->priority;
|
|
|
|
|
ast_free(newframe);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2006-09-26 17:25:27 +00:00
|
|
|
/* Now that we know for certain that we're going to a new location, set our arguments */
|
2006-10-03 15:50:25 +00:00
|
|
|
for (i = 0; i < args2.argc; i++) {
|
2006-09-26 17:25:27 +00:00
|
|
|
snprintf(argname, sizeof(argname), "ARG%d", i + 1);
|
2007-06-20 13:00:45 +00:00
|
|
|
frame_set_var(chan, newframe, argname, args2.argval[i]);
|
2007-06-14 19:39:12 +00:00
|
|
|
ast_debug(1, "Setting '%s' to '%s'\n", argname, args2.argval[i]);
|
2006-09-26 17:25:27 +00:00
|
|
|
}
|
|
|
|
|
|
2006-10-02 04:17:57 +00:00
|
|
|
/* And finally, save our return address */
|
2007-03-03 16:43:36 +00:00
|
|
|
oldlist = stack_store->data;
|
|
|
|
|
AST_LIST_LOCK(oldlist);
|
|
|
|
|
AST_LIST_INSERT_HEAD(oldlist, newframe, entries);
|
|
|
|
|
AST_LIST_UNLOCK(oldlist);
|
|
|
|
|
|
2005-11-08 23:37:53 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int gosubif_exec(struct ast_channel *chan, void *data)
|
|
|
|
|
{
|
2006-10-02 22:02:45 +00:00
|
|
|
char *args;
|
2005-11-08 23:37:53 +00:00
|
|
|
int res=0;
|
2006-10-02 22:02:45 +00:00
|
|
|
AST_DECLARE_APP_ARGS(cond,
|
|
|
|
|
AST_APP_ARG(ition);
|
|
|
|
|
AST_APP_ARG(labels);
|
|
|
|
|
);
|
|
|
|
|
AST_DECLARE_APP_ARGS(label,
|
|
|
|
|
AST_APP_ARG(iftrue);
|
|
|
|
|
AST_APP_ARG(iffalse);
|
|
|
|
|
);
|
2005-11-08 23:37:53 +00:00
|
|
|
|
|
|
|
|
if (ast_strlen_zero(data)) {
|
2006-10-02 04:17:57 +00:00
|
|
|
ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
|
2005-11-08 23:37:53 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2006-10-02 22:02:45 +00:00
|
|
|
args = ast_strdupa(data);
|
|
|
|
|
AST_NONSTANDARD_APP_ARGS(cond, args, '?');
|
|
|
|
|
if (cond.argc != 2) {
|
|
|
|
|
ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AST_NONSTANDARD_APP_ARGS(label, cond.labels, ':');
|
2005-11-08 23:37:53 +00:00
|
|
|
|
2006-10-02 22:02:45 +00:00
|
|
|
if (pbx_checkcondition(cond.ition)) {
|
|
|
|
|
if (!ast_strlen_zero(label.iftrue))
|
|
|
|
|
res = gosub_exec(chan, label.iftrue);
|
|
|
|
|
} else if (!ast_strlen_zero(label.iffalse)) {
|
|
|
|
|
res = gosub_exec(chan, label.iffalse);
|
2005-11-08 23:37:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2007-06-20 04:36:23 +00:00
|
|
|
static int local_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
|
|
|
|
|
{
|
|
|
|
|
struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
|
|
|
|
|
AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
|
|
|
|
|
struct gosub_stack_frame *frame;
|
|
|
|
|
struct ast_var_t *variables;
|
|
|
|
|
|
|
|
|
|
if (!stack_store)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
oldlist = stack_store->data;
|
|
|
|
|
AST_LIST_LOCK(oldlist);
|
|
|
|
|
frame = AST_LIST_FIRST(oldlist);
|
|
|
|
|
AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
|
|
|
|
|
if (!strcmp(data, ast_var_name(variables))) {
|
2007-06-20 05:47:05 +00:00
|
|
|
const char *tmp = pbx_builtin_getvar_helper(chan, data);
|
|
|
|
|
ast_copy_string(buf, S_OR(tmp, ""), len);
|
2007-06-20 04:36:23 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
AST_LIST_UNLOCK(oldlist);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int local_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
|
|
|
|
|
{
|
|
|
|
|
struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
|
|
|
|
|
AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
|
|
|
|
|
struct gosub_stack_frame *frame;
|
|
|
|
|
|
|
|
|
|
if (!stack_store) {
|
|
|
|
|
ast_log(LOG_ERROR, "Tried to set LOCAL(%s), but we aren't within a Gosub routine\n", var);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
oldlist = stack_store->data;
|
|
|
|
|
AST_LIST_LOCK(oldlist);
|
|
|
|
|
frame = AST_LIST_FIRST(oldlist);
|
2007-06-20 05:47:05 +00:00
|
|
|
|
2007-06-20 13:00:45 +00:00
|
|
|
if (frame)
|
|
|
|
|
frame_set_var(chan, frame, var, value);
|
2007-06-20 04:36:23 +00:00
|
|
|
|
|
|
|
|
AST_LIST_UNLOCK(oldlist);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct ast_custom_function local_function = {
|
|
|
|
|
.name = "LOCAL",
|
|
|
|
|
.synopsis = "Variables local to the gosub stack frame",
|
|
|
|
|
.syntax = "LOCAL(<varname>)",
|
|
|
|
|
.write = local_write,
|
|
|
|
|
.read = local_read,
|
|
|
|
|
};
|
|
|
|
|
|
2008-05-30 16:11:31 +00:00
|
|
|
static int handle_gosub(struct ast_channel *chan, AGI *agi, int argc, char **argv)
|
|
|
|
|
{
|
|
|
|
|
int old_priority, priority;
|
|
|
|
|
char old_context[AST_MAX_CONTEXT], old_extension[AST_MAX_EXTENSION];
|
|
|
|
|
struct ast_app *theapp;
|
|
|
|
|
char *gosub_args;
|
|
|
|
|
|
|
|
|
|
if (argc < 4 || argc > 5) {
|
|
|
|
|
return RESULT_SHOWUSAGE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ast_debug(1, "Gosub called with %d arguments: 0:%s 1:%s 2:%s 3:%s 4:%s\n", argc, argv[0], argv[1], argv[2], argv[3], argc == 5 ? argv[4] : "");
|
|
|
|
|
|
|
|
|
|
if (sscanf(argv[3], "%d", &priority) != 1 || priority < 1) {
|
|
|
|
|
/* Lookup the priority label */
|
|
|
|
|
if ((priority = ast_findlabel_extension(chan, argv[1], argv[2], argv[3], chan->cid.cid_num)) < 0) {
|
|
|
|
|
ast_log(LOG_ERROR, "Priority '%s' not found in '%s@%s'\n", argv[3], argv[2], argv[1]);
|
2008-11-19 13:19:49 +00:00
|
|
|
ast_agi_send(agi->fd, chan, "200 result=-1 Gosub label not found\n");
|
2008-05-30 16:11:31 +00:00
|
|
|
return RESULT_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
} else if (!ast_exists_extension(chan, argv[1], argv[2], priority, chan->cid.cid_num)) {
|
2008-11-19 13:19:49 +00:00
|
|
|
ast_agi_send(agi->fd, chan, "200 result=-1 Gosub label not found\n");
|
2008-05-30 16:11:31 +00:00
|
|
|
return RESULT_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Save previous location, since we're going to change it */
|
|
|
|
|
ast_copy_string(old_context, chan->context, sizeof(old_context));
|
|
|
|
|
ast_copy_string(old_extension, chan->exten, sizeof(old_extension));
|
|
|
|
|
old_priority = chan->priority;
|
|
|
|
|
|
|
|
|
|
if (!(theapp = pbx_findapp("Gosub"))) {
|
|
|
|
|
ast_log(LOG_ERROR, "Gosub() cannot be found in the list of loaded applications\n");
|
2008-11-19 13:19:49 +00:00
|
|
|
ast_agi_send(agi->fd, chan, "503 result=-2 Gosub is not loaded\n");
|
2008-05-30 16:11:31 +00:00
|
|
|
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) {
|
2008-11-03 00:39:04 +00:00
|
|
|
if (asprintf(&gosub_args, "%s,%s,%d(%s)", argv[1], argv[2], priority + 1, argv[4]) < 0) {
|
|
|
|
|
ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
|
|
|
|
|
gosub_args = NULL;
|
|
|
|
|
}
|
2008-05-30 16:11:31 +00:00
|
|
|
} else {
|
2008-11-03 00:39:04 +00:00
|
|
|
if (asprintf(&gosub_args, "%s,%s,%d", argv[1], argv[2], priority + 1) < 0) {
|
|
|
|
|
ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
|
|
|
|
|
gosub_args = NULL;
|
|
|
|
|
}
|
2008-05-30 16:11:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (gosub_args) {
|
|
|
|
|
int res;
|
|
|
|
|
|
|
|
|
|
ast_debug(1, "Trying gosub with arguments '%s'\n", gosub_args);
|
|
|
|
|
ast_copy_string(chan->context, "app_stack_gosub_virtual_context", sizeof(chan->context));
|
|
|
|
|
ast_copy_string(chan->exten, "s", sizeof(chan->exten));
|
|
|
|
|
chan->priority = 0;
|
|
|
|
|
|
|
|
|
|
if ((res = pbx_exec(chan, theapp, gosub_args)) == 0) {
|
2008-05-30 16:40:51 +00:00
|
|
|
struct ast_pbx *pbx = chan->pbx;
|
|
|
|
|
/* Suppress warning about PBX already existing */
|
|
|
|
|
chan->pbx = NULL;
|
2008-11-19 13:19:49 +00:00
|
|
|
ast_agi_send(agi->fd, chan, "100 result=0 Trying...\n");
|
2008-05-30 16:11:31 +00:00
|
|
|
ast_pbx_run(chan);
|
2008-11-19 13:19:49 +00:00
|
|
|
ast_agi_send(agi->fd, chan, "200 result=0 Gosub complete\n");
|
2008-05-30 16:40:51 +00:00
|
|
|
if (chan->pbx) {
|
|
|
|
|
ast_free(chan->pbx);
|
|
|
|
|
}
|
|
|
|
|
chan->pbx = pbx;
|
2008-05-30 16:11:31 +00:00
|
|
|
} else {
|
2008-11-19 13:19:49 +00:00
|
|
|
ast_agi_send(agi->fd, chan, "200 result=%d Gosub failed\n", res);
|
2008-05-30 16:11:31 +00:00
|
|
|
}
|
|
|
|
|
ast_free(gosub_args);
|
|
|
|
|
} else {
|
2008-11-19 13:19:49 +00:00
|
|
|
ast_agi_send(agi->fd, chan, "503 result=-2 Memory allocation failure\n");
|
2008-05-30 16:11:31 +00:00
|
|
|
return RESULT_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Restore previous location */
|
|
|
|
|
ast_copy_string(chan->context, old_context, sizeof(chan->context));
|
|
|
|
|
ast_copy_string(chan->exten, old_extension, sizeof(chan->exten));
|
|
|
|
|
chan->priority = old_priority;
|
|
|
|
|
|
|
|
|
|
return RESULT_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char usage_gosub[] =
|
|
|
|
|
" Usage: GOSUB <context> <extension> <priority> [<optional-argument>]\n"
|
|
|
|
|
" Cause the channel to execute the specified dialplan subroutine, returning\n"
|
|
|
|
|
" to the dialplan with execution of a Return()\n";
|
|
|
|
|
|
|
|
|
|
struct agi_command gosub_agi_command =
|
|
|
|
|
{ { "gosub", NULL }, handle_gosub, "Execute a dialplan subroutine", usage_gosub , 0 };
|
|
|
|
|
|
2006-08-21 02:11:39 +00:00
|
|
|
static int unload_module(void)
|
2005-11-08 23:37:53 +00:00
|
|
|
{
|
2008-05-30 16:11:31 +00:00
|
|
|
struct ast_context *con;
|
|
|
|
|
|
2008-11-03 00:52:05 +00:00
|
|
|
if (ast_agi_unregister) {
|
2008-06-05 16:01:32 +00:00
|
|
|
ast_agi_unregister(ast_module_info->self, &gosub_agi_command);
|
|
|
|
|
|
|
|
|
|
if ((con = ast_context_find("app_stack_gosub_virtual_context"))) {
|
2008-06-16 21:19:23 +00:00
|
|
|
ast_context_remove_extension2(con, "s", 1, NULL, 0);
|
2008-06-05 16:01:32 +00:00
|
|
|
ast_context_destroy(con, "app_stack"); /* leave nothing behind */
|
|
|
|
|
}
|
2008-05-30 16:11:31 +00:00
|
|
|
}
|
|
|
|
|
|
2005-11-08 23:37:53 +00:00
|
|
|
ast_unregister_application(app_return);
|
|
|
|
|
ast_unregister_application(app_pop);
|
|
|
|
|
ast_unregister_application(app_gosubif);
|
|
|
|
|
ast_unregister_application(app_gosub);
|
2007-06-20 04:36:23 +00:00
|
|
|
ast_custom_function_unregister(&local_function);
|
2005-11-08 23:37:53 +00:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-21 02:11:39 +00:00
|
|
|
static int load_module(void)
|
2005-11-08 23:37:53 +00:00
|
|
|
{
|
2008-05-30 16:11:31 +00:00
|
|
|
struct ast_context *con;
|
2008-06-05 16:01:32 +00:00
|
|
|
|
2008-11-03 00:52:05 +00:00
|
|
|
/* usage of AGI is optional, so check to see if the ast_agi_register()
|
|
|
|
|
function is available; if so, use it.
|
|
|
|
|
*/
|
|
|
|
|
if (ast_agi_register) {
|
Merged revisions 130145 via svnmerge from
https://origsvn.digium.com/svn/asterisk/trunk
Merging this rev from trunk to 1.6.0 was not
simple. Why? Because we've enhanced trunk to
do a [fast] merge-and-delete operation which
also solved problems with contexts having
entries from different registrars.
Fast as in the amount of time the contexts
are locked down. That *is* fast, but traversing
the entire dialplan looking for priorities to
delete takes more time overall.
This particular fix involved pulling in those
enhancements from trunk, along with all the
various fixes and refinements made along the
way.
Merging all this from trunk into 1.6 involved:
a. mergetrunk6 in the stuff from 130145;
b. revert all but the prop changes
c. catalog all revisions to pbx.c since 1.6.0 was forked
(at rev 105596).
d. catalog all revisions to pbx.c in trunk since 1.6.0
was forked, making special note of all revs that
were not merged into 1.6.0.
e. study each rev in trunk not applied to 1.6.0, and
determine if it was involved in the merge_and_delete
enhancements in trunk. 25 commits were done in 1.6.0,
all but one (106306) was a merge from trunk.
Trunk had 22 additional changes, of which 7 were
involved in the merge_and_delete enhancements:
106757
108894
109169
116461
123358
130145
130297
f. Go to trunk and collect patches, one by one,
of the changes made by each rev across the
entire source tree, using svn diff -c <num> > pfile
g. Apply each patch in order to 1.6.0, and
resolve all failures and compilation problems
before proceding to the next patch.
h. test the stuff.
i. profit!
........
r130145 | murf | 2008-07-11 12:24:31 -0600 (Fri, 11 Jul 2008) | 40 lines
(closes issue #13041)
Reported by: eliel
Tested by: murf
(closes issue #12960)
Reported by: mnicholson
In this 'omnibus' fix, I **think** I solved both
the problem in 13041, where unloading pbx_ael.so
caused crashes, or incomplete removal of previous
registrar'ed entries. And I added code to completely
remove all includes, switches, and ignorepats that
had a matching registrar entry, which should
appease 12960.
I also added a lot of seemingly useless brackets
around single statement if's, which helped debug
so much that I'm leaving them there.
I added a routine to check the correlation between
the extension tree lists and the hashtab
tables. It can be amazingly helpful when you have
lots of dialplan stuff, and need to narrow
down where a problem is occurring. It's ifdef'd
out by default.
I cleaned up the code around the new CIDmatch code.
It was leaving hanging extens with bad ptrs, getting confused
over which objects to remove, etc. I tightened
up the code and changed the call to remove_exten
in the merge_and_delete code.
I added more conditions to check for empty context
worthy of deletion. It's not empty if there are
any includes, switches, or ignorepats present.
If I've missed anything, please re-open this bug,
and be prepared to supply example dialplan code.
........
git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/1.6.0@130946 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2008-07-15 13:14:07 +00:00
|
|
|
con = ast_context_find_or_create(NULL, NULL, "app_stack_gosub_virtual_context", "app_stack");
|
2008-06-05 16:01:32 +00:00
|
|
|
if (!con) {
|
|
|
|
|
ast_log(LOG_ERROR, "Virtual context 'app_stack_gosub_virtual_context' does not exist and unable to create\n");
|
|
|
|
|
return AST_MODULE_LOAD_DECLINE;
|
|
|
|
|
} else {
|
|
|
|
|
ast_add_extension2(con, 1, "s", 1, NULL, NULL, "KeepAlive", ast_strdup(""), ast_free_ptr, "app_stack");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ast_agi_register(ast_module_info->self, &gosub_agi_command);
|
2008-05-30 16:11:31 +00:00
|
|
|
}
|
|
|
|
|
|
2005-11-08 23:37:53 +00:00
|
|
|
ast_register_application(app_pop, pop_exec, pop_synopsis, pop_descrip);
|
|
|
|
|
ast_register_application(app_return, return_exec, return_synopsis, return_descrip);
|
|
|
|
|
ast_register_application(app_gosubif, gosubif_exec, gosubif_synopsis, gosubif_descrip);
|
|
|
|
|
ast_register_application(app_gosub, gosub_exec, gosub_synopsis, gosub_descrip);
|
2007-06-20 04:36:23 +00:00
|
|
|
ast_custom_function_register(&local_function);
|
2005-11-08 23:37:53 +00:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2007-05-30 05:17:09 +00:00
|
|
|
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialplan subroutines (Gosub, Return, etc)");
|