app_stack.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (c) 2004-2006 Tilghman Lesher <app_stack_v003@the-tilghman.com>.
  5. *
  6. * This code is released by the author with no restrictions on usage.
  7. *
  8. * See http://www.asterisk.org for more information about
  9. * the Asterisk project. Please do not directly contact
  10. * any of the maintainers of this project for assistance;
  11. * the project provides a web site, mailing lists and IRC
  12. * channels for your use.
  13. *
  14. * This program is free software, distributed under the terms of
  15. * the GNU General Public License Version 2. See the LICENSE file
  16. * at the top of the source tree.
  17. */
  18. /*! \file
  19. *
  20. * \brief Stack applications Gosub, Return, etc.
  21. *
  22. * \author Tilghman Lesher <app_stack_v003@the-tilghman.com>
  23. *
  24. * \ingroup applications
  25. */
  26. /*** MODULEINFO
  27. <use>res_agi</use>
  28. <support_level>core</support_level>
  29. ***/
  30. #include "asterisk.h"
  31. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  32. #include "asterisk/pbx.h"
  33. #include "asterisk/module.h"
  34. #include "asterisk/app.h"
  35. #include "asterisk/manager.h"
  36. #include "asterisk/channel.h"
  37. #include "asterisk/agi.h"
  38. /*** DOCUMENTATION
  39. <application name="Gosub" language="en_US">
  40. <synopsis>
  41. Jump to label, saving return address.
  42. </synopsis>
  43. <syntax>
  44. <parameter name="context" />
  45. <parameter name="exten" />
  46. <parameter name="priority" required="true" hasparams="optional">
  47. <argument name="arg1" multiple="true" required="true" />
  48. <argument name="argN" />
  49. </parameter>
  50. </syntax>
  51. <description>
  52. <para>Jumps to the label specified, saving the return address.</para>
  53. </description>
  54. <see-also>
  55. <ref type="application">GosubIf</ref>
  56. <ref type="application">Macro</ref>
  57. <ref type="application">Goto</ref>
  58. <ref type="application">Return</ref>
  59. <ref type="application">StackPop</ref>
  60. </see-also>
  61. </application>
  62. <application name="GosubIf" language="en_US">
  63. <synopsis>
  64. Conditionally jump to label, saving return address.
  65. </synopsis>
  66. <syntax argsep="?">
  67. <parameter name="condition" required="true" />
  68. <parameter name="destination" required="true" argsep=":">
  69. <argument name="labeliftrue" hasparams="optional">
  70. <argument name="arg1" required="true" multiple="true" />
  71. <argument name="argN" />
  72. </argument>
  73. <argument name="labeliffalse" hasparams="optional">
  74. <argument name="arg1" required="true" multiple="true" />
  75. <argument name="argN" />
  76. </argument>
  77. </parameter>
  78. </syntax>
  79. <description>
  80. <para>If the condition is true, then jump to labeliftrue. If false, jumps to
  81. labeliffalse, if specified. In either case, a jump saves the return point
  82. in the dialplan, to be returned to with a Return.</para>
  83. </description>
  84. <see-also>
  85. <ref type="application">Gosub</ref>
  86. <ref type="application">Return</ref>
  87. <ref type="application">MacroIf</ref>
  88. <ref type="function">IF</ref>
  89. <ref type="application">GotoIf</ref>
  90. </see-also>
  91. </application>
  92. <application name="Return" language="en_US">
  93. <synopsis>
  94. Return from gosub routine.
  95. </synopsis>
  96. <syntax>
  97. <parameter name="value">
  98. <para>Return value.</para>
  99. </parameter>
  100. </syntax>
  101. <description>
  102. <para>Jumps to the last label on the stack, removing it. The return <replaceable>value</replaceable>, if
  103. any, is saved in the channel variable <variable>GOSUB_RETVAL</variable>.</para>
  104. </description>
  105. <see-also>
  106. <ref type="application">Gosub</ref>
  107. <ref type="application">StackPop</ref>
  108. </see-also>
  109. </application>
  110. <application name="StackPop" language="en_US">
  111. <synopsis>
  112. Remove one address from gosub stack.
  113. </synopsis>
  114. <syntax />
  115. <description>
  116. <para>Removes last label on the stack, discarding it.</para>
  117. </description>
  118. <see-also>
  119. <ref type="application">Return</ref>
  120. <ref type="application">Gosub</ref>
  121. </see-also>
  122. </application>
  123. <function name="LOCAL" language="en_US">
  124. <synopsis>
  125. Manage variables local to the gosub stack frame.
  126. </synopsis>
  127. <syntax>
  128. <parameter name="varname" required="true" />
  129. </syntax>
  130. <description>
  131. <para>Read and write a variable local to the gosub stack frame, once we Return() it will be lost
  132. (or it will go back to whatever value it had before the Gosub()).</para>
  133. </description>
  134. <see-also>
  135. <ref type="application">Gosub</ref>
  136. <ref type="application">GosubIf</ref>
  137. <ref type="application">Return</ref>
  138. </see-also>
  139. </function>
  140. <function name="LOCAL_PEEK" language="en_US">
  141. <synopsis>
  142. Retrieve variables hidden by the local gosub stack frame.
  143. </synopsis>
  144. <syntax>
  145. <parameter name="n" required="true" />
  146. <parameter name="varname" required="true" />
  147. </syntax>
  148. <description>
  149. <para>Read a variable <replaceable>varname</replaceable> hidden by
  150. <replaceable>n</replaceable> levels of gosub stack frames. Note that ${LOCAL_PEEK(0,foo)}
  151. is the same as <variable>foo</variable>, since the value of <replaceable>n</replaceable>
  152. peeks under 0 levels of stack frames; in other words, 0 is the current level. If
  153. <replaceable>n</replaceable> exceeds the available number of stack frames, then an empty
  154. string is returned.</para>
  155. </description>
  156. <see-also>
  157. <ref type="application">Gosub</ref>
  158. <ref type="application">GosubIf</ref>
  159. <ref type="application">Return</ref>
  160. </see-also>
  161. </function>
  162. <agi name="gosub" language="en_US">
  163. <synopsis>
  164. Cause the channel to execute the specified dialplan subroutine.
  165. </synopsis>
  166. <syntax>
  167. <parameter name="context" required="true" />
  168. <parameter name="extension" required="true" />
  169. <parameter name="priority" required="true" />
  170. <parameter name="optional-argument" />
  171. </syntax>
  172. <description>
  173. <para>Cause the channel to execute the specified dialplan subroutine,
  174. returning to the dialplan with execution of a Return().</para>
  175. </description>
  176. </agi>
  177. ***/
  178. static const char * const app_gosub = "Gosub";
  179. static const char * const app_gosubif = "GosubIf";
  180. static const char * const app_return = "Return";
  181. static const char * const app_pop = "StackPop";
  182. static void gosub_free(void *data);
  183. static struct ast_datastore_info stack_info = {
  184. .type = "GOSUB",
  185. .destroy = gosub_free,
  186. };
  187. struct gosub_stack_frame {
  188. AST_LIST_ENTRY(gosub_stack_frame) entries;
  189. /* 100 arguments is all that we support anyway, but this will handle up to 255 */
  190. unsigned char arguments;
  191. struct varshead varshead;
  192. int priority;
  193. unsigned int is_agi:1;
  194. char *context;
  195. char extension[0];
  196. };
  197. static int frame_set_var(struct ast_channel *chan, struct gosub_stack_frame *frame, const char *var, const char *value)
  198. {
  199. struct ast_var_t *variables;
  200. int found = 0;
  201. /* Does this variable already exist? */
  202. AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
  203. if (!strcmp(var, ast_var_name(variables))) {
  204. found = 1;
  205. break;
  206. }
  207. }
  208. if (!found) {
  209. variables = ast_var_assign(var, "");
  210. AST_LIST_INSERT_HEAD(&frame->varshead, variables, entries);
  211. pbx_builtin_pushvar_helper(chan, var, value);
  212. } else {
  213. pbx_builtin_setvar_helper(chan, var, value);
  214. }
  215. manager_event(EVENT_FLAG_DIALPLAN, "VarSet",
  216. "Channel: %s\r\n"
  217. "Variable: LOCAL(%s)\r\n"
  218. "Value: %s\r\n"
  219. "Uniqueid: %s\r\n",
  220. chan->name, var, value, chan->uniqueid);
  221. return 0;
  222. }
  223. static void gosub_release_frame(struct ast_channel *chan, struct gosub_stack_frame *frame)
  224. {
  225. struct ast_var_t *vardata;
  226. /* If chan is not defined, then we're calling it as part of gosub_free,
  227. * and the channel variables will be deallocated anyway. Otherwise, we're
  228. * just releasing a single frame, so we need to clean up the arguments for
  229. * that frame, so that we re-expose the variables from the previous frame
  230. * that were hidden by this one.
  231. */
  232. while ((vardata = AST_LIST_REMOVE_HEAD(&frame->varshead, entries))) {
  233. if (chan)
  234. pbx_builtin_setvar_helper(chan, ast_var_name(vardata), NULL);
  235. ast_var_delete(vardata);
  236. }
  237. ast_free(frame);
  238. }
  239. static struct gosub_stack_frame *gosub_allocate_frame(const char *context, const char *extension, int priority, unsigned char arguments)
  240. {
  241. struct gosub_stack_frame *new = NULL;
  242. int len_extension = strlen(extension), len_context = strlen(context);
  243. if ((new = ast_calloc(1, sizeof(*new) + 2 + len_extension + len_context))) {
  244. AST_LIST_HEAD_INIT_NOLOCK(&new->varshead);
  245. strcpy(new->extension, extension);
  246. new->context = new->extension + len_extension + 1;
  247. strcpy(new->context, context);
  248. new->priority = priority;
  249. new->arguments = arguments;
  250. }
  251. return new;
  252. }
  253. static void gosub_free(void *data)
  254. {
  255. AST_LIST_HEAD(, gosub_stack_frame) *oldlist = data;
  256. struct gosub_stack_frame *oldframe;
  257. AST_LIST_LOCK(oldlist);
  258. while ((oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries))) {
  259. gosub_release_frame(NULL, oldframe);
  260. }
  261. AST_LIST_UNLOCK(oldlist);
  262. AST_LIST_HEAD_DESTROY(oldlist);
  263. ast_free(oldlist);
  264. }
  265. static int pop_exec(struct ast_channel *chan, const char *data)
  266. {
  267. struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
  268. struct gosub_stack_frame *oldframe;
  269. AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
  270. if (!stack_store) {
  271. ast_log(LOG_WARNING, "%s called with no gosub stack allocated.\n", app_pop);
  272. return 0;
  273. }
  274. oldlist = stack_store->data;
  275. AST_LIST_LOCK(oldlist);
  276. oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
  277. AST_LIST_UNLOCK(oldlist);
  278. if (oldframe) {
  279. gosub_release_frame(chan, oldframe);
  280. } else {
  281. ast_debug(1, "%s called with an empty gosub stack\n", app_pop);
  282. }
  283. return 0;
  284. }
  285. static int return_exec(struct ast_channel *chan, const char *data)
  286. {
  287. struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
  288. struct gosub_stack_frame *oldframe;
  289. AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
  290. const char *retval = data;
  291. int res = 0;
  292. if (!stack_store) {
  293. ast_log(LOG_ERROR, "Return without Gosub: stack is unallocated\n");
  294. return -1;
  295. }
  296. oldlist = stack_store->data;
  297. AST_LIST_LOCK(oldlist);
  298. oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
  299. AST_LIST_UNLOCK(oldlist);
  300. if (!oldframe) {
  301. ast_log(LOG_ERROR, "Return without Gosub: stack is empty\n");
  302. return -1;
  303. } else if (oldframe->is_agi) {
  304. /* Exit from AGI */
  305. res = -1;
  306. }
  307. ast_explicit_goto(chan, oldframe->context, oldframe->extension, oldframe->priority);
  308. gosub_release_frame(chan, oldframe);
  309. /* Set a return value, if any */
  310. pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", S_OR(retval, ""));
  311. return res;
  312. }
  313. static int gosub_exec(struct ast_channel *chan, const char *data)
  314. {
  315. struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
  316. AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
  317. struct gosub_stack_frame *newframe, *lastframe;
  318. char argname[15], *tmp = ast_strdupa(data), *label, *endparen;
  319. int i, max_argc = 0;
  320. AST_DECLARE_APP_ARGS(args2,
  321. AST_APP_ARG(argval)[100];
  322. );
  323. if (ast_strlen_zero(data)) {
  324. ast_log(LOG_ERROR, "%s requires an argument: %s([[context,]exten,]priority[(arg1[,...][,argN])])\n", app_gosub, app_gosub);
  325. return -1;
  326. }
  327. if (!stack_store) {
  328. ast_debug(1, "Channel %s has no datastore, so we're allocating one.\n", chan->name);
  329. stack_store = ast_datastore_alloc(&stack_info, NULL);
  330. if (!stack_store) {
  331. ast_log(LOG_ERROR, "Unable to allocate new datastore. Gosub will fail.\n");
  332. return -1;
  333. }
  334. oldlist = ast_calloc(1, sizeof(*oldlist));
  335. if (!oldlist) {
  336. ast_log(LOG_ERROR, "Unable to allocate datastore list head. Gosub will fail.\n");
  337. ast_datastore_free(stack_store);
  338. return -1;
  339. }
  340. stack_store->data = oldlist;
  341. AST_LIST_HEAD_INIT(oldlist);
  342. ast_channel_datastore_add(chan, stack_store);
  343. } else {
  344. oldlist = stack_store->data;
  345. }
  346. if ((lastframe = AST_LIST_FIRST(oldlist))) {
  347. max_argc = lastframe->arguments;
  348. }
  349. /* Separate the arguments from the label */
  350. /* NOTE: you cannot use ast_app_separate_args for this, because '(' cannot be used as a delimiter. */
  351. label = strsep(&tmp, "(");
  352. if (tmp) {
  353. endparen = strrchr(tmp, ')');
  354. if (endparen)
  355. *endparen = '\0';
  356. else
  357. ast_log(LOG_WARNING, "Ouch. No closing paren: '%s'?\n", (char *)data);
  358. AST_STANDARD_RAW_ARGS(args2, tmp);
  359. } else
  360. args2.argc = 0;
  361. /* Mask out previous arguments in this invocation */
  362. if (args2.argc > max_argc) {
  363. max_argc = args2.argc;
  364. }
  365. /* Create the return address, but don't save it until we know that the Gosub destination exists */
  366. newframe = gosub_allocate_frame(chan->context, chan->exten, chan->priority + 1, max_argc);
  367. if (!newframe) {
  368. return -1;
  369. }
  370. if (ast_parseable_goto(chan, label)) {
  371. ast_log(LOG_ERROR, "Gosub address is invalid: '%s'\n", (char *)data);
  372. ast_free(newframe);
  373. return -1;
  374. }
  375. if (!ast_exists_extension(chan, chan->context, chan->exten,
  376. ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP) ? chan->priority + 1 : chan->priority,
  377. S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
  378. ast_log(LOG_ERROR, "Attempt to reach a non-existent destination for gosub: (Context:%s, Extension:%s, Priority:%d)\n",
  379. chan->context, chan->exten, ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP) ? chan->priority + 1 : chan->priority);
  380. ast_copy_string(chan->context, newframe->context, sizeof(chan->context));
  381. ast_copy_string(chan->exten, newframe->extension, sizeof(chan->exten));
  382. chan->priority = newframe->priority;
  383. ast_free(newframe);
  384. return -1;
  385. }
  386. /* Now that we know for certain that we're going to a new location, set our arguments */
  387. for (i = 0; i < max_argc; i++) {
  388. snprintf(argname, sizeof(argname), "ARG%d", i + 1);
  389. frame_set_var(chan, newframe, argname, i < args2.argc ? args2.argval[i] : "");
  390. ast_debug(1, "Setting '%s' to '%s'\n", argname, i < args2.argc ? args2.argval[i] : "");
  391. }
  392. snprintf(argname, sizeof(argname), "%d", args2.argc);
  393. frame_set_var(chan, newframe, "ARGC", argname);
  394. /* And finally, save our return address */
  395. oldlist = stack_store->data;
  396. AST_LIST_LOCK(oldlist);
  397. AST_LIST_INSERT_HEAD(oldlist, newframe, entries);
  398. AST_LIST_UNLOCK(oldlist);
  399. return 0;
  400. }
  401. static int gosubif_exec(struct ast_channel *chan, const char *data)
  402. {
  403. char *args;
  404. int res=0;
  405. AST_DECLARE_APP_ARGS(cond,
  406. AST_APP_ARG(ition);
  407. AST_APP_ARG(labels);
  408. );
  409. AST_DECLARE_APP_ARGS(label,
  410. AST_APP_ARG(iftrue);
  411. AST_APP_ARG(iffalse);
  412. );
  413. if (ast_strlen_zero(data)) {
  414. ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
  415. return 0;
  416. }
  417. args = ast_strdupa(data);
  418. AST_NONSTANDARD_RAW_ARGS(cond, args, '?');
  419. if (cond.argc != 2) {
  420. ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
  421. return 0;
  422. }
  423. AST_NONSTANDARD_RAW_ARGS(label, cond.labels, ':');
  424. if (pbx_checkcondition(cond.ition)) {
  425. if (!ast_strlen_zero(label.iftrue))
  426. res = gosub_exec(chan, label.iftrue);
  427. } else if (!ast_strlen_zero(label.iffalse)) {
  428. res = gosub_exec(chan, label.iffalse);
  429. }
  430. return res;
  431. }
  432. static int local_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
  433. {
  434. struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
  435. AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
  436. struct gosub_stack_frame *frame;
  437. struct ast_var_t *variables;
  438. if (!stack_store)
  439. return -1;
  440. oldlist = stack_store->data;
  441. AST_LIST_LOCK(oldlist);
  442. if (!(frame = AST_LIST_FIRST(oldlist))) {
  443. /* Not within a Gosub routine */
  444. AST_LIST_UNLOCK(oldlist);
  445. return -1;
  446. }
  447. AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
  448. if (!strcmp(data, ast_var_name(variables))) {
  449. const char *tmp;
  450. ast_channel_lock(chan);
  451. tmp = pbx_builtin_getvar_helper(chan, data);
  452. ast_copy_string(buf, S_OR(tmp, ""), len);
  453. ast_channel_unlock(chan);
  454. break;
  455. }
  456. }
  457. AST_LIST_UNLOCK(oldlist);
  458. return 0;
  459. }
  460. static int local_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
  461. {
  462. struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
  463. AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
  464. struct gosub_stack_frame *frame;
  465. if (!stack_store) {
  466. ast_log(LOG_ERROR, "Tried to set LOCAL(%s), but we aren't within a Gosub routine\n", var);
  467. return -1;
  468. }
  469. oldlist = stack_store->data;
  470. AST_LIST_LOCK(oldlist);
  471. frame = AST_LIST_FIRST(oldlist);
  472. if (frame)
  473. frame_set_var(chan, frame, var, value);
  474. AST_LIST_UNLOCK(oldlist);
  475. return 0;
  476. }
  477. static struct ast_custom_function local_function = {
  478. .name = "LOCAL",
  479. .write = local_write,
  480. .read = local_read,
  481. };
  482. static int peek_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
  483. {
  484. int found = 0, n;
  485. struct ast_var_t *variables;
  486. AST_DECLARE_APP_ARGS(args,
  487. AST_APP_ARG(n);
  488. AST_APP_ARG(name);
  489. );
  490. if (!chan) {
  491. ast_log(LOG_ERROR, "LOCAL_PEEK must be called on an active channel\n");
  492. return -1;
  493. }
  494. AST_STANDARD_RAW_ARGS(args, data);
  495. n = atoi(args.n);
  496. *buf = '\0';
  497. ast_channel_lock(chan);
  498. AST_LIST_TRAVERSE(&chan->varshead, variables, entries) {
  499. if (!strcmp(args.name, ast_var_name(variables)) && ++found > n) {
  500. ast_copy_string(buf, ast_var_value(variables), len);
  501. break;
  502. }
  503. }
  504. ast_channel_unlock(chan);
  505. return 0;
  506. }
  507. static struct ast_custom_function peek_function = {
  508. .name = "LOCAL_PEEK",
  509. .read = peek_read,
  510. };
  511. static int handle_gosub(struct ast_channel *chan, AGI *agi, int argc, const char * const *argv)
  512. {
  513. int old_priority, priority;
  514. char old_context[AST_MAX_CONTEXT], old_extension[AST_MAX_EXTENSION];
  515. struct ast_app *theapp;
  516. char *gosub_args;
  517. if (argc < 4 || argc > 5) {
  518. return RESULT_SHOWUSAGE;
  519. }
  520. 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] : "");
  521. if (sscanf(argv[3], "%30d", &priority) != 1 || priority < 1) {
  522. /* Lookup the priority label */
  523. priority = ast_findlabel_extension(chan, argv[1], argv[2], argv[3],
  524. S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL));
  525. if (priority < 0) {
  526. ast_log(LOG_ERROR, "Priority '%s' not found in '%s@%s'\n", argv[3], argv[2], argv[1]);
  527. ast_agi_send(agi->fd, chan, "200 result=-1 Gosub label not found\n");
  528. return RESULT_FAILURE;
  529. }
  530. } else if (!ast_exists_extension(chan, argv[1], argv[2], priority,
  531. S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
  532. ast_agi_send(agi->fd, chan, "200 result=-1 Gosub label not found\n");
  533. return RESULT_FAILURE;
  534. }
  535. /* Save previous location, since we're going to change it */
  536. ast_copy_string(old_context, chan->context, sizeof(old_context));
  537. ast_copy_string(old_extension, chan->exten, sizeof(old_extension));
  538. old_priority = chan->priority;
  539. if (!(theapp = pbx_findapp("Gosub"))) {
  540. ast_log(LOG_ERROR, "Gosub() cannot be found in the list of loaded applications\n");
  541. ast_agi_send(agi->fd, chan, "503 result=-2 Gosub is not loaded\n");
  542. return RESULT_FAILURE;
  543. }
  544. /* Apparently, if you run ast_pbx_run on a channel that already has a pbx
  545. * structure, you need to add 1 to the priority to get it to go to the
  546. * right place. But if it doesn't have a pbx structure, then leaving off
  547. * the 1 is the right thing to do. See how this code differs when we
  548. * call a Gosub for the CALLEE channel in Dial or Queue.
  549. */
  550. if (argc == 5) {
  551. if (asprintf(&gosub_args, "%s,%s,%d(%s)", argv[1], argv[2], priority + (chan->pbx ? 1 : 0), argv[4]) < 0) {
  552. ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
  553. gosub_args = NULL;
  554. }
  555. } else {
  556. if (asprintf(&gosub_args, "%s,%s,%d", argv[1], argv[2], priority + (chan->pbx ? 1 : 0)) < 0) {
  557. ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
  558. gosub_args = NULL;
  559. }
  560. }
  561. if (gosub_args) {
  562. int res;
  563. ast_debug(1, "Trying gosub with arguments '%s'\n", gosub_args);
  564. if ((res = pbx_exec(chan, theapp, gosub_args)) == 0) {
  565. struct ast_pbx *pbx = chan->pbx;
  566. struct ast_pbx_args args;
  567. struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
  568. AST_LIST_HEAD(, gosub_stack_frame) *oldlist = stack_store->data;
  569. struct gosub_stack_frame *cur = AST_LIST_FIRST(oldlist);
  570. cur->is_agi = 1;
  571. memset(&args, 0, sizeof(args));
  572. args.no_hangup_chan = 1;
  573. /* Suppress warning about PBX already existing */
  574. chan->pbx = NULL;
  575. ast_agi_send(agi->fd, chan, "100 result=0 Trying...\n");
  576. ast_pbx_run_args(chan, &args);
  577. ast_agi_send(agi->fd, chan, "200 result=0 Gosub complete\n");
  578. if (chan->pbx) {
  579. ast_free(chan->pbx);
  580. }
  581. chan->pbx = pbx;
  582. } else {
  583. ast_agi_send(agi->fd, chan, "200 result=%d Gosub failed\n", res);
  584. }
  585. ast_free(gosub_args);
  586. } else {
  587. ast_agi_send(agi->fd, chan, "503 result=-2 Memory allocation failure\n");
  588. return RESULT_FAILURE;
  589. }
  590. /* Restore previous location */
  591. ast_copy_string(chan->context, old_context, sizeof(chan->context));
  592. ast_copy_string(chan->exten, old_extension, sizeof(chan->exten));
  593. chan->priority = old_priority;
  594. return RESULT_SUCCESS;
  595. }
  596. static struct agi_command gosub_agi_command =
  597. { { "gosub", NULL }, handle_gosub, NULL, NULL, 0 };
  598. static int unload_module(void)
  599. {
  600. ast_agi_unregister(ast_module_info->self, &gosub_agi_command);
  601. ast_unregister_application(app_return);
  602. ast_unregister_application(app_pop);
  603. ast_unregister_application(app_gosubif);
  604. ast_unregister_application(app_gosub);
  605. ast_custom_function_unregister(&local_function);
  606. ast_custom_function_unregister(&peek_function);
  607. return 0;
  608. }
  609. static int load_module(void)
  610. {
  611. ast_agi_register(ast_module_info->self, &gosub_agi_command);
  612. ast_register_application_xml(app_pop, pop_exec);
  613. ast_register_application_xml(app_return, return_exec);
  614. ast_register_application_xml(app_gosubif, gosubif_exec);
  615. ast_register_application_xml(app_gosub, gosub_exec);
  616. ast_custom_function_register(&local_function);
  617. ast_custom_function_register(&peek_function);
  618. return 0;
  619. }
  620. AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Dialplan subroutines (Gosub, Return, etc)",
  621. .load = load_module,
  622. .unload = unload_module,
  623. .nonoptreq = "res_agi",
  624. );