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: 211569 $")
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
00042
00043 #define ASTERISK_AGI_OPTIONAL
00044 #include "asterisk/agi.h"
00045
00046
00047 static const char *app_gosub = "Gosub";
00048 static const char *app_gosubif = "GosubIf";
00049 static const char *app_return = "Return";
00050 static const char *app_pop = "StackPop";
00051
00052 static const char *gosub_synopsis = "Jump to label, saving return address";
00053 static const char *gosubif_synopsis = "Conditionally jump to label, saving return address";
00054 static const char *return_synopsis = "Return from gosub routine";
00055 static const char *pop_synopsis = "Remove one address from gosub stack";
00056
00057 static const char *gosub_descrip =
00058 " Gosub([[context,]exten,]priority[(arg1[,...][,argN])]):\n"
00059 "Jumps to the label specified, saving the return address.\n";
00060 static const char *gosubif_descrip =
00061 " GosubIf(condition?labeliftrue[(arg1[,...])][:labeliffalse[(arg1[,...])]]):\n"
00062 "If the condition is true, then jump to labeliftrue. If false, jumps to\n"
00063 "labeliffalse, if specified. In either case, a jump saves the return point\n"
00064 "in the dialplan, to be returned to with a Return.\n";
00065 static const char *return_descrip =
00066 " Return([return-value]):\n"
00067 "Jumps to the last label on the stack, removing it. The return value, if\n"
00068 "any, is saved in the channel variable GOSUB_RETVAL.\n";
00069 static const char *pop_descrip =
00070 " StackPop():\n"
00071 "Removes last label on the stack, discarding it.\n";
00072
00073 static void gosub_free(void *data);
00074
00075 static struct ast_datastore_info stack_info = {
00076 .type = "GOSUB",
00077 .destroy = gosub_free,
00078 };
00079
00080 struct gosub_stack_frame {
00081 AST_LIST_ENTRY(gosub_stack_frame) entries;
00082
00083 unsigned char arguments;
00084 struct varshead varshead;
00085 int priority;
00086 char *context;
00087 char extension[0];
00088 };
00089
00090 static int frame_set_var(struct ast_channel *chan, struct gosub_stack_frame *frame, const char *var, const char *value)
00091 {
00092 struct ast_var_t *variables;
00093 int found = 0;
00094
00095
00096 AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
00097 if (!strcmp(var, ast_var_name(variables))) {
00098 found = 1;
00099 break;
00100 }
00101 }
00102
00103 if (!found) {
00104 variables = ast_var_assign(var, "");
00105 AST_LIST_INSERT_HEAD(&frame->varshead, variables, entries);
00106 pbx_builtin_pushvar_helper(chan, var, value);
00107 } else {
00108 pbx_builtin_setvar_helper(chan, var, value);
00109 }
00110
00111 manager_event(EVENT_FLAG_DIALPLAN, "VarSet",
00112 "Channel: %s\r\n"
00113 "Variable: LOCAL(%s)\r\n"
00114 "Value: %s\r\n"
00115 "Uniqueid: %s\r\n",
00116 chan->name, var, value, chan->uniqueid);
00117 return 0;
00118 }
00119
00120 static void gosub_release_frame(struct ast_channel *chan, struct gosub_stack_frame *frame)
00121 {
00122 struct ast_var_t *vardata;
00123
00124
00125
00126
00127
00128
00129
00130 while ((vardata = AST_LIST_REMOVE_HEAD(&frame->varshead, entries))) {
00131 if (chan)
00132 pbx_builtin_setvar_helper(chan, ast_var_name(vardata), NULL);
00133 ast_var_delete(vardata);
00134 }
00135
00136 ast_free(frame);
00137 }
00138
00139 static struct gosub_stack_frame *gosub_allocate_frame(const char *context, const char *extension, int priority, unsigned char arguments)
00140 {
00141 struct gosub_stack_frame *new = NULL;
00142 int len_extension = strlen(extension), len_context = strlen(context);
00143
00144 if ((new = ast_calloc(1, sizeof(*new) + 2 + len_extension + len_context))) {
00145 AST_LIST_HEAD_INIT_NOLOCK(&new->varshead);
00146 strcpy(new->extension, extension);
00147 new->context = new->extension + len_extension + 1;
00148 strcpy(new->context, context);
00149 new->priority = priority;
00150 new->arguments = arguments;
00151 }
00152 return new;
00153 }
00154
00155 static void gosub_free(void *data)
00156 {
00157 AST_LIST_HEAD(, gosub_stack_frame) *oldlist = data;
00158 struct gosub_stack_frame *oldframe;
00159 AST_LIST_LOCK(oldlist);
00160 while ((oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries))) {
00161 gosub_release_frame(NULL, oldframe);
00162 }
00163 AST_LIST_UNLOCK(oldlist);
00164 AST_LIST_HEAD_DESTROY(oldlist);
00165 ast_free(oldlist);
00166 }
00167
00168 static int pop_exec(struct ast_channel *chan, void *data)
00169 {
00170 struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
00171 struct gosub_stack_frame *oldframe;
00172 AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
00173
00174 if (!stack_store) {
00175 ast_log(LOG_WARNING, "%s called with no gosub stack allocated.\n", app_pop);
00176 return 0;
00177 }
00178
00179 oldlist = stack_store->data;
00180 AST_LIST_LOCK(oldlist);
00181 oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
00182 AST_LIST_UNLOCK(oldlist);
00183
00184 if (oldframe) {
00185 gosub_release_frame(chan, oldframe);
00186 } else {
00187 ast_debug(1, "%s called with an empty gosub stack\n", app_pop);
00188 }
00189 return 0;
00190 }
00191
00192 static int return_exec(struct ast_channel *chan, void *data)
00193 {
00194 struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
00195 struct gosub_stack_frame *oldframe;
00196 AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
00197 char *retval = data;
00198
00199 if (!stack_store) {
00200 ast_log(LOG_ERROR, "Return without Gosub: stack is unallocated\n");
00201 return -1;
00202 }
00203
00204 oldlist = stack_store->data;
00205 AST_LIST_LOCK(oldlist);
00206 oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
00207 AST_LIST_UNLOCK(oldlist);
00208
00209 if (!oldframe) {
00210 ast_log(LOG_ERROR, "Return without Gosub: stack is empty\n");
00211 return -1;
00212 }
00213
00214 ast_explicit_goto(chan, oldframe->context, oldframe->extension, oldframe->priority);
00215 gosub_release_frame(chan, oldframe);
00216
00217
00218 pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", S_OR(retval, ""));
00219 return 0;
00220 }
00221
00222 static int gosub_exec(struct ast_channel *chan, void *data)
00223 {
00224 struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
00225 AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
00226 struct gosub_stack_frame *newframe;
00227 char argname[15], *tmp = ast_strdupa(data), *label, *endparen;
00228 int i;
00229 AST_DECLARE_APP_ARGS(args2,
00230 AST_APP_ARG(argval)[100];
00231 );
00232
00233 if (ast_strlen_zero(data)) {
00234 ast_log(LOG_ERROR, "%s requires an argument: %s([[context,]exten,]priority[(arg1[,...][,argN])])\n", app_gosub, app_gosub);
00235 return -1;
00236 }
00237
00238 if (!stack_store) {
00239 ast_debug(1, "Channel %s has no datastore, so we're allocating one.\n", chan->name);
00240 stack_store = ast_datastore_alloc(&stack_info, NULL);
00241 if (!stack_store) {
00242 ast_log(LOG_ERROR, "Unable to allocate new datastore. Gosub will fail.\n");
00243 return -1;
00244 }
00245
00246 oldlist = ast_calloc(1, sizeof(*oldlist));
00247 if (!oldlist) {
00248 ast_log(LOG_ERROR, "Unable to allocate datastore list head. Gosub will fail.\n");
00249 ast_datastore_free(stack_store);
00250 return -1;
00251 }
00252
00253 stack_store->data = oldlist;
00254 AST_LIST_HEAD_INIT(oldlist);
00255 ast_channel_datastore_add(chan, stack_store);
00256 }
00257
00258
00259
00260 label = strsep(&tmp, "(");
00261 if (tmp) {
00262 endparen = strrchr(tmp, ')');
00263 if (endparen)
00264 *endparen = '\0';
00265 else
00266 ast_log(LOG_WARNING, "Ouch. No closing paren: '%s'?\n", (char *)data);
00267 AST_STANDARD_RAW_ARGS(args2, tmp);
00268 } else
00269 args2.argc = 0;
00270
00271
00272 newframe = gosub_allocate_frame(chan->context, chan->exten, chan->priority + 1, args2.argc);
00273
00274 if (!newframe) {
00275 return -1;
00276 }
00277
00278 if (ast_parseable_goto(chan, label)) {
00279 ast_log(LOG_ERROR, "Gosub address is invalid: '%s'\n", (char *)data);
00280 ast_free(newframe);
00281 return -1;
00282 }
00283
00284 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)) {
00285 ast_log(LOG_ERROR, "Attempt to reach a non-existent destination for gosub: (Context:%s, Extension:%s, Priority:%d)\n",
00286 chan->context, chan->exten, chan->priority);
00287 ast_copy_string(chan->context, newframe->context, sizeof(chan->context));
00288 ast_copy_string(chan->exten, newframe->extension, sizeof(chan->exten));
00289 chan->priority = newframe->priority;
00290 ast_free(newframe);
00291 return -1;
00292 }
00293
00294
00295 for (i = 0; i < args2.argc; i++) {
00296 snprintf(argname, sizeof(argname), "ARG%d", i + 1);
00297 frame_set_var(chan, newframe, argname, args2.argval[i]);
00298 ast_debug(1, "Setting '%s' to '%s'\n", argname, args2.argval[i]);
00299 }
00300
00301
00302 oldlist = stack_store->data;
00303 AST_LIST_LOCK(oldlist);
00304 AST_LIST_INSERT_HEAD(oldlist, newframe, entries);
00305 AST_LIST_UNLOCK(oldlist);
00306
00307 return 0;
00308 }
00309
00310 static int gosubif_exec(struct ast_channel *chan, void *data)
00311 {
00312 char *args;
00313 int res=0;
00314 AST_DECLARE_APP_ARGS(cond,
00315 AST_APP_ARG(ition);
00316 AST_APP_ARG(labels);
00317 );
00318 AST_DECLARE_APP_ARGS(label,
00319 AST_APP_ARG(iftrue);
00320 AST_APP_ARG(iffalse);
00321 );
00322
00323 if (ast_strlen_zero(data)) {
00324 ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
00325 return 0;
00326 }
00327
00328 args = ast_strdupa(data);
00329 AST_NONSTANDARD_RAW_ARGS(cond, args, '?');
00330 if (cond.argc != 2) {
00331 ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
00332 return 0;
00333 }
00334
00335 AST_NONSTANDARD_RAW_ARGS(label, cond.labels, ':');
00336
00337 if (pbx_checkcondition(cond.ition)) {
00338 if (!ast_strlen_zero(label.iftrue))
00339 res = gosub_exec(chan, label.iftrue);
00340 } else if (!ast_strlen_zero(label.iffalse)) {
00341 res = gosub_exec(chan, label.iffalse);
00342 }
00343
00344 return res;
00345 }
00346
00347 static int local_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00348 {
00349 struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
00350 AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
00351 struct gosub_stack_frame *frame;
00352 struct ast_var_t *variables;
00353
00354 if (!stack_store)
00355 return -1;
00356
00357 oldlist = stack_store->data;
00358 AST_LIST_LOCK(oldlist);
00359 if (!(frame = AST_LIST_FIRST(oldlist))) {
00360
00361 AST_LIST_UNLOCK(oldlist);
00362 return -1;
00363 }
00364
00365 AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
00366 if (!strcmp(data, ast_var_name(variables))) {
00367 const char *tmp;
00368 ast_channel_lock(chan);
00369 tmp = pbx_builtin_getvar_helper(chan, data);
00370 ast_copy_string(buf, S_OR(tmp, ""), len);
00371 ast_channel_unlock(chan);
00372 break;
00373 }
00374 }
00375 AST_LIST_UNLOCK(oldlist);
00376 return 0;
00377 }
00378
00379 static int local_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
00380 {
00381 struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
00382 AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
00383 struct gosub_stack_frame *frame;
00384
00385 if (!stack_store) {
00386 ast_log(LOG_ERROR, "Tried to set LOCAL(%s), but we aren't within a Gosub routine\n", var);
00387 return -1;
00388 }
00389
00390 oldlist = stack_store->data;
00391 AST_LIST_LOCK(oldlist);
00392 frame = AST_LIST_FIRST(oldlist);
00393
00394 if (frame)
00395 frame_set_var(chan, frame, var, value);
00396
00397 AST_LIST_UNLOCK(oldlist);
00398
00399 return 0;
00400 }
00401
00402 static struct ast_custom_function local_function = {
00403 .name = "LOCAL",
00404 .synopsis = "Variables local to the gosub stack frame",
00405 .syntax = "LOCAL(<varname>)",
00406 .write = local_write,
00407 .read = local_read,
00408 };
00409
00410 static int handle_gosub(struct ast_channel *chan, AGI *agi, int argc, char **argv)
00411 {
00412 int old_priority, priority;
00413 char old_context[AST_MAX_CONTEXT], old_extension[AST_MAX_EXTENSION];
00414 struct ast_app *theapp;
00415 char *gosub_args;
00416
00417 if (argc < 4 || argc > 5) {
00418 return RESULT_SHOWUSAGE;
00419 }
00420
00421 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] : "");
00422
00423 if (sscanf(argv[3], "%30d", &priority) != 1 || priority < 1) {
00424
00425 if ((priority = ast_findlabel_extension(chan, argv[1], argv[2], argv[3], chan->cid.cid_num)) < 0) {
00426 ast_log(LOG_ERROR, "Priority '%s' not found in '%s@%s'\n", argv[3], argv[2], argv[1]);
00427 ast_agi_send(agi->fd, chan, "200 result=-1 Gosub label not found\n");
00428 return RESULT_FAILURE;
00429 }
00430 } else if (!ast_exists_extension(chan, argv[1], argv[2], priority, chan->cid.cid_num)) {
00431 ast_agi_send(agi->fd, chan, "200 result=-1 Gosub label not found\n");
00432 return RESULT_FAILURE;
00433 }
00434
00435
00436 ast_copy_string(old_context, chan->context, sizeof(old_context));
00437 ast_copy_string(old_extension, chan->exten, sizeof(old_extension));
00438 old_priority = chan->priority;
00439
00440 if (!(theapp = pbx_findapp("Gosub"))) {
00441 ast_log(LOG_ERROR, "Gosub() cannot be found in the list of loaded applications\n");
00442 ast_agi_send(agi->fd, chan, "503 result=-2 Gosub is not loaded\n");
00443 return RESULT_FAILURE;
00444 }
00445
00446
00447
00448
00449
00450
00451
00452 if (argc == 5) {
00453 if (asprintf(&gosub_args, "%s,%s,%d(%s)", argv[1], argv[2], priority + 1, argv[4]) < 0) {
00454 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00455 gosub_args = NULL;
00456 }
00457 } else {
00458 if (asprintf(&gosub_args, "%s,%s,%d", argv[1], argv[2], priority + 1) < 0) {
00459 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00460 gosub_args = NULL;
00461 }
00462 }
00463
00464 if (gosub_args) {
00465 int res;
00466
00467 ast_debug(1, "Trying gosub with arguments '%s'\n", gosub_args);
00468
00469 if ((res = pbx_exec(chan, theapp, gosub_args)) == 0) {
00470 struct ast_pbx *pbx = chan->pbx;
00471 struct ast_pbx_args args;
00472 memset(&args, 0, sizeof(args));
00473 args.no_hangup_chan = 1;
00474
00475 chan->pbx = NULL;
00476 ast_agi_send(agi->fd, chan, "100 result=0 Trying...\n");
00477 ast_pbx_run_args(chan, &args);
00478 ast_agi_send(agi->fd, chan, "200 result=0 Gosub complete\n");
00479 if (chan->pbx) {
00480 ast_free(chan->pbx);
00481 }
00482 chan->pbx = pbx;
00483 } else {
00484 ast_agi_send(agi->fd, chan, "200 result=%d Gosub failed\n", res);
00485 }
00486 ast_free(gosub_args);
00487 } else {
00488 ast_agi_send(agi->fd, chan, "503 result=-2 Memory allocation failure\n");
00489 return RESULT_FAILURE;
00490 }
00491
00492
00493 ast_copy_string(chan->context, old_context, sizeof(chan->context));
00494 ast_copy_string(chan->exten, old_extension, sizeof(chan->exten));
00495 chan->priority = old_priority;
00496
00497 return RESULT_SUCCESS;
00498 }
00499
00500 static char usage_gosub[] =
00501 " Usage: GOSUB <context> <extension> <priority> [<optional-argument>]\n"
00502 " Cause the channel to execute the specified dialplan subroutine, returning\n"
00503 " to the dialplan with execution of a Return()\n";
00504
00505 struct agi_command gosub_agi_command =
00506 { { "gosub", NULL }, handle_gosub, "Execute a dialplan subroutine", usage_gosub , 0 };
00507
00508 static int unload_module(void)
00509 {
00510 if (ast_agi_unregister) {
00511 ast_agi_unregister(ast_module_info->self, &gosub_agi_command);
00512 }
00513
00514 ast_unregister_application(app_return);
00515 ast_unregister_application(app_pop);
00516 ast_unregister_application(app_gosubif);
00517 ast_unregister_application(app_gosub);
00518 ast_custom_function_unregister(&local_function);
00519
00520 return 0;
00521 }
00522
00523 static int load_module(void)
00524 {
00525
00526
00527
00528 if (ast_agi_register) {
00529 ast_agi_register(ast_module_info->self, &gosub_agi_command);
00530 }
00531
00532 ast_register_application(app_pop, pop_exec, pop_synopsis, pop_descrip);
00533 ast_register_application(app_return, return_exec, return_synopsis, return_descrip);
00534 ast_register_application(app_gosubif, gosubif_exec, gosubif_synopsis, gosubif_descrip);
00535 ast_register_application(app_gosub, gosub_exec, gosub_synopsis, gosub_descrip);
00536 ast_custom_function_register(&local_function);
00537
00538 return 0;
00539 }
00540
00541 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialplan subroutines (Gosub, Return, etc)");