00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032 #include "asterisk.h"
00033
00034 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 174504 $")
00035
00036 #include "asterisk/pbx.h"
00037 #include "asterisk/module.h"
00038 #include "asterisk/app.h"
00039 #include "asterisk/manager.h"
00040 #include "asterisk/channel.h"
00041
00042
00043 #define ASTERISK_AGI_OPTIONAL
00044 #include "asterisk/agi.h"
00045
00046
00047 static const char *app_gosub = "Gosub";
00048 static const char *app_gosubif = "GosubIf";
00049 static const char *app_return = "Return";
00050 static const char *app_pop = "StackPop";
00051
00052 static const char *gosub_synopsis = "Jump to label, saving return address";
00053 static const char *gosubif_synopsis = "Conditionally jump to label, saving return address";
00054 static const char *return_synopsis = "Return from gosub routine";
00055 static const char *pop_synopsis = "Remove one address from gosub stack";
00056
00057 static const char *gosub_descrip =
00058 " Gosub([[context,]exten,]priority[(arg1[,...][,argN])]):\n"
00059 "Jumps to the label specified, saving the return address.\n";
00060 static const char *gosubif_descrip =
00061 " GosubIf(condition?labeliftrue[(arg1[,...])][:labeliffalse[(arg1[,...])]]):\n"
00062 "If the condition is true, then jump to labeliftrue. If false, jumps to\n"
00063 "labeliffalse, if specified. In either case, a jump saves the return point\n"
00064 "in the dialplan, to be returned to with a Return.\n";
00065 static const char *return_descrip =
00066 " Return([return-value]):\n"
00067 "Jumps to the last label on the stack, removing it. The return value, if\n"
00068 "any, is saved in the channel variable GOSUB_RETVAL.\n";
00069 static const char *pop_descrip =
00070 " StackPop():\n"
00071 "Removes last label on the stack, discarding it.\n";
00072
00073 static void gosub_free(void *data);
00074
00075 static struct ast_datastore_info stack_info = {
00076 .type = "GOSUB",
00077 .destroy = gosub_free,
00078 };
00079
00080 struct gosub_stack_frame {
00081 AST_LIST_ENTRY(gosub_stack_frame) entries;
00082
00083 unsigned char arguments;
00084 struct varshead varshead;
00085 int priority;
00086 char *context;
00087 char extension[0];
00088 };
00089
00090 static int frame_set_var(struct ast_channel *chan, struct gosub_stack_frame *frame, const char *var, const char *value)
00091 {
00092 struct ast_var_t *variables;
00093 int found = 0;
00094
00095
00096 AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
00097 if (!strcmp(var, ast_var_name(variables))) {
00098 found = 1;
00099 break;
00100 }
00101 }
00102
00103 if (!ast_strlen_zero(value)) {
00104 if (!found) {
00105 variables = ast_var_assign(var, "");
00106 AST_LIST_INSERT_HEAD(&frame->varshead, variables, entries);
00107 pbx_builtin_pushvar_helper(chan, var, value);
00108 } else
00109 pbx_builtin_setvar_helper(chan, var, value);
00110
00111 manager_event(EVENT_FLAG_DIALPLAN, "VarSet",
00112 "Channel: %s\r\n"
00113 "Variable: LOCAL(%s)\r\n"
00114 "Value: %s\r\n"
00115 "Uniqueid: %s\r\n",
00116 chan->name, var, value, chan->uniqueid);
00117 }
00118 return 0;
00119 }
00120
00121 static void gosub_release_frame(struct ast_channel *chan, struct gosub_stack_frame *frame)
00122 {
00123 struct ast_var_t *vardata;
00124
00125
00126
00127
00128
00129
00130
00131 while ((vardata = AST_LIST_REMOVE_HEAD(&frame->varshead, entries))) {
00132 if (chan)
00133 pbx_builtin_setvar_helper(chan, ast_var_name(vardata), NULL);
00134 ast_var_delete(vardata);
00135 }
00136
00137 ast_free(frame);
00138 }
00139
00140 static struct gosub_stack_frame *gosub_allocate_frame(const char *context, const char *extension, int priority, unsigned char arguments)
00141 {
00142 struct gosub_stack_frame *new = NULL;
00143 int len_extension = strlen(extension), len_context = strlen(context);
00144
00145 if ((new = ast_calloc(1, sizeof(*new) + 2 + len_extension + len_context))) {
00146 AST_LIST_HEAD_INIT_NOLOCK(&new->varshead);
00147 strcpy(new->extension, extension);
00148 new->context = new->extension + len_extension + 1;
00149 strcpy(new->context, context);
00150 new->priority = priority;
00151 new->arguments = arguments;
00152 }
00153 return new;
00154 }
00155
00156 static void gosub_free(void *data)
00157 {
00158 AST_LIST_HEAD(, gosub_stack_frame) *oldlist = data;
00159 struct gosub_stack_frame *oldframe;
00160 AST_LIST_LOCK(oldlist);
00161 while ((oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries))) {
00162 gosub_release_frame(NULL, oldframe);
00163 }
00164 AST_LIST_UNLOCK(oldlist);
00165 AST_LIST_HEAD_DESTROY(oldlist);
00166 ast_free(oldlist);
00167 }
00168
00169 static int pop_exec(struct ast_channel *chan, void *data)
00170 {
00171 struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
00172 struct gosub_stack_frame *oldframe;
00173 AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
00174
00175 if (!stack_store) {
00176 ast_log(LOG_WARNING, "%s called with no gosub stack allocated.\n", app_pop);
00177 return 0;
00178 }
00179
00180 oldlist = stack_store->data;
00181 AST_LIST_LOCK(oldlist);
00182 oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
00183 AST_LIST_UNLOCK(oldlist);
00184
00185 if (oldframe) {
00186 gosub_release_frame(chan, oldframe);
00187 } else {
00188 ast_debug(1, "%s called with an empty gosub stack\n", app_pop);
00189 }
00190 return 0;
00191 }
00192
00193 static int return_exec(struct ast_channel *chan, void *data)
00194 {
00195 struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
00196 struct gosub_stack_frame *oldframe;
00197 AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
00198 char *retval = data;
00199
00200 if (!stack_store) {
00201 ast_log(LOG_ERROR, "Return without Gosub: stack is unallocated\n");
00202 return -1;
00203 }
00204
00205 oldlist = stack_store->data;
00206 AST_LIST_LOCK(oldlist);
00207 oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
00208 AST_LIST_UNLOCK(oldlist);
00209
00210 if (!oldframe) {
00211 ast_log(LOG_ERROR, "Return without Gosub: stack is empty\n");
00212 return -1;
00213 }
00214
00215 ast_explicit_goto(chan, oldframe->context, oldframe->extension, oldframe->priority);
00216 gosub_release_frame(chan, oldframe);
00217
00218
00219 pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", S_OR(retval, ""));
00220 return 0;
00221 }
00222
00223 static int gosub_exec(struct ast_channel *chan, void *data)
00224 {
00225 struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
00226 AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
00227 struct gosub_stack_frame *newframe;
00228 char argname[15], *tmp = ast_strdupa(data), *label, *endparen;
00229 int i;
00230 AST_DECLARE_APP_ARGS(args2,
00231 AST_APP_ARG(argval)[100];
00232 );
00233
00234 if (ast_strlen_zero(data)) {
00235 ast_log(LOG_ERROR, "%s requires an argument: %s([[context,]exten,]priority[(arg1[,...][,argN])])\n", app_gosub, app_gosub);
00236 return -1;
00237 }
00238
00239 if (!stack_store) {
00240 ast_debug(1, "Channel %s has no datastore, so we're allocating one.\n", chan->name);
00241 stack_store = ast_datastore_alloc(&stack_info, NULL);
00242 if (!stack_store) {
00243 ast_log(LOG_ERROR, "Unable to allocate new datastore. Gosub will fail.\n");
00244 return -1;
00245 }
00246
00247 oldlist = ast_calloc(1, sizeof(*oldlist));
00248 if (!oldlist) {
00249 ast_log(LOG_ERROR, "Unable to allocate datastore list head. Gosub will fail.\n");
00250 ast_datastore_free(stack_store);
00251 return -1;
00252 }
00253
00254 stack_store->data = oldlist;
00255 AST_LIST_HEAD_INIT(oldlist);
00256 ast_channel_datastore_add(chan, stack_store);
00257 }
00258
00259
00260
00261 label = strsep(&tmp, "(");
00262 if (tmp) {
00263 endparen = strrchr(tmp, ')');
00264 if (endparen)
00265 *endparen = '\0';
00266 else
00267 ast_log(LOG_WARNING, "Ouch. No closing paren: '%s'?\n", (char *)data);
00268 AST_STANDARD_APP_ARGS(args2, tmp);
00269 } else
00270 args2.argc = 0;
00271
00272
00273 newframe = gosub_allocate_frame(chan->context, chan->exten, chan->priority + 1, args2.argc);
00274
00275 if (!newframe) {
00276 return -1;
00277 }
00278
00279 if (ast_parseable_goto(chan, label)) {
00280 ast_log(LOG_ERROR, "Gosub address is invalid: '%s'\n", (char *)data);
00281 ast_free(newframe);
00282 return -1;
00283 }
00284
00285 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)) {
00286 ast_log(LOG_ERROR, "Attempt to reach a non-existent destination for gosub: (Context:%s, Extension:%s, Priority:%d)\n",
00287 chan->context, chan->exten, chan->priority);
00288 ast_copy_string(chan->context, newframe->context, sizeof(chan->context));
00289 ast_copy_string(chan->exten, newframe->extension, sizeof(chan->exten));
00290 chan->priority = newframe->priority;
00291 ast_free(newframe);
00292 return -1;
00293 }
00294
00295
00296 for (i = 0; i < args2.argc; i++) {
00297 snprintf(argname, sizeof(argname), "ARG%d", i + 1);
00298 frame_set_var(chan, newframe, argname, args2.argval[i]);
00299 ast_debug(1, "Setting '%s' to '%s'\n", argname, args2.argval[i]);
00300 }
00301
00302
00303 oldlist = stack_store->data;
00304 AST_LIST_LOCK(oldlist);
00305 AST_LIST_INSERT_HEAD(oldlist, newframe, entries);
00306 AST_LIST_UNLOCK(oldlist);
00307
00308 return 0;
00309 }
00310
00311 static int gosubif_exec(struct ast_channel *chan, void *data)
00312 {
00313 char *args;
00314 int res=0;
00315 AST_DECLARE_APP_ARGS(cond,
00316 AST_APP_ARG(ition);
00317 AST_APP_ARG(labels);
00318 );
00319 AST_DECLARE_APP_ARGS(label,
00320 AST_APP_ARG(iftrue);
00321 AST_APP_ARG(iffalse);
00322 );
00323
00324 if (ast_strlen_zero(data)) {
00325 ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
00326 return 0;
00327 }
00328
00329 args = ast_strdupa(data);
00330 AST_NONSTANDARD_APP_ARGS(cond, args, '?');
00331 if (cond.argc != 2) {
00332 ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
00333 return 0;
00334 }
00335
00336 AST_NONSTANDARD_APP_ARGS(label, cond.labels, ':');
00337
00338 if (pbx_checkcondition(cond.ition)) {
00339 if (!ast_strlen_zero(label.iftrue))
00340 res = gosub_exec(chan, label.iftrue);
00341 } else if (!ast_strlen_zero(label.iffalse)) {
00342 res = gosub_exec(chan, label.iffalse);
00343 }
00344
00345 return res;
00346 }
00347
00348 static int local_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00349 {
00350 struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
00351 AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
00352 struct gosub_stack_frame *frame;
00353 struct ast_var_t *variables;
00354
00355 if (!stack_store)
00356 return -1;
00357
00358 oldlist = stack_store->data;
00359 AST_LIST_LOCK(oldlist);
00360 frame = AST_LIST_FIRST(oldlist);
00361 AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
00362 if (!strcmp(data, ast_var_name(variables))) {
00363 const char *tmp;
00364 ast_channel_lock(chan);
00365 tmp = pbx_builtin_getvar_helper(chan, data);
00366 ast_copy_string(buf, S_OR(tmp, ""), len);
00367 ast_channel_unlock(chan);
00368 break;
00369 }
00370 }
00371 AST_LIST_UNLOCK(oldlist);
00372 return 0;
00373 }
00374
00375 static int local_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
00376 {
00377 struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
00378 AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
00379 struct gosub_stack_frame *frame;
00380
00381 if (!stack_store) {
00382 ast_log(LOG_ERROR, "Tried to set LOCAL(%s), but we aren't within a Gosub routine\n", var);
00383 return -1;
00384 }
00385
00386 oldlist = stack_store->data;
00387 AST_LIST_LOCK(oldlist);
00388 frame = AST_LIST_FIRST(oldlist);
00389
00390 if (frame)
00391 frame_set_var(chan, frame, var, value);
00392
00393 AST_LIST_UNLOCK(oldlist);
00394
00395 return 0;
00396 }
00397
00398 static struct ast_custom_function local_function = {
00399 .name = "LOCAL",
00400 .synopsis = "Variables local to the gosub stack frame",
00401 .syntax = "LOCAL(<varname>)",
00402 .write = local_write,
00403 .read = local_read,
00404 };
00405
00406 static int handle_gosub(struct ast_channel *chan, AGI *agi, int argc, char **argv)
00407 {
00408 int old_priority, priority;
00409 char old_context[AST_MAX_CONTEXT], old_extension[AST_MAX_EXTENSION];
00410 struct ast_app *theapp;
00411 char *gosub_args;
00412
00413 if (argc < 4 || argc > 5) {
00414 return RESULT_SHOWUSAGE;
00415 }
00416
00417 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] : "");
00418
00419 if (sscanf(argv[3], "%d", &priority) != 1 || priority < 1) {
00420
00421 if ((priority = ast_findlabel_extension(chan, argv[1], argv[2], argv[3], chan->cid.cid_num)) < 0) {
00422 ast_log(LOG_ERROR, "Priority '%s' not found in '%s@%s'\n", argv[3], argv[2], argv[1]);
00423 ast_agi_send(agi->fd, chan, "200 result=-1 Gosub label not found\n");
00424 return RESULT_FAILURE;
00425 }
00426 } else if (!ast_exists_extension(chan, argv[1], argv[2], priority, chan->cid.cid_num)) {
00427 ast_agi_send(agi->fd, chan, "200 result=-1 Gosub label not found\n");
00428 return RESULT_FAILURE;
00429 }
00430
00431
00432 ast_copy_string(old_context, chan->context, sizeof(old_context));
00433 ast_copy_string(old_extension, chan->exten, sizeof(old_extension));
00434 old_priority = chan->priority;
00435
00436 if (!(theapp = pbx_findapp("Gosub"))) {
00437 ast_log(LOG_ERROR, "Gosub() cannot be found in the list of loaded applications\n");
00438 ast_agi_send(agi->fd, chan, "503 result=-2 Gosub is not loaded\n");
00439 return RESULT_FAILURE;
00440 }
00441
00442
00443
00444
00445
00446
00447
00448 if (argc == 5) {
00449 if (asprintf(&gosub_args, "%s,%s,%d(%s)", argv[1], argv[2], priority + 1, argv[4]) < 0) {
00450 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00451 gosub_args = NULL;
00452 }
00453 } else {
00454 if (asprintf(&gosub_args, "%s,%s,%d", argv[1], argv[2], priority + 1) < 0) {
00455 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00456 gosub_args = NULL;
00457 }
00458 }
00459
00460 if (gosub_args) {
00461 int res;
00462
00463 ast_debug(1, "Trying gosub with arguments '%s'\n", gosub_args);
00464
00465 if ((res = pbx_exec(chan, theapp, gosub_args)) == 0) {
00466 struct ast_pbx *pbx = chan->pbx;
00467 struct ast_pbx_args args;
00468 memset(&args, 0, sizeof(args));
00469 args.no_hangup_chan = 1;
00470
00471 chan->pbx = NULL;
00472 ast_agi_send(agi->fd, chan, "100 result=0 Trying...\n");
00473 ast_pbx_run_args(chan, &args);
00474 ast_agi_send(agi->fd, chan, "200 result=0 Gosub complete\n");
00475 if (chan->pbx) {
00476 ast_free(chan->pbx);
00477 }
00478 chan->pbx = pbx;
00479 } else {
00480 ast_agi_send(agi->fd, chan, "200 result=%d Gosub failed\n", res);
00481 }
00482 ast_free(gosub_args);
00483 } else {
00484 ast_agi_send(agi->fd, chan, "503 result=-2 Memory allocation failure\n");
00485 return RESULT_FAILURE;
00486 }
00487
00488
00489 ast_copy_string(chan->context, old_context, sizeof(chan->context));
00490 ast_copy_string(chan->exten, old_extension, sizeof(chan->exten));
00491 chan->priority = old_priority;
00492
00493 return RESULT_SUCCESS;
00494 }
00495
00496 static char usage_gosub[] =
00497 " Usage: GOSUB <context> <extension> <priority> [<optional-argument>]\n"
00498 " Cause the channel to execute the specified dialplan subroutine, returning\n"
00499 " to the dialplan with execution of a Return()\n";
00500
00501 struct agi_command gosub_agi_command =
00502 { { "gosub", NULL }, handle_gosub, "Execute a dialplan subroutine", usage_gosub , 0 };
00503
00504 static int unload_module(void)
00505 {
00506 if (ast_agi_unregister) {
00507 ast_agi_unregister(ast_module_info->self, &gosub_agi_command);
00508 }
00509
00510 ast_unregister_application(app_return);
00511 ast_unregister_application(app_pop);
00512 ast_unregister_application(app_gosubif);
00513 ast_unregister_application(app_gosub);
00514 ast_custom_function_unregister(&local_function);
00515
00516 return 0;
00517 }
00518
00519 static int load_module(void)
00520 {
00521
00522
00523
00524 if (ast_agi_register) {
00525 ast_agi_register(ast_module_info->self, &gosub_agi_command);
00526 }
00527
00528 ast_register_application(app_pop, pop_exec, pop_synopsis, pop_descrip);
00529 ast_register_application(app_return, return_exec, return_synopsis, return_descrip);
00530 ast_register_application(app_gosubif, gosubif_exec, gosubif_synopsis, gosubif_descrip);
00531 ast_register_application(app_gosub, gosub_exec, gosub_synopsis, gosub_descrip);
00532 ast_custom_function_register(&local_function);
00533
00534 return 0;
00535 }
00536
00537 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialplan subroutines (Gosub, Return, etc)");