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: 419630 $")
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
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208 static const char app_gosub[] = "Gosub";
00209 static const char app_gosubif[] = "GosubIf";
00210 static const char app_return[] = "Return";
00211 static const char app_pop[] = "StackPop";
00212
00213 static void gosub_free(void *data);
00214
00215 static const struct ast_datastore_info stack_info = {
00216 .type = "GOSUB",
00217 .destroy = gosub_free,
00218 };
00219
00220 struct gosub_stack_frame {
00221 AST_LIST_ENTRY(gosub_stack_frame) entries;
00222
00223 unsigned char arguments;
00224 struct varshead varshead;
00225 int priority;
00226
00227 unsigned int is_special:1;
00228 char *context;
00229 char extension[0];
00230 };
00231
00232 AST_LIST_HEAD(gosub_stack_list, gosub_stack_frame);
00233
00234 static int frame_set_var(struct ast_channel *chan, struct gosub_stack_frame *frame, const char *var, const char *value)
00235 {
00236 struct ast_var_t *variables;
00237 int found = 0;
00238
00239
00240 AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
00241 if (!strcmp(var, ast_var_name(variables))) {
00242 found = 1;
00243 break;
00244 }
00245 }
00246
00247 if (!found) {
00248 if ((variables = ast_var_assign(var, ""))) {
00249 AST_LIST_INSERT_HEAD(&frame->varshead, variables, entries);
00250 }
00251 pbx_builtin_pushvar_helper(chan, var, value);
00252 } else {
00253 pbx_builtin_setvar_helper(chan, var, value);
00254 }
00255
00256 manager_event(EVENT_FLAG_DIALPLAN, "VarSet",
00257 "Channel: %s\r\n"
00258 "Variable: LOCAL(%s)\r\n"
00259 "Value: %s\r\n"
00260 "Uniqueid: %s\r\n",
00261 chan->name, var, value, chan->uniqueid);
00262 return 0;
00263 }
00264
00265 static void gosub_release_frame(struct ast_channel *chan, struct gosub_stack_frame *frame)
00266 {
00267 struct ast_var_t *vardata;
00268
00269
00270
00271
00272
00273
00274
00275 while ((vardata = AST_LIST_REMOVE_HEAD(&frame->varshead, entries))) {
00276 if (chan)
00277 pbx_builtin_setvar_helper(chan, ast_var_name(vardata), NULL);
00278 ast_var_delete(vardata);
00279 }
00280
00281 ast_free(frame);
00282 }
00283
00284 static struct gosub_stack_frame *gosub_allocate_frame(const char *context, const char *extension, int priority, unsigned char arguments)
00285 {
00286 struct gosub_stack_frame *new = NULL;
00287 int len_extension = strlen(extension), len_context = strlen(context);
00288
00289 if ((new = ast_calloc(1, sizeof(*new) + 2 + len_extension + len_context))) {
00290 AST_LIST_HEAD_INIT_NOLOCK(&new->varshead);
00291 strcpy(new->extension, extension);
00292 new->context = new->extension + len_extension + 1;
00293 strcpy(new->context, context);
00294 new->priority = priority;
00295 new->arguments = arguments;
00296 }
00297 return new;
00298 }
00299
00300 static void gosub_free(void *data)
00301 {
00302 struct gosub_stack_list *oldlist = data;
00303 struct gosub_stack_frame *oldframe;
00304
00305 AST_LIST_LOCK(oldlist);
00306 while ((oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries))) {
00307 gosub_release_frame(NULL, oldframe);
00308 }
00309 AST_LIST_UNLOCK(oldlist);
00310 AST_LIST_HEAD_DESTROY(oldlist);
00311 ast_free(oldlist);
00312 }
00313
00314 static int pop_exec(struct ast_channel *chan, const char *data)
00315 {
00316 struct ast_datastore *stack_store;
00317 struct gosub_stack_frame *oldframe;
00318 struct gosub_stack_list *oldlist;
00319 int res = 0;
00320
00321 ast_channel_lock(chan);
00322 if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
00323 ast_log(LOG_WARNING, "%s called with no gosub stack allocated.\n", app_pop);
00324 ast_channel_unlock(chan);
00325 return 0;
00326 }
00327
00328 oldlist = stack_store->data;
00329 AST_LIST_LOCK(oldlist);
00330 oldframe = AST_LIST_FIRST(oldlist);
00331 if (oldframe) {
00332 if (oldframe->is_special) {
00333 ast_debug(1, "%s attempted to pop special return location.\n", app_pop);
00334
00335
00336 res = -1;
00337 } else {
00338 AST_LIST_REMOVE_HEAD(oldlist, entries);
00339 gosub_release_frame(chan, oldframe);
00340 }
00341 } else {
00342 ast_debug(1, "%s called with an empty gosub stack\n", app_pop);
00343 }
00344 AST_LIST_UNLOCK(oldlist);
00345 ast_channel_unlock(chan);
00346 return res;
00347 }
00348
00349 static int return_exec(struct ast_channel *chan, const char *data)
00350 {
00351 struct ast_datastore *stack_store;
00352 struct gosub_stack_frame *oldframe;
00353 struct gosub_stack_list *oldlist;
00354 const char *retval = data;
00355 int res = 0;
00356
00357 ast_channel_lock(chan);
00358 if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
00359 ast_log(LOG_ERROR, "Return without Gosub: stack is unallocated\n");
00360 ast_channel_unlock(chan);
00361 return -1;
00362 }
00363
00364 oldlist = stack_store->data;
00365 AST_LIST_LOCK(oldlist);
00366 oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
00367 AST_LIST_UNLOCK(oldlist);
00368
00369 if (!oldframe) {
00370 ast_log(LOG_ERROR, "Return without Gosub: stack is empty\n");
00371 ast_channel_unlock(chan);
00372 return -1;
00373 }
00374 if (oldframe->is_special) {
00375
00376 res = -1;
00377 }
00378
00379
00380
00381
00382
00383
00384 ast_copy_string(chan->context, oldframe->context, sizeof(chan->context));
00385 ast_copy_string(chan->exten, oldframe->extension, sizeof(chan->exten));
00386 if (ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP)) {
00387 --oldframe->priority;
00388 }
00389 chan->priority = oldframe->priority;
00390
00391 gosub_release_frame(chan, oldframe);
00392
00393
00394 pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", S_OR(retval, ""));
00395 ast_channel_unlock(chan);
00396 return res;
00397 }
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419 static const char *expand_gosub_args(struct ast_channel *chan, const char *args)
00420 {
00421 int len;
00422 char *parse;
00423 char *label;
00424 char *new_args;
00425 const char *context;
00426 const char *exten;
00427 const char *pri;
00428
00429
00430 parse = ast_strdupa(args);
00431 label = strsep(&parse, "(");
00432 if (parse) {
00433 char *endparen;
00434
00435 endparen = strrchr(parse, ')');
00436 if (endparen) {
00437 *endparen = '\0';
00438 } else {
00439 ast_log(LOG_WARNING, "Ouch. No closing paren: '%s'?\n", args);
00440 }
00441 }
00442
00443
00444 context = strsep(&label, ",");
00445 exten = strsep(&label, ",");
00446 pri = strsep(&label, ",");
00447 if (!exten) {
00448
00449 pri = context;
00450 exten = NULL;
00451 context = NULL;
00452 } else if (!pri) {
00453
00454 pri = exten;
00455 exten = context;
00456 context = NULL;
00457 }
00458
00459 ast_channel_lock(chan);
00460 if (ast_strlen_zero(exten)) {
00461 exten = chan->exten;
00462 }
00463 if (ast_strlen_zero(context)) {
00464 context = chan->context;
00465 }
00466 len = strlen(context) + strlen(exten) + strlen(pri) + 3;
00467 if (!ast_strlen_zero(parse)) {
00468 len += 2 + strlen(parse);
00469 }
00470 new_args = ast_malloc(len);
00471 if (new_args) {
00472 if (ast_strlen_zero(parse)) {
00473 snprintf(new_args, len, "%s,%s,%s", context, exten, pri);
00474 } else {
00475 snprintf(new_args, len, "%s,%s,%s(%s)", context, exten, pri, parse);
00476 }
00477 }
00478 ast_channel_unlock(chan);
00479
00480 ast_debug(4, "Gosub args:%s new_args:%s\n", args, new_args ? new_args : "");
00481
00482 return new_args;
00483 }
00484
00485 static int gosub_exec(struct ast_channel *chan, const char *data)
00486 {
00487 struct ast_datastore *stack_store;
00488 struct gosub_stack_list *oldlist;
00489 struct gosub_stack_frame *newframe;
00490 struct gosub_stack_frame *lastframe;
00491 char argname[15];
00492 char *parse;
00493 char *label;
00494 char *caller_id;
00495 char *orig_context;
00496 char *orig_exten;
00497 char *dest_context;
00498 char *dest_exten;
00499 int orig_priority;
00500 int dest_priority;
00501 int i;
00502 int max_argc = 0;
00503 AST_DECLARE_APP_ARGS(args2,
00504 AST_APP_ARG(argval)[100];
00505 );
00506
00507 if (ast_strlen_zero(data)) {
00508 ast_log(LOG_ERROR, "%s requires an argument: %s([[context,]exten,]priority[(arg1[,...][,argN])])\n", app_gosub, app_gosub);
00509 return -1;
00510 }
00511
00512
00513
00514
00515
00516
00517
00518 parse = ast_strdupa(data);
00519 label = strsep(&parse, "(");
00520 if (parse) {
00521 char *endparen;
00522
00523 endparen = strrchr(parse, ')');
00524 if (endparen) {
00525 *endparen = '\0';
00526 } else {
00527 ast_log(LOG_WARNING, "Ouch. No closing paren: '%s'?\n", data);
00528 }
00529 AST_STANDARD_RAW_ARGS(args2, parse);
00530 } else {
00531 args2.argc = 0;
00532 }
00533
00534 ast_channel_lock(chan);
00535 orig_context = ast_strdupa(chan->context);
00536 orig_exten = ast_strdupa(chan->exten);
00537 orig_priority = chan->priority;
00538 ast_channel_unlock(chan);
00539
00540 if (ast_parseable_goto(chan, label)) {
00541 ast_log(LOG_ERROR, "%s address is invalid: '%s'\n", app_gosub, data);
00542 goto error_exit;
00543 }
00544
00545 ast_channel_lock(chan);
00546 dest_context = ast_strdupa(chan->context);
00547 dest_exten = ast_strdupa(chan->exten);
00548 dest_priority = chan->priority;
00549 if (ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP)) {
00550 ++dest_priority;
00551 }
00552 caller_id = S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL);
00553 if (caller_id) {
00554 caller_id = ast_strdupa(caller_id);
00555 }
00556 ast_channel_unlock(chan);
00557
00558 if (!ast_exists_extension(chan, dest_context, dest_exten, dest_priority, caller_id)) {
00559 ast_log(LOG_ERROR, "Attempt to reach a non-existent destination for %s: (Context:%s, Extension:%s, Priority:%d)\n",
00560 app_gosub, dest_context, dest_exten, dest_priority);
00561 goto error_exit;
00562 }
00563
00564
00565
00566 ast_channel_lock(chan);
00567
00568
00569 if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
00570 ast_debug(1, "Channel %s has no datastore, so we're allocating one.\n",
00571 chan->name);
00572 stack_store = ast_datastore_alloc(&stack_info, NULL);
00573 if (!stack_store) {
00574 ast_log(LOG_ERROR, "Unable to allocate new datastore. %s failed.\n",
00575 app_gosub);
00576 goto error_exit_locked;
00577 }
00578
00579 oldlist = ast_calloc(1, sizeof(*oldlist));
00580 if (!oldlist) {
00581 ast_log(LOG_ERROR, "Unable to allocate datastore list head. %s failed.\n",
00582 app_gosub);
00583 ast_datastore_free(stack_store);
00584 goto error_exit_locked;
00585 }
00586 AST_LIST_HEAD_INIT(oldlist);
00587
00588 stack_store->data = oldlist;
00589 ast_channel_datastore_add(chan, stack_store);
00590 } else {
00591 oldlist = stack_store->data;
00592 }
00593
00594 if ((lastframe = AST_LIST_FIRST(oldlist))) {
00595 max_argc = lastframe->arguments;
00596 }
00597
00598
00599 if (args2.argc > max_argc) {
00600 max_argc = args2.argc;
00601 }
00602
00603
00604 newframe = gosub_allocate_frame(orig_context, orig_exten, orig_priority + 1, max_argc);
00605 if (!newframe) {
00606 goto error_exit_locked;
00607 }
00608
00609
00610 for (i = 0; i < max_argc; i++) {
00611 snprintf(argname, sizeof(argname), "ARG%d", i + 1);
00612 frame_set_var(chan, newframe, argname, i < args2.argc ? args2.argval[i] : "");
00613 ast_debug(1, "Setting '%s' to '%s'\n", argname, i < args2.argc ? args2.argval[i] : "");
00614 }
00615 snprintf(argname, sizeof(argname), "%u", args2.argc);
00616 frame_set_var(chan, newframe, "ARGC", argname);
00617
00618
00619 AST_LIST_LOCK(oldlist);
00620 AST_LIST_INSERT_HEAD(oldlist, newframe, entries);
00621 AST_LIST_UNLOCK(oldlist);
00622 ast_channel_unlock(chan);
00623
00624 return 0;
00625
00626 error_exit:
00627 ast_channel_lock(chan);
00628
00629 error_exit_locked:
00630
00631 ast_copy_string(chan->context, orig_context, sizeof(chan->context));
00632 ast_copy_string(chan->exten, orig_exten, sizeof(chan->exten));
00633 chan->priority = orig_priority;
00634 ast_channel_unlock(chan);
00635 return -1;
00636 }
00637
00638 static int gosubif_exec(struct ast_channel *chan, const char *data)
00639 {
00640 char *args;
00641 int res=0;
00642 AST_DECLARE_APP_ARGS(cond,
00643 AST_APP_ARG(ition);
00644 AST_APP_ARG(labels);
00645 );
00646 AST_DECLARE_APP_ARGS(label,
00647 AST_APP_ARG(iftrue);
00648 AST_APP_ARG(iffalse);
00649 );
00650
00651 if (ast_strlen_zero(data)) {
00652 ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
00653 return 0;
00654 }
00655
00656 args = ast_strdupa(data);
00657 AST_NONSTANDARD_RAW_ARGS(cond, args, '?');
00658 if (cond.argc != 2) {
00659 ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
00660 return 0;
00661 }
00662
00663 AST_NONSTANDARD_RAW_ARGS(label, cond.labels, ':');
00664
00665 if (pbx_checkcondition(cond.ition)) {
00666 if (!ast_strlen_zero(label.iftrue))
00667 res = gosub_exec(chan, label.iftrue);
00668 } else if (!ast_strlen_zero(label.iffalse)) {
00669 res = gosub_exec(chan, label.iffalse);
00670 }
00671
00672 return res;
00673 }
00674
00675 static int local_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00676 {
00677 struct ast_datastore *stack_store;
00678 struct gosub_stack_list *oldlist;
00679 struct gosub_stack_frame *frame;
00680 struct ast_var_t *variables;
00681
00682 if (!chan) {
00683 ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
00684 return -1;
00685 }
00686
00687 ast_channel_lock(chan);
00688 if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
00689 ast_channel_unlock(chan);
00690 return -1;
00691 }
00692
00693 oldlist = stack_store->data;
00694 AST_LIST_LOCK(oldlist);
00695 if (!(frame = AST_LIST_FIRST(oldlist))) {
00696
00697 AST_LIST_UNLOCK(oldlist);
00698 ast_channel_unlock(chan);
00699 return -1;
00700 }
00701
00702 AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
00703 if (!strcmp(data, ast_var_name(variables))) {
00704 const char *tmp;
00705 tmp = pbx_builtin_getvar_helper(chan, data);
00706 ast_copy_string(buf, S_OR(tmp, ""), len);
00707 break;
00708 }
00709 }
00710 AST_LIST_UNLOCK(oldlist);
00711 ast_channel_unlock(chan);
00712 return 0;
00713 }
00714
00715 static int local_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
00716 {
00717 struct ast_datastore *stack_store;
00718 struct gosub_stack_list *oldlist;
00719 struct gosub_stack_frame *frame;
00720
00721 if (!chan) {
00722 ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
00723 return -1;
00724 }
00725
00726 ast_channel_lock(chan);
00727 if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
00728 ast_log(LOG_ERROR, "Tried to set LOCAL(%s), but we aren't within a Gosub routine\n", var);
00729 ast_channel_unlock(chan);
00730 return -1;
00731 }
00732
00733 oldlist = stack_store->data;
00734 AST_LIST_LOCK(oldlist);
00735 frame = AST_LIST_FIRST(oldlist);
00736
00737 if (frame) {
00738 frame_set_var(chan, frame, var, value);
00739 }
00740
00741 AST_LIST_UNLOCK(oldlist);
00742 ast_channel_unlock(chan);
00743
00744 return 0;
00745 }
00746
00747 static struct ast_custom_function local_function = {
00748 .name = "LOCAL",
00749 .write = local_write,
00750 .read = local_read,
00751 };
00752
00753 static int peek_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00754 {
00755 int found = 0, n;
00756 struct ast_var_t *variables;
00757 AST_DECLARE_APP_ARGS(args,
00758 AST_APP_ARG(n);
00759 AST_APP_ARG(name);
00760 );
00761
00762 if (!chan) {
00763 ast_log(LOG_ERROR, "LOCAL_PEEK must be called on an active channel\n");
00764 return -1;
00765 }
00766
00767 AST_STANDARD_RAW_ARGS(args, data);
00768
00769 if (ast_strlen_zero(args.n) || ast_strlen_zero(args.name)) {
00770 ast_log(LOG_ERROR, "LOCAL_PEEK requires parameters n and varname\n");
00771 return -1;
00772 }
00773
00774 n = atoi(args.n);
00775 *buf = '\0';
00776
00777 ast_channel_lock(chan);
00778 AST_LIST_TRAVERSE(&chan->varshead, variables, entries) {
00779 if (!strcmp(args.name, ast_var_name(variables)) && ++found > n) {
00780 ast_copy_string(buf, ast_var_value(variables), len);
00781 break;
00782 }
00783 }
00784 ast_channel_unlock(chan);
00785 return 0;
00786 }
00787
00788 static struct ast_custom_function peek_function = {
00789 .name = "LOCAL_PEEK",
00790 .read = peek_read,
00791 };
00792
00793 static int stackpeek_read(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **str, ssize_t len)
00794 {
00795 struct ast_datastore *stack_store;
00796 struct gosub_stack_list *oldlist;
00797 struct gosub_stack_frame *frame;
00798 int n;
00799 AST_DECLARE_APP_ARGS(args,
00800 AST_APP_ARG(n);
00801 AST_APP_ARG(which);
00802 AST_APP_ARG(suppress);
00803 );
00804
00805 if (!chan) {
00806 ast_log(LOG_ERROR, "STACK_PEEK must be called on an active channel\n");
00807 return -1;
00808 }
00809
00810 data = ast_strdupa(data);
00811 AST_STANDARD_APP_ARGS(args, data);
00812
00813 if (ast_strlen_zero(args.n) || ast_strlen_zero(args.which)) {
00814 ast_log(LOG_ERROR, "STACK_PEEK requires parameters n and which\n");
00815 return -1;
00816 }
00817
00818 n = atoi(args.n);
00819 if (n <= 0) {
00820 ast_log(LOG_ERROR, "STACK_PEEK must be called with a positive peek value\n");
00821 return -1;
00822 }
00823
00824 ast_channel_lock(chan);
00825 if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
00826 if (!ast_true(args.suppress)) {
00827 ast_log(LOG_ERROR, "STACK_PEEK called on a channel without a gosub stack\n");
00828 }
00829 ast_channel_unlock(chan);
00830 return -1;
00831 }
00832
00833 oldlist = stack_store->data;
00834
00835 AST_LIST_LOCK(oldlist);
00836 AST_LIST_TRAVERSE(oldlist, frame, entries) {
00837 if (--n == 0) {
00838 break;
00839 }
00840 }
00841
00842 if (!frame) {
00843
00844 if (!ast_true(args.suppress)) {
00845 ast_log(LOG_ERROR, "Stack peek of '%s' is more stack frames than I have\n", args.n);
00846 }
00847 AST_LIST_UNLOCK(oldlist);
00848 ast_channel_unlock(chan);
00849 return -1;
00850 }
00851
00852 args.which = ast_skip_blanks(args.which);
00853
00854 switch (args.which[0]) {
00855 case 'l':
00856 ast_str_set(str, len, "%s,%s,%d", frame->context, frame->extension, frame->priority - 1);
00857 break;
00858 case 'c':
00859 ast_str_set(str, len, "%s", frame->context);
00860 break;
00861 case 'e':
00862 ast_str_set(str, len, "%s", frame->extension);
00863 break;
00864 case 'p':
00865 ast_str_set(str, len, "%d", frame->priority - 1);
00866 break;
00867 default:
00868 ast_log(LOG_ERROR, "Unknown argument '%s' to STACK_PEEK\n", args.which);
00869 break;
00870 }
00871
00872 AST_LIST_UNLOCK(oldlist);
00873 ast_channel_unlock(chan);
00874
00875 return 0;
00876 }
00877
00878 static struct ast_custom_function stackpeek_function = {
00879 .name = "STACK_PEEK",
00880 .read2 = stackpeek_read,
00881 };
00882
00883
00884
00885
00886
00887
00888
00889
00890
00891
00892
00893
00894
00895 static void balance_stack(struct ast_channel *chan)
00896 {
00897 struct ast_datastore *stack_store;
00898 struct gosub_stack_list *oldlist;
00899 struct gosub_stack_frame *oldframe;
00900 int found;
00901
00902 stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
00903 if (!stack_store) {
00904 ast_log(LOG_WARNING, "No %s stack allocated.\n", app_gosub);
00905 return;
00906 }
00907
00908 oldlist = stack_store->data;
00909 AST_LIST_LOCK(oldlist);
00910 do {
00911 oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
00912 if (!oldframe) {
00913 break;
00914 }
00915 found = oldframe->is_special;
00916 gosub_release_frame(chan, oldframe);
00917 } while (!found);
00918 AST_LIST_UNLOCK(oldlist);
00919 }
00920
00921
00922
00923
00924
00925
00926
00927
00928
00929
00930
00931
00932
00933
00934
00935
00936 static int gosub_run(struct ast_channel *chan, const char *sub_args, int ignore_hangup)
00937 {
00938 const char *saved_context;
00939 const char *saved_exten;
00940 int saved_priority;
00941 int saved_hangup_flags;
00942 int saved_autoloopflag;
00943 int res;
00944
00945 ast_channel_lock(chan);
00946
00947 ast_verb(3, "%s Internal %s(%s) start\n", chan->name, app_gosub, sub_args);
00948
00949
00950 saved_hangup_flags = chan->_softhangup
00951 & (AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE);
00952 if (saved_hangup_flags) {
00953 chan->_softhangup &= ~(AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE);
00954 }
00955
00956
00957 saved_autoloopflag = ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP);
00958 ast_set_flag(chan, AST_FLAG_IN_AUTOLOOP);
00959
00960
00961 saved_context = ast_strdupa(chan->context);
00962 saved_exten = ast_strdupa(chan->exten);
00963 saved_priority = chan->priority;
00964
00965 ast_debug(4, "%s Original location: %s,%s,%d\n", chan->name,
00966 saved_context, saved_exten, saved_priority);
00967
00968 ast_channel_unlock(chan);
00969 res = gosub_exec(chan, sub_args);
00970 ast_debug(4, "%s exited with status %d\n", app_gosub, res);
00971 ast_channel_lock(chan);
00972 if (!res) {
00973 struct ast_datastore *stack_store;
00974
00975
00976 stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
00977 if (!stack_store) {
00978
00979 ast_log(LOG_ERROR, "No %s stack!\n", app_gosub);
00980 res = -1;
00981 } else {
00982 struct gosub_stack_list *oldlist;
00983 struct gosub_stack_frame *cur;
00984
00985 oldlist = stack_store->data;
00986 cur = AST_LIST_FIRST(oldlist);
00987 cur->is_special = 1;
00988 }
00989 }
00990 if (!res) {
00991 int found = 0;
00992
00993
00994
00995
00996
00997
00998
00999
01000 do {
01001
01002 if (chan->_softhangup & AST_SOFTHANGUP_UNBRIDGE) {
01003 saved_hangup_flags |= AST_SOFTHANGUP_UNBRIDGE;
01004 chan->_softhangup &= ~AST_SOFTHANGUP_UNBRIDGE;
01005 }
01006 if (ast_check_hangup(chan)) {
01007 if (chan->_softhangup & AST_SOFTHANGUP_ASYNCGOTO) {
01008 ast_log(LOG_ERROR, "%s An async goto just messed up our execution location.\n",
01009 chan->name);
01010 break;
01011 }
01012 if (!ignore_hangup) {
01013 break;
01014 }
01015 }
01016
01017
01018 ++chan->priority;
01019
01020 ast_channel_unlock(chan);
01021 res = ast_spawn_extension(chan, chan->context, chan->exten, chan->priority,
01022 S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
01023 &found, 1);
01024 ast_channel_lock(chan);
01025 } while (!res);
01026 if (found && res) {
01027
01028 ast_debug(1, "Spawn extension (%s,%s,%d) exited with %d on '%s'\n",
01029 chan->context, chan->exten, chan->priority, res, chan->name);
01030 ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n",
01031 chan->context, chan->exten, chan->priority, chan->name);
01032 }
01033
01034
01035 if (chan->priority == saved_priority
01036 && !strcmp(chan->context, saved_context)
01037 && !strcmp(chan->exten, saved_exten)) {
01038 ast_verb(3, "%s Internal %s(%s) complete GOSUB_RETVAL=%s\n",
01039 chan->name, app_gosub, sub_args,
01040 S_OR(pbx_builtin_getvar_helper(chan, "GOSUB_RETVAL"), ""));
01041 } else {
01042 ast_log(LOG_NOTICE, "%s Abnormal '%s(%s)' exit. Popping routine return locations.\n",
01043 chan->name, app_gosub, sub_args);
01044 balance_stack(chan);
01045 pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", "");
01046 }
01047
01048
01049 res = 0;
01050 }
01051
01052 ast_debug(4, "%s Ending location: %s,%s,%d\n", chan->name,
01053 chan->context, chan->exten, chan->priority);
01054
01055
01056 if (!(chan->_softhangup & AST_SOFTHANGUP_ASYNCGOTO)) {
01057 ast_copy_string(chan->context, saved_context, sizeof(chan->context));
01058 ast_copy_string(chan->exten, saved_exten, sizeof(chan->exten));
01059 chan->priority = saved_priority;
01060 }
01061
01062
01063 ast_set2_flag(chan, saved_autoloopflag, AST_FLAG_IN_AUTOLOOP);
01064
01065
01066 if (saved_hangup_flags) {
01067 ast_softhangup_nolock(chan, saved_hangup_flags);
01068 }
01069
01070 ast_channel_unlock(chan);
01071
01072 return res;
01073 }
01074
01075 static int handle_gosub(struct ast_channel *chan, AGI *agi, int argc, const char * const *argv)
01076 {
01077 int res;
01078 int priority;
01079 int old_autoloopflag;
01080 int old_priority;
01081 const char *old_context;
01082 const char *old_extension;
01083 char *gosub_args;
01084
01085 if (argc < 4 || argc > 5) {
01086 return RESULT_SHOWUSAGE;
01087 }
01088
01089 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] : "");
01090
01091 if (sscanf(argv[3], "%30d", &priority) != 1 || priority < 1) {
01092
01093 priority = ast_findlabel_extension(chan, argv[1], argv[2], argv[3],
01094 S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL));
01095 if (priority < 0) {
01096 ast_log(LOG_ERROR, "Priority '%s' not found in '%s@%s'\n", argv[3], argv[2], argv[1]);
01097 ast_agi_send(agi->fd, chan, "200 result=-1 Gosub label not found\n");
01098 return RESULT_FAILURE;
01099 }
01100 } else if (!ast_exists_extension(chan, argv[1], argv[2], priority,
01101 S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
01102 ast_agi_send(agi->fd, chan, "200 result=-1 Gosub label not found\n");
01103 return RESULT_FAILURE;
01104 }
01105
01106 if (argc == 5) {
01107 if (ast_asprintf(&gosub_args, "%s,%s,%d(%s)", argv[1], argv[2], priority, argv[4]) < 0) {
01108 gosub_args = NULL;
01109 }
01110 } else {
01111 if (ast_asprintf(&gosub_args, "%s,%s,%d", argv[1], argv[2], priority) < 0) {
01112 gosub_args = NULL;
01113 }
01114 }
01115 if (!gosub_args) {
01116 ast_agi_send(agi->fd, chan, "503 result=-2 Memory allocation failure\n");
01117 return RESULT_FAILURE;
01118 }
01119
01120 ast_channel_lock(chan);
01121
01122 ast_verb(3, "%s AGI %s(%s) start\n", chan->name, app_gosub, gosub_args);
01123
01124
01125 old_autoloopflag = ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP);
01126 ast_set_flag(chan, AST_FLAG_IN_AUTOLOOP);
01127
01128
01129 old_context = ast_strdupa(chan->context);
01130 old_extension = ast_strdupa(chan->exten);
01131 old_priority = chan->priority;
01132
01133 ast_debug(4, "%s Original location: %s,%s,%d\n", chan->name,
01134 old_context, old_extension, old_priority);
01135 ast_channel_unlock(chan);
01136
01137 res = gosub_exec(chan, gosub_args);
01138 if (!res) {
01139 struct ast_datastore *stack_store;
01140
01141
01142 ast_channel_lock(chan);
01143 stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
01144 if (!stack_store) {
01145
01146 ast_log(LOG_ERROR, "No %s stack!\n", app_gosub);
01147 res = -1;
01148 } else {
01149 struct gosub_stack_list *oldlist;
01150 struct gosub_stack_frame *cur;
01151
01152 oldlist = stack_store->data;
01153 cur = AST_LIST_FIRST(oldlist);
01154 cur->is_special = 1;
01155 }
01156 ast_channel_unlock(chan);
01157 }
01158 if (!res) {
01159 struct ast_pbx *pbx;
01160 struct ast_pbx_args args;
01161 int abnormal_exit;
01162
01163 memset(&args, 0, sizeof(args));
01164 args.no_hangup_chan = 1;
01165
01166 ast_channel_lock(chan);
01167
01168
01169 ++chan->priority;
01170
01171
01172 pbx = chan->pbx;
01173 chan->pbx = NULL;
01174 ast_channel_unlock(chan);
01175
01176 ast_agi_send(agi->fd, chan, "100 result=0 Trying...\n");
01177 ast_pbx_run_args(chan, &args);
01178
01179 ast_channel_lock(chan);
01180 ast_free(chan->pbx);
01181 chan->pbx = pbx;
01182
01183
01184 if (chan->priority == old_priority
01185 && !strcmp(chan->context, old_context)
01186 && !strcmp(chan->exten, old_extension)) {
01187 ast_verb(3, "%s AGI %s(%s) complete GOSUB_RETVAL=%s\n",
01188 chan->name, app_gosub, gosub_args,
01189 S_OR(pbx_builtin_getvar_helper(chan, "GOSUB_RETVAL"), ""));
01190 abnormal_exit = 0;
01191 } else {
01192 ast_log(LOG_NOTICE, "%s Abnormal AGI %s(%s) exit. Popping routine return locations.\n",
01193 chan->name, app_gosub, gosub_args);
01194 balance_stack(chan);
01195 pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", "");
01196 abnormal_exit = 1;
01197 }
01198 ast_channel_unlock(chan);
01199
01200 ast_agi_send(agi->fd, chan, "200 result=0 Gosub complete%s\n",
01201 abnormal_exit ? " (abnormal exit)" : "");
01202 } else {
01203 ast_agi_send(agi->fd, chan, "200 result=%d Gosub failed\n", res);
01204 }
01205
01206
01207 free(gosub_args);
01208
01209 ast_channel_lock(chan);
01210 ast_debug(4, "%s Ending location: %s,%s,%d\n", chan->name,
01211 chan->context, chan->exten, chan->priority);
01212
01213
01214 ast_copy_string(chan->context, old_context, sizeof(chan->context));
01215 ast_copy_string(chan->exten, old_extension, sizeof(chan->exten));
01216 chan->priority = old_priority;
01217
01218
01219 ast_set2_flag(chan, old_autoloopflag, AST_FLAG_IN_AUTOLOOP);
01220 ast_channel_unlock(chan);
01221
01222 return RESULT_SUCCESS;
01223 }
01224
01225 static struct agi_command gosub_agi_command =
01226 { { "gosub", NULL }, handle_gosub, NULL, NULL, 0 };
01227
01228 static int unload_module(void)
01229 {
01230 ast_install_stack_functions(NULL);
01231
01232 ast_agi_unregister(ast_module_info->self, &gosub_agi_command);
01233
01234 ast_unregister_application(app_return);
01235 ast_unregister_application(app_pop);
01236 ast_unregister_application(app_gosubif);
01237 ast_unregister_application(app_gosub);
01238 ast_custom_function_unregister(&local_function);
01239 ast_custom_function_unregister(&peek_function);
01240 ast_custom_function_unregister(&stackpeek_function);
01241
01242 return 0;
01243 }
01244
01245 static int load_module(void)
01246 {
01247
01248 static struct ast_app_stack_funcs funcs = {
01249 .run_sub = gosub_run,
01250 .expand_sub_args = expand_gosub_args,
01251 };
01252
01253 ast_agi_register(ast_module_info->self, &gosub_agi_command);
01254
01255 ast_register_application_xml(app_pop, pop_exec);
01256 ast_register_application_xml(app_return, return_exec);
01257 ast_register_application_xml(app_gosubif, gosubif_exec);
01258 ast_register_application_xml(app_gosub, gosub_exec);
01259 ast_custom_function_register(&local_function);
01260 ast_custom_function_register(&peek_function);
01261 ast_custom_function_register(&stackpeek_function);
01262
01263 funcs.module = ast_module_info->self,
01264 ast_install_stack_functions(&funcs);
01265
01266 return 0;
01267 }
01268
01269 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT | AST_MODFLAG_LOAD_ORDER, "Dialplan subroutines (Gosub, Return, etc)",
01270 .load = load_module,
01271 .unload = unload_module,
01272 .load_pri = AST_MODPRI_APP_DEPEND,
01273 .nonoptreq = "res_agi",
01274 );