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: 236303 $")
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;
00333 char argname[15], *tmp = ast_strdupa(data), *label, *endparen;
00334 int i;
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 }
00363
00364
00365
00366 label = strsep(&tmp, "(");
00367 if (tmp) {
00368 endparen = strrchr(tmp, ')');
00369 if (endparen)
00370 *endparen = '\0';
00371 else
00372 ast_log(LOG_WARNING, "Ouch. No closing paren: '%s'?\n", (char *)data);
00373 AST_STANDARD_RAW_ARGS(args2, tmp);
00374 } else
00375 args2.argc = 0;
00376
00377
00378 newframe = gosub_allocate_frame(chan->context, chan->exten, chan->priority + 1, args2.argc);
00379
00380 if (!newframe) {
00381 return -1;
00382 }
00383
00384 if (ast_parseable_goto(chan, label)) {
00385 ast_log(LOG_ERROR, "Gosub address is invalid: '%s'\n", (char *)data);
00386 ast_free(newframe);
00387 return -1;
00388 }
00389
00390 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)) {
00391 ast_log(LOG_ERROR, "Attempt to reach a non-existent destination for gosub: (Context:%s, Extension:%s, Priority:%d)\n",
00392 chan->context, chan->exten, chan->priority);
00393 ast_copy_string(chan->context, newframe->context, sizeof(chan->context));
00394 ast_copy_string(chan->exten, newframe->extension, sizeof(chan->exten));
00395 chan->priority = newframe->priority;
00396 ast_free(newframe);
00397 return -1;
00398 }
00399
00400
00401 for (i = 0; i < args2.argc; i++) {
00402 snprintf(argname, sizeof(argname), "ARG%d", i + 1);
00403 frame_set_var(chan, newframe, argname, args2.argval[i]);
00404 ast_debug(1, "Setting '%s' to '%s'\n", argname, args2.argval[i]);
00405 }
00406 snprintf(argname, sizeof(argname), "%d", args2.argc);
00407 frame_set_var(chan, newframe, "ARGC", argname);
00408
00409
00410 oldlist = stack_store->data;
00411 AST_LIST_LOCK(oldlist);
00412 AST_LIST_INSERT_HEAD(oldlist, newframe, entries);
00413 AST_LIST_UNLOCK(oldlist);
00414
00415 return 0;
00416 }
00417
00418 static int gosubif_exec(struct ast_channel *chan, void *data)
00419 {
00420 char *args;
00421 int res=0;
00422 AST_DECLARE_APP_ARGS(cond,
00423 AST_APP_ARG(ition);
00424 AST_APP_ARG(labels);
00425 );
00426 AST_DECLARE_APP_ARGS(label,
00427 AST_APP_ARG(iftrue);
00428 AST_APP_ARG(iffalse);
00429 );
00430
00431 if (ast_strlen_zero(data)) {
00432 ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
00433 return 0;
00434 }
00435
00436 args = ast_strdupa(data);
00437 AST_NONSTANDARD_RAW_ARGS(cond, args, '?');
00438 if (cond.argc != 2) {
00439 ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
00440 return 0;
00441 }
00442
00443 AST_NONSTANDARD_RAW_ARGS(label, cond.labels, ':');
00444
00445 if (pbx_checkcondition(cond.ition)) {
00446 if (!ast_strlen_zero(label.iftrue))
00447 res = gosub_exec(chan, label.iftrue);
00448 } else if (!ast_strlen_zero(label.iffalse)) {
00449 res = gosub_exec(chan, label.iffalse);
00450 }
00451
00452 return res;
00453 }
00454
00455 static int local_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00456 {
00457 struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
00458 AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
00459 struct gosub_stack_frame *frame;
00460 struct ast_var_t *variables;
00461
00462 if (!stack_store)
00463 return -1;
00464
00465 oldlist = stack_store->data;
00466 AST_LIST_LOCK(oldlist);
00467 if (!(frame = AST_LIST_FIRST(oldlist))) {
00468
00469 AST_LIST_UNLOCK(oldlist);
00470 return -1;
00471 }
00472
00473 AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
00474 if (!strcmp(data, ast_var_name(variables))) {
00475 const char *tmp;
00476 ast_channel_lock(chan);
00477 tmp = pbx_builtin_getvar_helper(chan, data);
00478 ast_copy_string(buf, S_OR(tmp, ""), len);
00479 ast_channel_unlock(chan);
00480 break;
00481 }
00482 }
00483 AST_LIST_UNLOCK(oldlist);
00484 return 0;
00485 }
00486
00487 static int local_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
00488 {
00489 struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
00490 AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
00491 struct gosub_stack_frame *frame;
00492
00493 if (!stack_store) {
00494 ast_log(LOG_ERROR, "Tried to set LOCAL(%s), but we aren't within a Gosub routine\n", var);
00495 return -1;
00496 }
00497
00498 oldlist = stack_store->data;
00499 AST_LIST_LOCK(oldlist);
00500 frame = AST_LIST_FIRST(oldlist);
00501
00502 if (frame)
00503 frame_set_var(chan, frame, var, value);
00504
00505 AST_LIST_UNLOCK(oldlist);
00506
00507 return 0;
00508 }
00509
00510 static struct ast_custom_function local_function = {
00511 .name = "LOCAL",
00512 .write = local_write,
00513 .read = local_read,
00514 };
00515
00516 static int peek_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00517 {
00518 int found = 0, n;
00519 struct ast_var_t *variables;
00520 AST_DECLARE_APP_ARGS(args,
00521 AST_APP_ARG(n);
00522 AST_APP_ARG(name);
00523 );
00524
00525 if (!chan) {
00526 ast_log(LOG_ERROR, "LOCAL_PEEK must be called on an active channel\n");
00527 return -1;
00528 }
00529
00530 AST_STANDARD_RAW_ARGS(args, data);
00531 n = atoi(args.n);
00532 *buf = '\0';
00533
00534 ast_channel_lock(chan);
00535 AST_LIST_TRAVERSE(&chan->varshead, variables, entries) {
00536 if (!strcmp(args.name, ast_var_name(variables)) && ++found > n) {
00537 ast_copy_string(buf, ast_var_value(variables), len);
00538 break;
00539 }
00540 }
00541 ast_channel_unlock(chan);
00542 return 0;
00543 }
00544
00545 static struct ast_custom_function peek_function = {
00546 .name = "LOCAL_PEEK",
00547 .read = peek_read,
00548 };
00549
00550 static int handle_gosub(struct ast_channel *chan, AGI *agi, int argc, char **argv)
00551 {
00552 int old_priority, priority;
00553 char old_context[AST_MAX_CONTEXT], old_extension[AST_MAX_EXTENSION];
00554 struct ast_app *theapp;
00555 char *gosub_args;
00556
00557 if (argc < 4 || argc > 5) {
00558 return RESULT_SHOWUSAGE;
00559 }
00560
00561 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] : "");
00562
00563 if (sscanf(argv[3], "%30d", &priority) != 1 || priority < 1) {
00564
00565 if ((priority = ast_findlabel_extension(chan, argv[1], argv[2], argv[3], chan->cid.cid_num)) < 0) {
00566 ast_log(LOG_ERROR, "Priority '%s' not found in '%s@%s'\n", argv[3], argv[2], argv[1]);
00567 ast_agi_send(agi->fd, chan, "200 result=-1 Gosub label not found\n");
00568 return RESULT_FAILURE;
00569 }
00570 } else if (!ast_exists_extension(chan, argv[1], argv[2], priority, chan->cid.cid_num)) {
00571 ast_agi_send(agi->fd, chan, "200 result=-1 Gosub label not found\n");
00572 return RESULT_FAILURE;
00573 }
00574
00575
00576 ast_copy_string(old_context, chan->context, sizeof(old_context));
00577 ast_copy_string(old_extension, chan->exten, sizeof(old_extension));
00578 old_priority = chan->priority;
00579
00580 if (!(theapp = pbx_findapp("Gosub"))) {
00581 ast_log(LOG_ERROR, "Gosub() cannot be found in the list of loaded applications\n");
00582 ast_agi_send(agi->fd, chan, "503 result=-2 Gosub is not loaded\n");
00583 return RESULT_FAILURE;
00584 }
00585
00586
00587
00588
00589
00590
00591
00592 if (argc == 5) {
00593 if (asprintf(&gosub_args, "%s,%s,%d(%s)", argv[1], argv[2], priority + (chan->pbx ? 1 : 0), argv[4]) < 0) {
00594 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00595 gosub_args = NULL;
00596 }
00597 } else {
00598 if (asprintf(&gosub_args, "%s,%s,%d", argv[1], argv[2], priority + (chan->pbx ? 1 : 0)) < 0) {
00599 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00600 gosub_args = NULL;
00601 }
00602 }
00603
00604 if (gosub_args) {
00605 int res;
00606
00607 ast_debug(1, "Trying gosub with arguments '%s'\n", gosub_args);
00608
00609 if ((res = pbx_exec(chan, theapp, gosub_args)) == 0) {
00610 struct ast_pbx *pbx = chan->pbx;
00611 struct ast_pbx_args args;
00612 struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
00613 AST_LIST_HEAD(, gosub_stack_frame) *oldlist = stack_store->data;
00614 struct gosub_stack_frame *cur = AST_LIST_FIRST(oldlist);
00615 cur->is_agi = 1;
00616
00617 memset(&args, 0, sizeof(args));
00618 args.no_hangup_chan = 1;
00619
00620 chan->pbx = NULL;
00621 ast_agi_send(agi->fd, chan, "100 result=0 Trying...\n");
00622 ast_pbx_run_args(chan, &args);
00623 ast_agi_send(agi->fd, chan, "200 result=0 Gosub complete\n");
00624 if (chan->pbx) {
00625 ast_free(chan->pbx);
00626 }
00627 chan->pbx = pbx;
00628 } else {
00629 ast_agi_send(agi->fd, chan, "200 result=%d Gosub failed\n", res);
00630 }
00631 ast_free(gosub_args);
00632 } else {
00633 ast_agi_send(agi->fd, chan, "503 result=-2 Memory allocation failure\n");
00634 return RESULT_FAILURE;
00635 }
00636
00637
00638 ast_copy_string(chan->context, old_context, sizeof(chan->context));
00639 ast_copy_string(chan->exten, old_extension, sizeof(chan->exten));
00640 chan->priority = old_priority;
00641
00642 return RESULT_SUCCESS;
00643 }
00644
00645 static char usage_gosub[] =
00646 " Usage: GOSUB <context> <extension> <priority> [<optional-argument>]\n"
00647 " Cause the channel to execute the specified dialplan subroutine, returning\n"
00648 " to the dialplan with execution of a Return()\n";
00649
00650 struct agi_command gosub_agi_command =
00651 { { "gosub", NULL }, handle_gosub, "Execute a dialplan subroutine", usage_gosub , 0 };
00652
00653 static int unload_module(void)
00654 {
00655 if (ast_agi_unregister) {
00656 ast_agi_unregister(ast_module_info->self, &gosub_agi_command);
00657 }
00658
00659 ast_unregister_application(app_return);
00660 ast_unregister_application(app_pop);
00661 ast_unregister_application(app_gosubif);
00662 ast_unregister_application(app_gosub);
00663 ast_custom_function_unregister(&local_function);
00664 ast_custom_function_unregister(&peek_function);
00665
00666 return 0;
00667 }
00668
00669 static int load_module(void)
00670 {
00671 if (ast_agi_register) {
00672 ast_agi_register(ast_module_info->self, &gosub_agi_command);
00673 }
00674
00675 ast_register_application_xml(app_pop, pop_exec);
00676 ast_register_application_xml(app_return, return_exec);
00677 ast_register_application_xml(app_gosubif, gosubif_exec);
00678 ast_register_application_xml(app_gosub, gosub_exec);
00679 ast_custom_function_register(&local_function);
00680 ast_custom_function_register(&peek_function);
00681
00682 return 0;
00683 }
00684
00685 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialplan subroutines (Gosub, Return, etc)");