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