8f7ab0e7843eea85b819cf81bd85be6046e022e4
[asterisk/asterisk.git] / apps / app_stack.c
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
19 /*! \file
20  *
21  * \brief Stack applications Gosub, Return, etc.
22  *
23  * \author Tilghman Lesher <app_stack_v003@the-tilghman.com>
24  * 
25  * \ingroup applications
26  */
27
28 /*** MODULEINFO
29         <use type="module">res_agi</use>
30         <support_level>core</support_level>
31  ***/
32
33 #include "asterisk.h"
34  
35 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
36
37 #include "asterisk/pbx.h"
38 #include "asterisk/module.h"
39 #include "asterisk/app.h"
40 #include "asterisk/manager.h"
41 #include "asterisk/channel.h"
42 #include "asterisk/agi.h"
43
44 /*** DOCUMENTATION
45         <application name="Gosub" language="en_US">
46                 <synopsis>
47                         Jump to label, saving return address.
48                 </synopsis>
49                 <syntax>
50                         <parameter name="context" />
51                         <parameter name="exten" />
52                         <parameter name="priority" required="true" hasparams="optional">
53                                 <argument name="arg1" multiple="true" required="true" />
54                                 <argument name="argN" />
55                         </parameter>
56                 </syntax>
57                 <description>
58                         <para>Jumps to the label specified, saving the return address.</para>
59                 </description>
60                 <see-also>
61                         <ref type="application">GosubIf</ref>
62                         <ref type="application">Macro</ref>
63                         <ref type="application">Goto</ref>
64                         <ref type="application">Return</ref>
65                         <ref type="application">StackPop</ref>
66                 </see-also>
67         </application>
68         <application name="GosubIf" language="en_US">
69                 <synopsis>
70                         Conditionally jump to label, saving return address.
71                 </synopsis>
72                 <syntax argsep="?">
73                         <parameter name="condition" required="true" />
74                         <parameter name="destination" required="true" argsep=":">
75                                 <argument name="labeliftrue" hasparams="optional">
76                                         <argument name="arg1" required="true" multiple="true" />
77                                         <argument name="argN" />
78                                 </argument>
79                                 <argument name="labeliffalse" hasparams="optional">
80                                         <argument name="arg1" required="true" multiple="true" />
81                                         <argument name="argN" />
82                                 </argument>
83                         </parameter>
84                 </syntax>
85                 <description>
86                         <para>If the condition is true, then jump to labeliftrue.  If false, jumps to
87                         labeliffalse, if specified.  In either case, a jump saves the return point
88                         in the dialplan, to be returned to with a Return.</para>
89                 </description>
90                 <see-also>
91                         <ref type="application">Gosub</ref>
92                         <ref type="application">Return</ref>
93                         <ref type="application">MacroIf</ref>
94                         <ref type="function">IF</ref>
95                         <ref type="application">GotoIf</ref>
96                 </see-also>
97         </application>
98         <application name="Return" language="en_US">
99                 <synopsis>
100                         Return from gosub routine.
101                 </synopsis>
102                 <syntax>
103                         <parameter name="value">
104                                 <para>Return value.</para>
105                         </parameter>
106                 </syntax>
107                 <description>
108                         <para>Jumps to the last label on the stack, removing it. The return <replaceable>value</replaceable>, if
109                         any, is saved in the channel variable <variable>GOSUB_RETVAL</variable>.</para>
110                 </description>
111                 <see-also>
112                         <ref type="application">Gosub</ref>
113                         <ref type="application">StackPop</ref>
114                 </see-also>
115         </application>
116         <application name="StackPop" language="en_US">
117                 <synopsis>
118                         Remove one address from gosub stack.
119                 </synopsis>
120                 <syntax />
121                 <description>
122                         <para>Removes last label on the stack, discarding it.</para>
123                 </description>
124                 <see-also>
125                         <ref type="application">Return</ref>
126                         <ref type="application">Gosub</ref>
127                 </see-also>
128         </application>
129         <function name="LOCAL" language="en_US">
130                 <synopsis>
131                         Manage variables local to the gosub stack frame.
132                 </synopsis>
133                 <syntax>
134                         <parameter name="varname" required="true" />
135                 </syntax>
136                 <description>
137                         <para>Read and write a variable local to the gosub stack frame, once we Return() it will be lost
138                         (or it will go back to whatever value it had before the Gosub()).</para>
139                 </description>
140                 <see-also>
141                         <ref type="application">Gosub</ref>
142                         <ref type="application">GosubIf</ref>
143                         <ref type="application">Return</ref>
144                 </see-also>
145         </function>
146         <function name="LOCAL_PEEK" language="en_US">
147                 <synopsis>
148                         Retrieve variables hidden by the local gosub stack frame.
149                 </synopsis>
150                 <syntax>
151                         <parameter name="n" required="true" />
152                         <parameter name="varname" required="true" />
153                 </syntax>
154                 <description>
155                         <para>Read a variable <replaceable>varname</replaceable> hidden by
156                         <replaceable>n</replaceable> levels of gosub stack frames.  Note that ${LOCAL_PEEK(0,foo)}
157                         is the same as <variable>foo</variable>, since the value of <replaceable>n</replaceable>
158                         peeks under 0 levels of stack frames; in other words, 0 is the current level.  If
159                         <replaceable>n</replaceable> exceeds the available number of stack frames, then an empty
160                         string is returned.</para>
161                 </description>
162                 <see-also>
163                         <ref type="application">Gosub</ref>
164                         <ref type="application">GosubIf</ref>
165                         <ref type="application">Return</ref>
166                 </see-also>
167         </function>
168         <function name="STACK_PEEK" language="en_US">
169                 <synopsis>
170                         View info about the location which called Gosub
171                 </synopsis>
172                 <syntax>
173                         <parameter name="n" required="true" />
174                         <parameter name="which" required="true" />
175                         <parameter name="suppress" required="false" />
176                 </syntax>
177                 <description>
178                         <para>Read the calling <literal>c</literal>ontext, <literal>e</literal>xtension,
179                         <literal>p</literal>riority, or <literal>l</literal>abel, as specified by
180                         <replaceable>which</replaceable>, by going up <replaceable>n</replaceable> frames
181                         in the Gosub stack.  If <replaceable>suppress</replaceable> is true, then if the
182                         number of available stack frames is exceeded, then no error message will be
183                         printed.</para>
184                 </description>
185         </function>
186         <agi name="gosub" language="en_US">
187                 <synopsis>
188                         Cause the channel to execute the specified dialplan subroutine.
189                 </synopsis>
190                 <syntax>
191                         <parameter name="context" required="true" />
192                         <parameter name="extension" required="true" />
193                         <parameter name="priority" required="true" />
194                         <parameter name="optional-argument" />
195                 </syntax>
196                 <description>
197                         <para>Cause the channel to execute the specified dialplan subroutine,
198                         returning to the dialplan with execution of a Return().</para>
199                 </description>
200         </agi>
201  ***/
202
203 static const char * const app_gosub = "Gosub";
204 static const char * const app_gosubif = "GosubIf";
205 static const char * const app_return = "Return";
206 static const char * const app_pop = "StackPop";
207
208 static void gosub_free(void *data);
209
210 static struct ast_datastore_info stack_info = {
211         .type = "GOSUB",
212         .destroy = gosub_free,
213 };
214
215 struct gosub_stack_frame {
216         AST_LIST_ENTRY(gosub_stack_frame) entries;
217         /* 100 arguments is all that we support anyway, but this will handle up to 255 */
218         unsigned char arguments;
219         struct varshead varshead;
220         int priority;
221         unsigned int is_agi:1;
222         char *context;
223         char extension[0];
224 };
225
226 static int frame_set_var(struct ast_channel *chan, struct gosub_stack_frame *frame, const char *var, const char *value)
227 {
228         struct ast_var_t *variables;
229         int found = 0;
230
231         /* Does this variable already exist? */
232         AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
233                 if (!strcmp(var, ast_var_name(variables))) {
234                         found = 1;
235                         break;
236                 }
237         }
238
239         if (!found) {
240                 variables = ast_var_assign(var, "");
241                 AST_LIST_INSERT_HEAD(&frame->varshead, variables, entries);
242                 pbx_builtin_pushvar_helper(chan, var, value);
243         } else {
244                 pbx_builtin_setvar_helper(chan, var, value);
245         }
246
247         manager_event(EVENT_FLAG_DIALPLAN, "VarSet",
248                 "Channel: %s\r\n"
249                 "Variable: LOCAL(%s)\r\n"
250                 "Value: %s\r\n"
251                 "Uniqueid: %s\r\n",
252                 ast_channel_name(chan), var, value, ast_channel_uniqueid(chan));
253         return 0;
254 }
255
256 static void gosub_release_frame(struct ast_channel *chan, struct gosub_stack_frame *frame)
257 {
258         struct ast_var_t *vardata;
259
260         /* If chan is not defined, then we're calling it as part of gosub_free,
261          * and the channel variables will be deallocated anyway.  Otherwise, we're
262          * just releasing a single frame, so we need to clean up the arguments for
263          * that frame, so that we re-expose the variables from the previous frame
264          * that were hidden by this one.
265          */
266         while ((vardata = AST_LIST_REMOVE_HEAD(&frame->varshead, entries))) {
267                 if (chan)
268                         pbx_builtin_setvar_helper(chan, ast_var_name(vardata), NULL);   
269                 ast_var_delete(vardata);
270         }
271
272         ast_free(frame);
273 }
274
275 static struct gosub_stack_frame *gosub_allocate_frame(const char *context, const char *extension, int priority, unsigned char arguments)
276 {
277         struct gosub_stack_frame *new = NULL;
278         int len_extension = strlen(extension), len_context = strlen(context);
279
280         if ((new = ast_calloc(1, sizeof(*new) + 2 + len_extension + len_context))) {
281                 AST_LIST_HEAD_INIT_NOLOCK(&new->varshead);
282                 strcpy(new->extension, extension);
283                 new->context = new->extension + len_extension + 1;
284                 strcpy(new->context, context);
285                 new->priority = priority;
286                 new->arguments = arguments;
287         }
288         return new;
289 }
290
291 static void gosub_free(void *data)
292 {
293         AST_LIST_HEAD(, gosub_stack_frame) *oldlist = data;
294         struct gosub_stack_frame *oldframe;
295         AST_LIST_LOCK(oldlist);
296         while ((oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries))) {
297                 gosub_release_frame(NULL, oldframe);
298         }
299         AST_LIST_UNLOCK(oldlist);
300         AST_LIST_HEAD_DESTROY(oldlist);
301         ast_free(oldlist);
302 }
303
304 static int pop_exec(struct ast_channel *chan, const char *data)
305 {
306         struct ast_datastore *stack_store;
307         struct gosub_stack_frame *oldframe;
308         AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
309
310         ast_channel_lock(chan);
311         if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
312                 ast_log(LOG_WARNING, "%s called with no gosub stack allocated.\n", app_pop);
313                 ast_channel_unlock(chan);
314                 return 0;
315         }
316
317         oldlist = stack_store->data;
318         AST_LIST_LOCK(oldlist);
319         oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
320         AST_LIST_UNLOCK(oldlist);
321
322         if (oldframe) {
323                 gosub_release_frame(chan, oldframe);
324         } else {
325                 ast_debug(1, "%s called with an empty gosub stack\n", app_pop);
326         }
327         ast_channel_unlock(chan);
328         return 0;
329 }
330
331 static int return_exec(struct ast_channel *chan, const char *data)
332 {
333         struct ast_datastore *stack_store;
334         struct gosub_stack_frame *oldframe;
335         AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
336         const char *retval = data;
337         int res = 0;
338
339         ast_channel_lock(chan);
340         if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
341                 ast_log(LOG_ERROR, "Return without Gosub: stack is unallocated\n");
342                 ast_channel_unlock(chan);
343                 return -1;
344         }
345
346         oldlist = stack_store->data;
347         AST_LIST_LOCK(oldlist);
348         oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
349         AST_LIST_UNLOCK(oldlist);
350
351         if (!oldframe) {
352                 ast_log(LOG_ERROR, "Return without Gosub: stack is empty\n");
353                 ast_channel_unlock(chan);
354                 return -1;
355         } else if (oldframe->is_agi) {
356                 /* Exit from AGI */
357                 res = -1;
358         }
359
360         ast_explicit_goto(chan, oldframe->context, oldframe->extension, oldframe->priority);
361         gosub_release_frame(chan, oldframe);
362
363         /* Set a return value, if any */
364         pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", S_OR(retval, ""));
365         ast_channel_unlock(chan);
366         return res;
367 }
368
369 static int gosub_exec(struct ast_channel *chan, const char *data)
370 {
371         struct ast_datastore *stack_store;
372         AST_LIST_HEAD(,gosub_stack_frame) *oldlist;
373         struct gosub_stack_frame *newframe, *lastframe;
374         char argname[15], *tmp = ast_strdupa(data), *label, *endparen;
375         int i, max_argc = 0;
376         AST_DECLARE_APP_ARGS(args2,
377                 AST_APP_ARG(argval)[100];
378         );
379
380         if (ast_strlen_zero(data)) {
381                 ast_log(LOG_ERROR, "%s requires an argument: %s([[context,]exten,]priority[(arg1[,...][,argN])])\n", app_gosub, app_gosub);
382                 return -1;
383         }
384
385         ast_channel_lock(chan);
386         if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
387                 ast_debug(1, "Channel %s has no datastore, so we're allocating one.\n", ast_channel_name(chan));
388                 stack_store = ast_datastore_alloc(&stack_info, NULL);
389                 if (!stack_store) {
390                         ast_log(LOG_ERROR, "Unable to allocate new datastore.  Gosub will fail.\n");
391                         ast_channel_unlock(chan);
392                         return -1;
393                 }
394
395                 oldlist = ast_calloc(1, sizeof(*oldlist));
396                 if (!oldlist) {
397                         ast_log(LOG_ERROR, "Unable to allocate datastore list head.  Gosub will fail.\n");
398                         ast_datastore_free(stack_store);
399                         ast_channel_unlock(chan);
400                         return -1;
401                 }
402
403                 stack_store->data = oldlist;
404                 AST_LIST_HEAD_INIT(oldlist);
405                 ast_channel_datastore_add(chan, stack_store);
406         } else {
407                 oldlist = stack_store->data;
408         }
409
410         if ((lastframe = AST_LIST_FIRST(oldlist))) {
411                 max_argc = lastframe->arguments;
412         }
413
414         /* Separate the arguments from the label */
415         /* NOTE:  you cannot use ast_app_separate_args for this, because '(' cannot be used as a delimiter. */
416         label = strsep(&tmp, "(");
417         if (tmp) {
418                 endparen = strrchr(tmp, ')');
419                 if (endparen)
420                         *endparen = '\0';
421                 else
422                         ast_log(LOG_WARNING, "Ouch.  No closing paren: '%s'?\n", (char *)data);
423                 AST_STANDARD_RAW_ARGS(args2, tmp);
424         } else
425                 args2.argc = 0;
426
427         /* Mask out previous arguments in this invocation */
428         if (args2.argc > max_argc) {
429                 max_argc = args2.argc;
430         }
431
432         /* Create the return address, but don't save it until we know that the Gosub destination exists */
433         newframe = gosub_allocate_frame(ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan) + 1, max_argc);
434
435         if (!newframe) {
436                 ast_channel_unlock(chan);
437                 return -1;
438         }
439
440         if (ast_parseable_goto(chan, label)) {
441                 ast_log(LOG_ERROR, "Gosub address is invalid: '%s'\n", (char *)data);
442                 ast_free(newframe);
443                 ast_channel_unlock(chan);
444                 return -1;
445         }
446
447         if (!ast_exists_extension(chan, ast_channel_context(chan), ast_channel_exten(chan),
448                 ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP) ? ast_channel_priority(chan) + 1 : ast_channel_priority(chan),
449                 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
450                 ast_log(LOG_ERROR, "Attempt to reach a non-existent destination for gosub: (Context:%s, Extension:%s, Priority:%d)\n",
451                                 ast_channel_context(chan), ast_channel_exten(chan), ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP) ? ast_channel_priority(chan) + 1 : ast_channel_priority(chan));
452                 ast_channel_context_set(chan, newframe->context);
453                 ast_channel_exten_set(chan, newframe->extension);
454                 ast_channel_priority_set(chan, newframe->priority - 1);
455                 ast_free(newframe);
456                 ast_channel_unlock(chan);
457                 return -1;
458         }
459
460         /* Now that we know for certain that we're going to a new location, set our arguments */
461         for (i = 0; i < max_argc; i++) {
462                 snprintf(argname, sizeof(argname), "ARG%d", i + 1);
463                 frame_set_var(chan, newframe, argname, i < args2.argc ? args2.argval[i] : "");
464                 ast_debug(1, "Setting '%s' to '%s'\n", argname, i < args2.argc ? args2.argval[i] : "");
465         }
466         snprintf(argname, sizeof(argname), "%d", args2.argc);
467         frame_set_var(chan, newframe, "ARGC", argname);
468
469         /* And finally, save our return address */
470         oldlist = stack_store->data;
471         AST_LIST_LOCK(oldlist);
472         AST_LIST_INSERT_HEAD(oldlist, newframe, entries);
473         AST_LIST_UNLOCK(oldlist);
474         ast_channel_unlock(chan);
475
476         return 0;
477 }
478
479 static int gosubif_exec(struct ast_channel *chan, const char *data)
480 {
481         char *args;
482         int res=0;
483         AST_DECLARE_APP_ARGS(cond,
484                 AST_APP_ARG(ition);
485                 AST_APP_ARG(labels);
486         );
487         AST_DECLARE_APP_ARGS(label,
488                 AST_APP_ARG(iftrue);
489                 AST_APP_ARG(iffalse);
490         );
491
492         if (ast_strlen_zero(data)) {
493                 ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
494                 return 0;
495         }
496
497         args = ast_strdupa(data);
498         AST_NONSTANDARD_RAW_ARGS(cond, args, '?');
499         if (cond.argc != 2) {
500                 ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
501                 return 0;
502         }
503
504         AST_NONSTANDARD_RAW_ARGS(label, cond.labels, ':');
505
506         if (pbx_checkcondition(cond.ition)) {
507                 if (!ast_strlen_zero(label.iftrue))
508                         res = gosub_exec(chan, label.iftrue);
509         } else if (!ast_strlen_zero(label.iffalse)) {
510                 res = gosub_exec(chan, label.iffalse);
511         }
512
513         return res;
514 }
515
516 static int local_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
517 {
518         struct ast_datastore *stack_store;
519         AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
520         struct gosub_stack_frame *frame;
521         struct ast_var_t *variables;
522
523         ast_channel_lock(chan);
524         if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
525                 ast_channel_unlock(chan);
526                 return -1;
527         }
528
529         oldlist = stack_store->data;
530         AST_LIST_LOCK(oldlist);
531         if (!(frame = AST_LIST_FIRST(oldlist))) {
532                 /* Not within a Gosub routine */
533                 AST_LIST_UNLOCK(oldlist);
534                 ast_channel_unlock(chan);
535                 return -1;
536         }
537
538         AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
539                 if (!strcmp(data, ast_var_name(variables))) {
540                         const char *tmp;
541                         tmp = pbx_builtin_getvar_helper(chan, data);
542                         ast_copy_string(buf, S_OR(tmp, ""), len);
543                         break;
544                 }
545         }
546         AST_LIST_UNLOCK(oldlist);
547         ast_channel_unlock(chan);
548         return 0;
549 }
550
551 static int local_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
552 {
553         struct ast_datastore *stack_store;
554         AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
555         struct gosub_stack_frame *frame;
556
557         ast_channel_lock(chan);
558         if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
559                 ast_log(LOG_ERROR, "Tried to set LOCAL(%s), but we aren't within a Gosub routine\n", var);
560                 ast_channel_unlock(chan);
561                 return -1;
562         }
563
564         oldlist = stack_store->data;
565         AST_LIST_LOCK(oldlist);
566         frame = AST_LIST_FIRST(oldlist);
567
568         if (frame) {
569                 frame_set_var(chan, frame, var, value);
570         }
571
572         AST_LIST_UNLOCK(oldlist);
573         ast_channel_unlock(chan);
574
575         return 0;
576 }
577
578 static struct ast_custom_function local_function = {
579         .name = "LOCAL",
580         .write = local_write,
581         .read = local_read,
582 };
583
584 static int peek_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
585 {
586         int found = 0, n;
587         struct ast_var_t *variables;
588         AST_DECLARE_APP_ARGS(args,
589                 AST_APP_ARG(n);
590                 AST_APP_ARG(name);
591         );
592
593         if (!chan) {
594                 ast_log(LOG_ERROR, "LOCAL_PEEK must be called on an active channel\n");
595                 return -1;
596         }
597
598         AST_STANDARD_RAW_ARGS(args, data);
599         n = atoi(args.n);
600         *buf = '\0';
601
602         ast_channel_lock(chan);
603         AST_LIST_TRAVERSE(ast_channel_varshead(chan), variables, entries) {
604                 if (!strcmp(args.name, ast_var_name(variables)) && ++found > n) {
605                         ast_copy_string(buf, ast_var_value(variables), len);
606                         break;
607                 }
608         }
609         ast_channel_unlock(chan);
610         return 0;
611 }
612
613 static struct ast_custom_function peek_function = {
614         .name = "LOCAL_PEEK",
615         .read = peek_read,
616 };
617
618 static int stackpeek_read(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **str, ssize_t len)
619 {
620         struct ast_datastore *stack_store;
621         AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
622         struct gosub_stack_frame *frame;
623         int n;
624         AST_DECLARE_APP_ARGS(args,
625                 AST_APP_ARG(n);
626                 AST_APP_ARG(which);
627                 AST_APP_ARG(suppress);
628         );
629
630         if (!chan) {
631                 ast_log(LOG_ERROR, "STACK_PEEK must be called on an active channel\n");
632                 return -1;
633         }
634
635         data = ast_strdupa(data);
636         AST_STANDARD_APP_ARGS(args, data);
637
638         n = atoi(args.n);
639         if (n <= 0) {
640                 ast_log(LOG_ERROR, "STACK_PEEK must be called with a positive peek value\n");
641                 return -1;
642         }
643
644         ast_channel_lock(chan);
645         if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
646                 if (!ast_true(args.suppress)) {
647                         ast_log(LOG_ERROR, "STACK_PEEK called on a channel without a gosub stack\n");
648                 }
649                 ast_channel_unlock(chan);
650                 return -1;
651         }
652
653         oldlist = stack_store->data;
654
655         AST_LIST_LOCK(oldlist);
656         AST_LIST_TRAVERSE(oldlist, frame, entries) {
657                 if (--n == 0) {
658                         break;
659                 }
660         }
661
662         if (!frame) {
663                 /* Too deep */
664                 if (!ast_true(args.suppress)) {
665                         ast_log(LOG_ERROR, "Stack peek of '%s' is more stack frames than I have\n", args.n);
666                 }
667                 ast_channel_unlock(chan);
668                 return -1;
669         }
670
671         args.which = ast_skip_blanks(args.which);
672
673         switch (args.which[0]) {
674         case 'l': /* label */
675                 ast_str_set(str, len, "%s,%s,%d", frame->context, frame->extension, frame->priority - 1);
676                 break;
677         case 'c': /* context */
678                 ast_str_set(str, len, "%s", frame->context);
679                 break;
680         case 'e': /* extension */
681                 ast_str_set(str, len, "%s", frame->extension);
682                 break;
683         case 'p': /* priority */
684                 ast_str_set(str, len, "%d", frame->priority - 1);
685                 break;
686         default:
687                 ast_log(LOG_ERROR, "Unknown argument '%s' to STACK_PEEK\n", args.which);
688         }
689
690         AST_LIST_UNLOCK(oldlist);
691         ast_channel_unlock(chan);
692
693         return 0;
694 }
695
696 static struct ast_custom_function stackpeek_function = {
697         .name = "STACK_PEEK",
698         .read2 = stackpeek_read,
699 };
700
701 static int handle_gosub(struct ast_channel *chan, AGI *agi, int argc, const char * const *argv)
702 {
703         int old_priority, priority;
704         char old_context[AST_MAX_CONTEXT], old_extension[AST_MAX_EXTENSION];
705         struct ast_app *theapp;
706         char *gosub_args;
707
708         if (argc < 4 || argc > 5) {
709                 return RESULT_SHOWUSAGE;
710         }
711
712         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] : "");
713
714         if (sscanf(argv[3], "%30d", &priority) != 1 || priority < 1) {
715                 /* Lookup the priority label */
716                 priority = ast_findlabel_extension(chan, argv[1], argv[2], argv[3],
717                         S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL));
718                 if (priority < 0) {
719                         ast_log(LOG_ERROR, "Priority '%s' not found in '%s@%s'\n", argv[3], argv[2], argv[1]);
720                         ast_agi_send(agi->fd, chan, "200 result=-1 Gosub label not found\n");
721                         return RESULT_FAILURE;
722                 }
723         } else if (!ast_exists_extension(chan, argv[1], argv[2], priority,
724                 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
725                 ast_agi_send(agi->fd, chan, "200 result=-1 Gosub label not found\n");
726                 return RESULT_FAILURE;
727         }
728
729         /* Save previous location, since we're going to change it */
730         ast_copy_string(old_context, ast_channel_context(chan), sizeof(old_context));
731         ast_copy_string(old_extension, ast_channel_exten(chan), sizeof(old_extension));
732         old_priority = ast_channel_priority(chan);
733
734         if (!(theapp = pbx_findapp("Gosub"))) {
735                 ast_log(LOG_ERROR, "Gosub() cannot be found in the list of loaded applications\n");
736                 ast_agi_send(agi->fd, chan, "503 result=-2 Gosub is not loaded\n");
737                 return RESULT_FAILURE;
738         }
739
740         /* Apparently, if you run ast_pbx_run on a channel that already has a pbx
741          * structure, you need to add 1 to the priority to get it to go to the
742          * right place.  But if it doesn't have a pbx structure, then leaving off
743          * the 1 is the right thing to do.  See how this code differs when we
744          * call a Gosub for the CALLEE channel in Dial or Queue.
745          */
746         if (argc == 5) {
747                 if (asprintf(&gosub_args, "%s,%s,%d(%s)", argv[1], argv[2], priority + (ast_channel_pbx(chan) ? 1 : 0), argv[4]) < 0) {
748                         ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
749                         gosub_args = NULL;
750                 }
751         } else {
752                 if (asprintf(&gosub_args, "%s,%s,%d", argv[1], argv[2], priority + (ast_channel_pbx(chan) ? 1 : 0)) < 0) {
753                         ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
754                         gosub_args = NULL;
755                 }
756         }
757
758         if (gosub_args) {
759                 int res;
760
761                 ast_debug(1, "Trying gosub with arguments '%s'\n", gosub_args);
762
763                 if ((res = pbx_exec(chan, theapp, gosub_args)) == 0) {
764                         struct ast_pbx *pbx = ast_channel_pbx(chan);
765                         struct ast_pbx_args args;
766                         struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
767                         AST_LIST_HEAD(,gosub_stack_frame) *oldlist = stack_store->data;
768                         struct gosub_stack_frame *cur = AST_LIST_FIRST(oldlist);
769                         cur->is_agi = 1;
770
771                         memset(&args, 0, sizeof(args));
772                         args.no_hangup_chan = 1;
773                         /* Suppress warning about PBX already existing */
774                         ast_channel_pbx_set(chan, NULL);
775                         ast_agi_send(agi->fd, chan, "100 result=0 Trying...\n");
776                         ast_pbx_run_args(chan, &args);
777                         ast_agi_send(agi->fd, chan, "200 result=0 Gosub complete\n");
778                         if (ast_channel_pbx(chan)) {
779                                 ast_free(ast_channel_pbx(chan));
780                         }
781                         ast_channel_pbx_set(chan, pbx);
782                 } else {
783                         ast_agi_send(agi->fd, chan, "200 result=%d Gosub failed\n", res);
784                 }
785                 ast_free(gosub_args);
786         } else {
787                 ast_agi_send(agi->fd, chan, "503 result=-2 Memory allocation failure\n");
788                 return RESULT_FAILURE;
789         }
790
791         /* Restore previous location */
792         ast_channel_context_set(chan, old_context);
793         ast_channel_exten_set(chan, old_extension);
794         ast_channel_priority_set(chan, old_priority);
795
796         return RESULT_SUCCESS;
797 }
798
799 static struct agi_command gosub_agi_command =
800         { { "gosub", NULL }, handle_gosub, NULL, NULL, 0 };
801
802 static int unload_module(void)
803 {
804         ast_agi_unregister(ast_module_info->self, &gosub_agi_command);
805
806         ast_unregister_application(app_return);
807         ast_unregister_application(app_pop);
808         ast_unregister_application(app_gosubif);
809         ast_unregister_application(app_gosub);
810         ast_custom_function_unregister(&local_function);
811         ast_custom_function_unregister(&peek_function);
812         ast_custom_function_unregister(&stackpeek_function);
813
814         return 0;
815 }
816
817 static int load_module(void)
818 {
819         ast_agi_register(ast_module_info->self, &gosub_agi_command);
820
821         ast_register_application_xml(app_pop, pop_exec);
822         ast_register_application_xml(app_return, return_exec);
823         ast_register_application_xml(app_gosubif, gosubif_exec);
824         ast_register_application_xml(app_gosub, gosub_exec);
825         ast_custom_function_register(&local_function);
826         ast_custom_function_register(&peek_function);
827         ast_custom_function_register(&stackpeek_function);
828
829         return 0;
830 }
831
832 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT | AST_MODFLAG_LOAD_ORDER, "Dialplan subroutines (Gosub, Return, etc)",
833                 .load = load_module,
834                 .unload = unload_module,
835                 .load_pri = AST_MODPRI_APP_DEPEND,
836                 .nonoptreq = "res_agi",
837                 );