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: 236302 $")
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 unsigned int is_agi:1;
00087 char *context;
00088 char extension[0];
00089 };
00090
00091 static int frame_set_var(struct ast_channel *chan, struct gosub_stack_frame *frame, const char *var, const char *value)
00092 {
00093 struct ast_var_t *variables;
00094 int found = 0;
00095
00096
00097 AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
00098 if (!strcmp(var, ast_var_name(variables))) {
00099 found = 1;
00100 break;
00101 }
00102 }
00103
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
00112 manager_event(EVENT_FLAG_DIALPLAN, "VarSet",
00113 "Channel: %s\r\n"
00114 "Variable: LOCAL(%s)\r\n"
00115 "Value: %s\r\n"
00116 "Uniqueid: %s\r\n",
00117 chan->name, var, value, chan->uniqueid);
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 int res = 0;
00200
00201 if (!stack_store) {
00202 ast_log(LOG_ERROR, "Return without Gosub: stack is unallocated\n");
00203 return -1;
00204 }
00205
00206 oldlist = stack_store->data;
00207 AST_LIST_LOCK(oldlist);
00208 oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
00209 AST_LIST_UNLOCK(oldlist);
00210
00211 if (!oldframe) {
00212 ast_log(LOG_ERROR, "Return without Gosub: stack is empty\n");
00213 return -1;
00214 } else if (oldframe->is_agi) {
00215
00216 res = -1;
00217 }
00218
00219 ast_explicit_goto(chan, oldframe->context, oldframe->extension, oldframe->priority);
00220 gosub_release_frame(chan, oldframe);
00221
00222
00223 pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", S_OR(retval, ""));
00224 return res;
00225 }
00226
00227 static int gosub_exec(struct ast_channel *chan, void *data)
00228 {
00229 struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
00230 AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
00231 struct gosub_stack_frame *newframe;
00232 char argname[15], *tmp = ast_strdupa(data), *label, *endparen;
00233 int i;
00234 AST_DECLARE_APP_ARGS(args2,
00235 AST_APP_ARG(argval)[100];
00236 );
00237
00238 if (ast_strlen_zero(data)) {
00239 ast_log(LOG_ERROR, "%s requires an argument: %s([[context,]exten,]priority[(arg1[,...][,argN])])\n", app_gosub, app_gosub);
00240 return -1;
00241 }
00242
00243 if (!stack_store) {
00244 ast_debug(1, "Channel %s has no datastore, so we're allocating one.\n", chan->name);
00245 stack_store = ast_datastore_alloc(&stack_info, NULL);
00246 if (!stack_store) {
00247 ast_log(LOG_ERROR, "Unable to allocate new datastore. Gosub will fail.\n");
00248 return -1;
00249 }
00250
00251 oldlist = ast_calloc(1, sizeof(*oldlist));
00252 if (!oldlist) {
00253 ast_log(LOG_ERROR, "Unable to allocate datastore list head. Gosub will fail.\n");
00254 ast_datastore_free(stack_store);
00255 return -1;
00256 }
00257
00258 stack_store->data = oldlist;
00259 AST_LIST_HEAD_INIT(oldlist);
00260 ast_channel_datastore_add(chan, stack_store);
00261 }
00262
00263
00264
00265 label = strsep(&tmp, "(");
00266 if (tmp) {
00267 endparen = strrchr(tmp, ')');
00268 if (endparen)
00269 *endparen = '\0';
00270 else
00271 ast_log(LOG_WARNING, "Ouch. No closing paren: '%s'?\n", (char *)data);
00272 AST_STANDARD_RAW_ARGS(args2, tmp);
00273 } else
00274 args2.argc = 0;
00275
00276
00277 newframe = gosub_allocate_frame(chan->context, chan->exten, chan->priority + 1, args2.argc);
00278
00279 if (!newframe) {
00280 return -1;
00281 }
00282
00283 if (ast_parseable_goto(chan, label)) {
00284 ast_log(LOG_ERROR, "Gosub address is invalid: '%s'\n", (char *)data);
00285 ast_free(newframe);
00286 return -1;
00287 }
00288
00289 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)) {
00290 ast_log(LOG_ERROR, "Attempt to reach a non-existent destination for gosub: (Context:%s, Extension:%s, Priority:%d)\n",
00291 chan->context, chan->exten, chan->priority);
00292 ast_copy_string(chan->context, newframe->context, sizeof(chan->context));
00293 ast_copy_string(chan->exten, newframe->extension, sizeof(chan->exten));
00294 chan->priority = newframe->priority;
00295 ast_free(newframe);
00296 return -1;
00297 }
00298
00299
00300 for (i = 0; i < args2.argc; i++) {
00301 snprintf(argname, sizeof(argname), "ARG%d", i + 1);
00302 frame_set_var(chan, newframe, argname, args2.argval[i]);
00303 ast_debug(1, "Setting '%s' to '%s'\n", argname, args2.argval[i]);
00304 }
00305
00306
00307 oldlist = stack_store->data;
00308 AST_LIST_LOCK(oldlist);
00309 AST_LIST_INSERT_HEAD(oldlist, newframe, entries);
00310 AST_LIST_UNLOCK(oldlist);
00311
00312 return 0;
00313 }
00314
00315 static int gosubif_exec(struct ast_channel *chan, void *data)
00316 {
00317 char *args;
00318 int res=0;
00319 AST_DECLARE_APP_ARGS(cond,
00320 AST_APP_ARG(ition);
00321 AST_APP_ARG(labels);
00322 );
00323 AST_DECLARE_APP_ARGS(label,
00324 AST_APP_ARG(iftrue);
00325 AST_APP_ARG(iffalse);
00326 );
00327
00328 if (ast_strlen_zero(data)) {
00329 ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
00330 return 0;
00331 }
00332
00333 args = ast_strdupa(data);
00334 AST_NONSTANDARD_RAW_ARGS(cond, args, '?');
00335 if (cond.argc != 2) {
00336 ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
00337 return 0;
00338 }
00339
00340 AST_NONSTANDARD_RAW_ARGS(label, cond.labels, ':');
00341
00342 if (pbx_checkcondition(cond.ition)) {
00343 if (!ast_strlen_zero(label.iftrue))
00344 res = gosub_exec(chan, label.iftrue);
00345 } else if (!ast_strlen_zero(label.iffalse)) {
00346 res = gosub_exec(chan, label.iffalse);
00347 }
00348
00349 return res;
00350 }
00351
00352 static int local_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00353 {
00354 struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
00355 AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
00356 struct gosub_stack_frame *frame;
00357 struct ast_var_t *variables;
00358
00359 if (!stack_store)
00360 return -1;
00361
00362 oldlist = stack_store->data;
00363 AST_LIST_LOCK(oldlist);
00364 if (!(frame = AST_LIST_FIRST(oldlist))) {
00365
00366 AST_LIST_UNLOCK(oldlist);
00367 return -1;
00368 }
00369
00370 AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
00371 if (!strcmp(data, ast_var_name(variables))) {
00372 const char *tmp;
00373 ast_channel_lock(chan);
00374 tmp = pbx_builtin_getvar_helper(chan, data);
00375 ast_copy_string(buf, S_OR(tmp, ""), len);
00376 ast_channel_unlock(chan);
00377 break;
00378 }
00379 }
00380 AST_LIST_UNLOCK(oldlist);
00381 return 0;
00382 }
00383
00384 static int local_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
00385 {
00386 struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
00387 AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
00388 struct gosub_stack_frame *frame;
00389
00390 if (!stack_store) {
00391 ast_log(LOG_ERROR, "Tried to set LOCAL(%s), but we aren't within a Gosub routine\n", var);
00392 return -1;
00393 }
00394
00395 oldlist = stack_store->data;
00396 AST_LIST_LOCK(oldlist);
00397 frame = AST_LIST_FIRST(oldlist);
00398
00399 if (frame)
00400 frame_set_var(chan, frame, var, value);
00401
00402 AST_LIST_UNLOCK(oldlist);
00403
00404 return 0;
00405 }
00406
00407 static struct ast_custom_function local_function = {
00408 .name = "LOCAL",
00409 .synopsis = "Variables local to the gosub stack frame",
00410 .syntax = "LOCAL(<varname>)",
00411 .write = local_write,
00412 .read = local_read,
00413 };
00414
00415 static int handle_gosub(struct ast_channel *chan, AGI *agi, int argc, char **argv)
00416 {
00417 int old_priority, priority;
00418 char old_context[AST_MAX_CONTEXT], old_extension[AST_MAX_EXTENSION];
00419 struct ast_app *theapp;
00420 char *gosub_args;
00421
00422 if (argc < 4 || argc > 5) {
00423 return RESULT_SHOWUSAGE;
00424 }
00425
00426 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] : "");
00427
00428 if (sscanf(argv[3], "%30d", &priority) != 1 || priority < 1) {
00429
00430 if ((priority = ast_findlabel_extension(chan, argv[1], argv[2], argv[3], chan->cid.cid_num)) < 0) {
00431 ast_log(LOG_ERROR, "Priority '%s' not found in '%s@%s'\n", argv[3], argv[2], argv[1]);
00432 ast_agi_send(agi->fd, chan, "200 result=-1 Gosub label not found\n");
00433 return RESULT_FAILURE;
00434 }
00435 } else if (!ast_exists_extension(chan, argv[1], argv[2], priority, chan->cid.cid_num)) {
00436 ast_agi_send(agi->fd, chan, "200 result=-1 Gosub label not found\n");
00437 return RESULT_FAILURE;
00438 }
00439
00440
00441 ast_copy_string(old_context, chan->context, sizeof(old_context));
00442 ast_copy_string(old_extension, chan->exten, sizeof(old_extension));
00443 old_priority = chan->priority;
00444
00445 if (!(theapp = pbx_findapp("Gosub"))) {
00446 ast_log(LOG_ERROR, "Gosub() cannot be found in the list of loaded applications\n");
00447 ast_agi_send(agi->fd, chan, "503 result=-2 Gosub is not loaded\n");
00448 return RESULT_FAILURE;
00449 }
00450
00451
00452
00453
00454
00455
00456
00457 if (argc == 5) {
00458 if (asprintf(&gosub_args, "%s,%s,%d(%s)", argv[1], argv[2], priority + (chan->pbx ? 1 : 0), argv[4]) < 0) {
00459 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00460 gosub_args = NULL;
00461 }
00462 } else {
00463 if (asprintf(&gosub_args, "%s,%s,%d", argv[1], argv[2], priority + (chan->pbx ? 1 : 0)) < 0) {
00464 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00465 gosub_args = NULL;
00466 }
00467 }
00468
00469 if (gosub_args) {
00470 int res;
00471
00472 ast_debug(1, "Trying gosub with arguments '%s'\n", gosub_args);
00473
00474 if ((res = pbx_exec(chan, theapp, gosub_args)) == 0) {
00475 struct ast_pbx *pbx = chan->pbx;
00476 struct ast_pbx_args args;
00477 struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
00478 AST_LIST_HEAD(, gosub_stack_frame) *oldlist = stack_store->data;
00479 struct gosub_stack_frame *cur = AST_LIST_FIRST(oldlist);
00480 cur->is_agi = 1;
00481
00482 memset(&args, 0, sizeof(args));
00483 args.no_hangup_chan = 1;
00484
00485 chan->pbx = NULL;
00486 ast_agi_send(agi->fd, chan, "100 result=0 Trying...\n");
00487 ast_pbx_run_args(chan, &args);
00488 ast_agi_send(agi->fd, chan, "200 result=0 Gosub complete\n");
00489 if (chan->pbx) {
00490 ast_free(chan->pbx);
00491 }
00492 chan->pbx = pbx;
00493 } else {
00494 ast_agi_send(agi->fd, chan, "200 result=%d Gosub failed\n", res);
00495 }
00496 ast_free(gosub_args);
00497 } else {
00498 ast_agi_send(agi->fd, chan, "503 result=-2 Memory allocation failure\n");
00499 return RESULT_FAILURE;
00500 }
00501
00502
00503 ast_copy_string(chan->context, old_context, sizeof(chan->context));
00504 ast_copy_string(chan->exten, old_extension, sizeof(chan->exten));
00505 chan->priority = old_priority;
00506
00507 return RESULT_SUCCESS;
00508 }
00509
00510 static char usage_gosub[] =
00511 " Usage: GOSUB <context> <extension> <priority> [<optional-argument>]\n"
00512 " Cause the channel to execute the specified dialplan subroutine, returning\n"
00513 " to the dialplan with execution of a Return()\n";
00514
00515 struct agi_command gosub_agi_command =
00516 { { "gosub", NULL }, handle_gosub, "Execute a dialplan subroutine", usage_gosub , 0 };
00517
00518 static int unload_module(void)
00519 {
00520 if (ast_agi_unregister) {
00521 ast_agi_unregister(ast_module_info->self, &gosub_agi_command);
00522 }
00523
00524 ast_unregister_application(app_return);
00525 ast_unregister_application(app_pop);
00526 ast_unregister_application(app_gosubif);
00527 ast_unregister_application(app_gosub);
00528 ast_custom_function_unregister(&local_function);
00529
00530 return 0;
00531 }
00532
00533 static int load_module(void)
00534 {
00535
00536
00537
00538 if (ast_agi_register) {
00539 ast_agi_register(ast_module_info->self, &gosub_agi_command);
00540 }
00541
00542 ast_register_application(app_pop, pop_exec, pop_synopsis, pop_descrip);
00543 ast_register_application(app_return, return_exec, return_synopsis, return_descrip);
00544 ast_register_application(app_gosubif, gosubif_exec, gosubif_synopsis, gosubif_descrip);
00545 ast_register_application(app_gosub, gosub_exec, gosub_synopsis, gosub_descrip);
00546 ast_custom_function_register(&local_function);
00547
00548 return 0;
00549 }
00550
00551 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialplan subroutines (Gosub, Return, etc)");