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: 264753 $")
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 #include "asterisk/agi.h"
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169 static const char *app_gosub = "Gosub";
00170 static const char *app_gosubif = "GosubIf";
00171 static const char *app_return = "Return";
00172 static const char *app_pop = "StackPop";
00173
00174 static void gosub_free(void *data);
00175
00176 static struct ast_datastore_info stack_info = {
00177 .type = "GOSUB",
00178 .destroy = gosub_free,
00179 };
00180
00181 struct gosub_stack_frame {
00182 AST_LIST_ENTRY(gosub_stack_frame) entries;
00183
00184 unsigned char arguments;
00185 struct varshead varshead;
00186 int priority;
00187 unsigned int is_agi:1;
00188 char *context;
00189 char extension[0];
00190 };
00191
00192 static int frame_set_var(struct ast_channel *chan, struct gosub_stack_frame *frame, const char *var, const char *value)
00193 {
00194 struct ast_var_t *variables;
00195 int found = 0;
00196
00197
00198 AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
00199 if (!strcmp(var, ast_var_name(variables))) {
00200 found = 1;
00201 break;
00202 }
00203 }
00204
00205 if (!found) {
00206 variables = ast_var_assign(var, "");
00207 AST_LIST_INSERT_HEAD(&frame->varshead, variables, entries);
00208 pbx_builtin_pushvar_helper(chan, var, value);
00209 } else {
00210 pbx_builtin_setvar_helper(chan, var, value);
00211 }
00212
00213 manager_event(EVENT_FLAG_DIALPLAN, "VarSet",
00214 "Channel: %s\r\n"
00215 "Variable: LOCAL(%s)\r\n"
00216 "Value: %s\r\n"
00217 "Uniqueid: %s\r\n",
00218 chan->name, var, value, chan->uniqueid);
00219 return 0;
00220 }
00221
00222 static void gosub_release_frame(struct ast_channel *chan, struct gosub_stack_frame *frame)
00223 {
00224 struct ast_var_t *vardata;
00225
00226
00227
00228
00229
00230
00231
00232 while ((vardata = AST_LIST_REMOVE_HEAD(&frame->varshead, entries))) {
00233 if (chan)
00234 pbx_builtin_setvar_helper(chan, ast_var_name(vardata), NULL);
00235 ast_var_delete(vardata);
00236 }
00237
00238 ast_free(frame);
00239 }
00240
00241 static struct gosub_stack_frame *gosub_allocate_frame(const char *context, const char *extension, int priority, unsigned char arguments)
00242 {
00243 struct gosub_stack_frame *new = NULL;
00244 int len_extension = strlen(extension), len_context = strlen(context);
00245
00246 if ((new = ast_calloc(1, sizeof(*new) + 2 + len_extension + len_context))) {
00247 AST_LIST_HEAD_INIT_NOLOCK(&new->varshead);
00248 strcpy(new->extension, extension);
00249 new->context = new->extension + len_extension + 1;
00250 strcpy(new->context, context);
00251 new->priority = priority;
00252 new->arguments = arguments;
00253 }
00254 return new;
00255 }
00256
00257 static void gosub_free(void *data)
00258 {
00259 AST_LIST_HEAD(, gosub_stack_frame) *oldlist = data;
00260 struct gosub_stack_frame *oldframe;
00261 AST_LIST_LOCK(oldlist);
00262 while ((oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries))) {
00263 gosub_release_frame(NULL, oldframe);
00264 }
00265 AST_LIST_UNLOCK(oldlist);
00266 AST_LIST_HEAD_DESTROY(oldlist);
00267 ast_free(oldlist);
00268 }
00269
00270 static int pop_exec(struct ast_channel *chan, void *data)
00271 {
00272 struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
00273 struct gosub_stack_frame *oldframe;
00274 AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
00275
00276 if (!stack_store) {
00277 ast_log(LOG_WARNING, "%s called with no gosub stack allocated.\n", app_pop);
00278 return 0;
00279 }
00280
00281 oldlist = stack_store->data;
00282 AST_LIST_LOCK(oldlist);
00283 oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
00284 AST_LIST_UNLOCK(oldlist);
00285
00286 if (oldframe) {
00287 gosub_release_frame(chan, oldframe);
00288 } else {
00289 ast_debug(1, "%s called with an empty gosub stack\n", app_pop);
00290 }
00291 return 0;
00292 }
00293
00294 static int return_exec(struct ast_channel *chan, void *data)
00295 {
00296 struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
00297 struct gosub_stack_frame *oldframe;
00298 AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
00299 char *retval = data;
00300 int res = 0;
00301
00302 if (!stack_store) {
00303 ast_log(LOG_ERROR, "Return without Gosub: stack is unallocated\n");
00304 return -1;
00305 }
00306
00307 oldlist = stack_store->data;
00308 AST_LIST_LOCK(oldlist);
00309 oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
00310 AST_LIST_UNLOCK(oldlist);
00311
00312 if (!oldframe) {
00313 ast_log(LOG_ERROR, "Return without Gosub: stack is empty\n");
00314 return -1;
00315 } else if (oldframe->is_agi) {
00316
00317 res = -1;
00318 }
00319
00320 ast_explicit_goto(chan, oldframe->context, oldframe->extension, oldframe->priority);
00321 gosub_release_frame(chan, oldframe);
00322
00323
00324 pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", S_OR(retval, ""));
00325 return res;
00326 }
00327
00328 static int gosub_exec(struct ast_channel *chan, void *data)
00329 {
00330 struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
00331 AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
00332 struct gosub_stack_frame *newframe, *lastframe;
00333 char argname[15], *tmp = ast_strdupa(data), *label, *endparen;
00334 int i, max_argc = 0;
00335 AST_DECLARE_APP_ARGS(args2,
00336 AST_APP_ARG(argval)[100];
00337 );
00338
00339 if (ast_strlen_zero(data)) {
00340 ast_log(LOG_ERROR, "%s requires an argument: %s([[context,]exten,]priority[(arg1[,...][,argN])])\n", app_gosub, app_gosub);
00341 return -1;
00342 }
00343
00344 if (!stack_store) {
00345 ast_debug(1, "Channel %s has no datastore, so we're allocating one.\n", chan->name);
00346 stack_store = ast_datastore_alloc(&stack_info, NULL);
00347 if (!stack_store) {
00348 ast_log(LOG_ERROR, "Unable to allocate new datastore. Gosub will fail.\n");
00349 return -1;
00350 }
00351
00352 oldlist = ast_calloc(1, sizeof(*oldlist));
00353 if (!oldlist) {
00354 ast_log(LOG_ERROR, "Unable to allocate datastore list head. Gosub will fail.\n");
00355 ast_datastore_free(stack_store);
00356 return -1;
00357 }
00358
00359 stack_store->data = oldlist;
00360 AST_LIST_HEAD_INIT(oldlist);
00361 ast_channel_datastore_add(chan, stack_store);
00362 } else {
00363 oldlist = stack_store->data;
00364 }
00365
00366 if ((lastframe = AST_LIST_FIRST(oldlist))) {
00367 max_argc = lastframe->arguments;
00368 }
00369
00370
00371
00372 label = strsep(&tmp, "(");
00373 if (tmp) {
00374 endparen = strrchr(tmp, ')');
00375 if (endparen)
00376 *endparen = '\0';
00377 else
00378 ast_log(LOG_WARNING, "Ouch. No closing paren: '%s'?\n", (char *)data);
00379 AST_STANDARD_RAW_ARGS(args2, tmp);
00380 } else
00381 args2.argc = 0;
00382
00383
00384 if (args2.argc > max_argc) {
00385 max_argc = args2.argc;
00386 }
00387
00388
00389 newframe = gosub_allocate_frame(chan->context, chan->exten, chan->priority + 1, max_argc);
00390
00391 if (!newframe) {
00392 return -1;
00393 }
00394
00395 if (ast_parseable_goto(chan, label)) {
00396 ast_log(LOG_ERROR, "Gosub address is invalid: '%s'\n", (char *)data);
00397 ast_free(newframe);
00398 return -1;
00399 }
00400
00401 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)) {
00402 ast_log(LOG_ERROR, "Attempt to reach a non-existent destination for gosub: (Context:%s, Extension:%s, Priority:%d)\n",
00403 chan->context, chan->exten, ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP) ? chan->priority + 1 : chan->priority);
00404 ast_copy_string(chan->context, newframe->context, sizeof(chan->context));
00405 ast_copy_string(chan->exten, newframe->extension, sizeof(chan->exten));
00406 chan->priority = newframe->priority;
00407 ast_free(newframe);
00408 return -1;
00409 }
00410
00411
00412 for (i = 0; i < max_argc; i++) {
00413 snprintf(argname, sizeof(argname), "ARG%d", i + 1);
00414 frame_set_var(chan, newframe, argname, i < args2.argc ? args2.argval[i] : "");
00415 ast_debug(1, "Setting '%s' to '%s'\n", argname, i < args2.argc ? args2.argval[i] : "");
00416 }
00417 snprintf(argname, sizeof(argname), "%d", args2.argc);
00418 frame_set_var(chan, newframe, "ARGC", argname);
00419
00420
00421 oldlist = stack_store->data;
00422 AST_LIST_LOCK(oldlist);
00423 AST_LIST_INSERT_HEAD(oldlist, newframe, entries);
00424 AST_LIST_UNLOCK(oldlist);
00425
00426 return 0;
00427 }
00428
00429 static int gosubif_exec(struct ast_channel *chan, void *data)
00430 {
00431 char *args;
00432 int res=0;
00433 AST_DECLARE_APP_ARGS(cond,
00434 AST_APP_ARG(ition);
00435 AST_APP_ARG(labels);
00436 );
00437 AST_DECLARE_APP_ARGS(label,
00438 AST_APP_ARG(iftrue);
00439 AST_APP_ARG(iffalse);
00440 );
00441
00442 if (ast_strlen_zero(data)) {
00443 ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
00444 return 0;
00445 }
00446
00447 args = ast_strdupa(data);
00448 AST_NONSTANDARD_RAW_ARGS(cond, args, '?');
00449 if (cond.argc != 2) {
00450 ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
00451 return 0;
00452 }
00453
00454 AST_NONSTANDARD_RAW_ARGS(label, cond.labels, ':');
00455
00456 if (pbx_checkcondition(cond.ition)) {
00457 if (!ast_strlen_zero(label.iftrue))
00458 res = gosub_exec(chan, label.iftrue);
00459 } else if (!ast_strlen_zero(label.iffalse)) {
00460 res = gosub_exec(chan, label.iffalse);
00461 }
00462
00463 return res;
00464 }
00465
00466 static int local_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00467 {
00468 struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
00469 AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
00470 struct gosub_stack_frame *frame;
00471 struct ast_var_t *variables;
00472
00473 if (!stack_store)
00474 return -1;
00475
00476 oldlist = stack_store->data;
00477 AST_LIST_LOCK(oldlist);
00478 if (!(frame = AST_LIST_FIRST(oldlist))) {
00479
00480 AST_LIST_UNLOCK(oldlist);
00481 return -1;
00482 }
00483
00484 AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
00485 if (!strcmp(data, ast_var_name(variables))) {
00486 const char *tmp;
00487 ast_channel_lock(chan);
00488 tmp = pbx_builtin_getvar_helper(chan, data);
00489 ast_copy_string(buf, S_OR(tmp, ""), len);
00490 ast_channel_unlock(chan);
00491 break;
00492 }
00493 }
00494 AST_LIST_UNLOCK(oldlist);
00495 return 0;
00496 }
00497
00498 static int local_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
00499 {
00500 struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
00501 AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
00502 struct gosub_stack_frame *frame;
00503
00504 if (!stack_store) {
00505 ast_log(LOG_ERROR, "Tried to set LOCAL(%s), but we aren't within a Gosub routine\n", var);
00506 return -1;
00507 }
00508
00509 oldlist = stack_store->data;
00510 AST_LIST_LOCK(oldlist);
00511 frame = AST_LIST_FIRST(oldlist);
00512
00513 if (frame)
00514 frame_set_var(chan, frame, var, value);
00515
00516 AST_LIST_UNLOCK(oldlist);
00517
00518 return 0;
00519 }
00520
00521 static struct ast_custom_function local_function = {
00522 .name = "LOCAL",
00523 .write = local_write,
00524 .read = local_read,
00525 };
00526
00527 static int peek_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00528 {
00529 int found = 0, n;
00530 struct ast_var_t *variables;
00531 AST_DECLARE_APP_ARGS(args,
00532 AST_APP_ARG(n);
00533 AST_APP_ARG(name);
00534 );
00535
00536 if (!chan) {
00537 ast_log(LOG_ERROR, "LOCAL_PEEK must be called on an active channel\n");
00538 return -1;
00539 }
00540
00541 AST_STANDARD_RAW_ARGS(args, data);
00542 n = atoi(args.n);
00543 *buf = '\0';
00544
00545 ast_channel_lock(chan);
00546 AST_LIST_TRAVERSE(&chan->varshead, variables, entries) {
00547 if (!strcmp(args.name, ast_var_name(variables)) && ++found > n) {
00548 ast_copy_string(buf, ast_var_value(variables), len);
00549 break;
00550 }
00551 }
00552 ast_channel_unlock(chan);
00553 return 0;
00554 }
00555
00556 static struct ast_custom_function peek_function = {
00557 .name = "LOCAL_PEEK",
00558 .read = peek_read,
00559 };
00560
00561 static int handle_gosub(struct ast_channel *chan, AGI *agi, int argc, char **argv)
00562 {
00563 int old_priority, priority;
00564 char old_context[AST_MAX_CONTEXT], old_extension[AST_MAX_EXTENSION];
00565 struct ast_app *theapp;
00566 char *gosub_args;
00567
00568 if (argc < 4 || argc > 5) {
00569 return RESULT_SHOWUSAGE;
00570 }
00571
00572 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] : "");
00573
00574 if (sscanf(argv[3], "%30d", &priority) != 1 || priority < 1) {
00575
00576 if ((priority = ast_findlabel_extension(chan, argv[1], argv[2], argv[3], chan->cid.cid_num)) < 0) {
00577 ast_log(LOG_ERROR, "Priority '%s' not found in '%s@%s'\n", argv[3], argv[2], argv[1]);
00578 ast_agi_send(agi->fd, chan, "200 result=-1 Gosub label not found\n");
00579 return RESULT_FAILURE;
00580 }
00581 } else if (!ast_exists_extension(chan, argv[1], argv[2], priority, chan->cid.cid_num)) {
00582 ast_agi_send(agi->fd, chan, "200 result=-1 Gosub label not found\n");
00583 return RESULT_FAILURE;
00584 }
00585
00586
00587 ast_copy_string(old_context, chan->context, sizeof(old_context));
00588 ast_copy_string(old_extension, chan->exten, sizeof(old_extension));
00589 old_priority = chan->priority;
00590
00591 if (!(theapp = pbx_findapp("Gosub"))) {
00592 ast_log(LOG_ERROR, "Gosub() cannot be found in the list of loaded applications\n");
00593 ast_agi_send(agi->fd, chan, "503 result=-2 Gosub is not loaded\n");
00594 return RESULT_FAILURE;
00595 }
00596
00597
00598
00599
00600
00601
00602
00603 if (argc == 5) {
00604 if (asprintf(&gosub_args, "%s,%s,%d(%s)", argv[1], argv[2], priority + (chan->pbx ? 1 : 0), argv[4]) < 0) {
00605 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00606 gosub_args = NULL;
00607 }
00608 } else {
00609 if (asprintf(&gosub_args, "%s,%s,%d", argv[1], argv[2], priority + (chan->pbx ? 1 : 0)) < 0) {
00610 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00611 gosub_args = NULL;
00612 }
00613 }
00614
00615 if (gosub_args) {
00616 int res;
00617
00618 ast_debug(1, "Trying gosub with arguments '%s'\n", gosub_args);
00619
00620 if ((res = pbx_exec(chan, theapp, gosub_args)) == 0) {
00621 struct ast_pbx *pbx = chan->pbx;
00622 struct ast_pbx_args args;
00623 struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
00624 AST_LIST_HEAD(, gosub_stack_frame) *oldlist = stack_store->data;
00625 struct gosub_stack_frame *cur = AST_LIST_FIRST(oldlist);
00626 cur->is_agi = 1;
00627
00628 memset(&args, 0, sizeof(args));
00629 args.no_hangup_chan = 1;
00630
00631 chan->pbx = NULL;
00632 ast_agi_send(agi->fd, chan, "100 result=0 Trying...\n");
00633 ast_pbx_run_args(chan, &args);
00634 ast_agi_send(agi->fd, chan, "200 result=0 Gosub complete\n");
00635 if (chan->pbx) {
00636 ast_free(chan->pbx);
00637 }
00638 chan->pbx = pbx;
00639 } else {
00640 ast_agi_send(agi->fd, chan, "200 result=%d Gosub failed\n", res);
00641 }
00642 ast_free(gosub_args);
00643 } else {
00644 ast_agi_send(agi->fd, chan, "503 result=-2 Memory allocation failure\n");
00645 return RESULT_FAILURE;
00646 }
00647
00648
00649 ast_copy_string(chan->context, old_context, sizeof(chan->context));
00650 ast_copy_string(chan->exten, old_extension, sizeof(chan->exten));
00651 chan->priority = old_priority;
00652
00653 return RESULT_SUCCESS;
00654 }
00655
00656 static char usage_gosub[] =
00657 " Usage: GOSUB <context> <extension> <priority> [<optional-argument>]\n"
00658 " Cause the channel to execute the specified dialplan subroutine, returning\n"
00659 " to the dialplan with execution of a Return()\n";
00660
00661 struct agi_command gosub_agi_command =
00662 { { "gosub", NULL }, handle_gosub, "Execute a dialplan subroutine", usage_gosub , 0 };
00663
00664 static int unload_module(void)
00665 {
00666 if (ast_agi_unregister) {
00667 ast_agi_unregister(ast_module_info->self, &gosub_agi_command);
00668 }
00669
00670 ast_unregister_application(app_return);
00671 ast_unregister_application(app_pop);
00672 ast_unregister_application(app_gosubif);
00673 ast_unregister_application(app_gosub);
00674 ast_custom_function_unregister(&local_function);
00675 ast_custom_function_unregister(&peek_function);
00676
00677 return 0;
00678 }
00679
00680 static int load_module(void)
00681 {
00682 if (ast_agi_register) {
00683 ast_agi_register(ast_module_info->self, &gosub_agi_command);
00684 }
00685
00686 ast_register_application_xml(app_pop, pop_exec);
00687 ast_register_application_xml(app_return, return_exec);
00688 ast_register_application_xml(app_gosubif, gosubif_exec);
00689 ast_register_application_xml(app_gosub, gosub_exec);
00690 ast_custom_function_register(&local_function);
00691 ast_custom_function_register(&peek_function);
00692
00693 return 0;
00694 }
00695
00696 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialplan subroutines (Gosub, Return, etc)");