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