Allow non-normal execution routines to be able to run on hungup channels.
[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 app_gosub[] = "Gosub";
204 static const char app_gosubif[] = "GosubIf";
205 static const char app_return[] = "Return";
206 static const char 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         /*! TRUE if the return location marks the end of a special routine. */
222         unsigned int is_special:1;
223         char *context;
224         char extension[0];
225 };
226
227 AST_LIST_HEAD(gosub_stack_list, gosub_stack_frame);
228
229 static int frame_set_var(struct ast_channel *chan, struct gosub_stack_frame *frame, const char *var, const char *value)
230 {
231         struct ast_var_t *variables;
232         int found = 0;
233
234         /* Does this variable already exist? */
235         AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
236                 if (!strcmp(var, ast_var_name(variables))) {
237                         found = 1;
238                         break;
239                 }
240         }
241
242         if (!found) {
243                 variables = ast_var_assign(var, "");
244                 AST_LIST_INSERT_HEAD(&frame->varshead, variables, entries);
245                 pbx_builtin_pushvar_helper(chan, var, value);
246         } else {
247                 pbx_builtin_setvar_helper(chan, var, value);
248         }
249
250         manager_event(EVENT_FLAG_DIALPLAN, "VarSet",
251                 "Channel: %s\r\n"
252                 "Variable: LOCAL(%s)\r\n"
253                 "Value: %s\r\n"
254                 "Uniqueid: %s\r\n",
255                 ast_channel_name(chan), var, value, ast_channel_uniqueid(chan));
256         return 0;
257 }
258
259 static void gosub_release_frame(struct ast_channel *chan, struct gosub_stack_frame *frame)
260 {
261         struct ast_var_t *vardata;
262
263         /* If chan is not defined, then we're calling it as part of gosub_free,
264          * and the channel variables will be deallocated anyway.  Otherwise, we're
265          * just releasing a single frame, so we need to clean up the arguments for
266          * that frame, so that we re-expose the variables from the previous frame
267          * that were hidden by this one.
268          */
269         while ((vardata = AST_LIST_REMOVE_HEAD(&frame->varshead, entries))) {
270                 if (chan)
271                         pbx_builtin_setvar_helper(chan, ast_var_name(vardata), NULL);   
272                 ast_var_delete(vardata);
273         }
274
275         ast_free(frame);
276 }
277
278 static struct gosub_stack_frame *gosub_allocate_frame(const char *context, const char *extension, int priority, unsigned char arguments)
279 {
280         struct gosub_stack_frame *new = NULL;
281         int len_extension = strlen(extension), len_context = strlen(context);
282
283         if ((new = ast_calloc(1, sizeof(*new) + 2 + len_extension + len_context))) {
284                 AST_LIST_HEAD_INIT_NOLOCK(&new->varshead);
285                 strcpy(new->extension, extension);
286                 new->context = new->extension + len_extension + 1;
287                 strcpy(new->context, context);
288                 new->priority = priority;
289                 new->arguments = arguments;
290         }
291         return new;
292 }
293
294 static void gosub_free(void *data)
295 {
296         struct gosub_stack_list *oldlist = data;
297         struct gosub_stack_frame *oldframe;
298
299         AST_LIST_LOCK(oldlist);
300         while ((oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries))) {
301                 gosub_release_frame(NULL, oldframe);
302         }
303         AST_LIST_UNLOCK(oldlist);
304         AST_LIST_HEAD_DESTROY(oldlist);
305         ast_free(oldlist);
306 }
307
308 static int pop_exec(struct ast_channel *chan, const char *data)
309 {
310         struct ast_datastore *stack_store;
311         struct gosub_stack_frame *oldframe;
312         struct gosub_stack_list *oldlist;
313         int res = 0;
314
315         ast_channel_lock(chan);
316         if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
317                 ast_log(LOG_WARNING, "%s called with no gosub stack allocated.\n", app_pop);
318                 ast_channel_unlock(chan);
319                 return 0;
320         }
321
322         oldlist = stack_store->data;
323         AST_LIST_LOCK(oldlist);
324         oldframe = AST_LIST_FIRST(oldlist);
325         if (oldframe) {
326                 if (oldframe->is_special) {
327                         ast_debug(1, "%s attempted to pop special return location.\n", app_pop);
328
329                         /* Abort the special routine dialplan execution.  Dialplan programming error. */
330                         res = -1;
331                 } else {
332                         AST_LIST_REMOVE_HEAD(oldlist, entries);
333                         gosub_release_frame(chan, oldframe);
334                 }
335         } else {
336                 ast_debug(1, "%s called with an empty gosub stack\n", app_pop);
337         }
338         AST_LIST_UNLOCK(oldlist);
339         ast_channel_unlock(chan);
340         return res;
341 }
342
343 static int return_exec(struct ast_channel *chan, const char *data)
344 {
345         struct ast_datastore *stack_store;
346         struct gosub_stack_frame *oldframe;
347         struct gosub_stack_list *oldlist;
348         const char *retval = data;
349         int res = 0;
350
351         ast_channel_lock(chan);
352         if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
353                 ast_log(LOG_ERROR, "Return without Gosub: stack is unallocated\n");
354                 ast_channel_unlock(chan);
355                 return -1;
356         }
357
358         oldlist = stack_store->data;
359         AST_LIST_LOCK(oldlist);
360         oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
361         AST_LIST_UNLOCK(oldlist);
362
363         if (!oldframe) {
364                 ast_log(LOG_ERROR, "Return without Gosub: stack is empty\n");
365                 ast_channel_unlock(chan);
366                 return -1;
367         }
368         if (oldframe->is_special) {
369                 /* Exit from special routine. */
370                 res = -1;
371         }
372
373         /*
374          * We cannot use ast_explicit_goto() because we MUST restore
375          * what was there before.  Channels that do not have a PBX may
376          * not have the context or exten set.
377          */
378         ast_channel_context_set(chan, oldframe->context);
379         ast_channel_exten_set(chan, oldframe->extension);
380         if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP)) {
381                 --oldframe->priority;
382         }
383         ast_channel_priority_set(chan, oldframe->priority);
384
385         gosub_release_frame(chan, oldframe);
386
387         /* Set a return value, if any */
388         pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", S_OR(retval, ""));
389         ast_channel_unlock(chan);
390         return res;
391 }
392
393 /*!
394  * \internal
395  * \brief Add missing context and/or exten to Gosub application argument string.
396  * \since 11.0
397  *
398  * \param chan Channel to obtain context/exten.
399  * \param args Gosub application argument string.
400  *
401  * \details
402  * Fills in the optional context and exten from the given channel.
403  * Convert: [[context,]exten,]priority[(arg1[,...][,argN])]
404  * To: context,exten,priority[(arg1[,...][,argN])]
405  *
406  * \retval expanded Gosub argument string on success.  Must be freed.
407  * \retval NULL on error.
408  *
409  * \note The parsing needs to be kept in sync with the
410  * gosub_exec() argument format.
411  */
412 static const char *expand_gosub_args(struct ast_channel *chan, const char *args)
413 {
414         int len;
415         char *parse;
416         char *label;
417         char *new_args;
418         const char *context;
419         const char *exten;
420         const char *pri;
421
422         /* Separate the context,exten,pri from the optional routine arguments. */
423         parse = ast_strdupa(args);
424         label = strsep(&parse, "(");
425         if (parse) {
426                 char *endparen;
427
428                 endparen = strrchr(parse, ')');
429                 if (endparen) {
430                         *endparen = '\0';
431                 } else {
432                         ast_log(LOG_WARNING, "Ouch.  No closing paren: '%s'?\n", args);
433                 }
434         }
435
436         /* Split context,exten,pri */
437         context = strsep(&label, ",");
438         exten = strsep(&label, ",");
439         pri = strsep(&label, ",");
440         if (!exten) {
441                 /* Only a priority in this one */
442                 pri = context;
443                 exten = NULL;
444                 context = NULL;
445         } else if (!pri) {
446                 /* Only an extension and priority in this one */
447                 pri = exten;
448                 exten = context;
449                 context = NULL;
450         }
451
452         ast_channel_lock(chan);
453         if (ast_strlen_zero(exten)) {
454                 exten = ast_channel_exten(chan);
455         }
456         if (ast_strlen_zero(context)) {
457                 context = ast_channel_context(chan);
458         }
459         len = strlen(context) + strlen(exten) + strlen(pri) + 3;
460         if (!ast_strlen_zero(parse)) {
461                 len += 2 + strlen(parse);
462         }
463         new_args = ast_malloc(len);
464         if (new_args) {
465                 if (ast_strlen_zero(parse)) {
466                         snprintf(new_args, len, "%s,%s,%s", context, exten, pri);
467                 } else {
468                         snprintf(new_args, len, "%s,%s,%s(%s)", context, exten, pri, parse);
469                 }
470         }
471         ast_channel_unlock(chan);
472
473         ast_debug(4, "Gosub args:%s new_args:%s\n", args, new_args ? new_args : "");
474
475         return new_args;
476 }
477
478 static int gosub_exec(struct ast_channel *chan, const char *data)
479 {
480         struct ast_datastore *stack_store;
481         struct gosub_stack_list *oldlist;
482         struct gosub_stack_frame *newframe;
483         struct gosub_stack_frame *lastframe;
484         char argname[15];
485         char *parse;
486         char *label;
487         char *caller_id;
488         char *orig_context;
489         char *orig_exten;
490         char *dest_context;
491         char *dest_exten;
492         int orig_priority;
493         int dest_priority;
494         int i;
495         int max_argc = 0;
496         AST_DECLARE_APP_ARGS(args2,
497                 AST_APP_ARG(argval)[100];
498         );
499
500         if (ast_strlen_zero(data)) {
501                 ast_log(LOG_ERROR, "%s requires an argument: %s([[context,]exten,]priority[(arg1[,...][,argN])])\n", app_gosub, app_gosub);
502                 return -1;
503         }
504
505         /*
506          * Separate the arguments from the label
507          *
508          * NOTE:  You cannot use ast_app_separate_args for this, because
509          * '(' cannot be used as a delimiter.
510          */
511         parse = ast_strdupa(data);
512         label = strsep(&parse, "(");
513         if (parse) {
514                 char *endparen;
515
516                 endparen = strrchr(parse, ')');
517                 if (endparen) {
518                         *endparen = '\0';
519                 } else {
520                         ast_log(LOG_WARNING, "Ouch.  No closing paren: '%s'?\n", data);
521                 }
522                 AST_STANDARD_RAW_ARGS(args2, parse);
523         } else {
524                 args2.argc = 0;
525         }
526
527         ast_channel_lock(chan);
528         orig_context = ast_strdupa(ast_channel_context(chan));
529         orig_exten = ast_strdupa(ast_channel_exten(chan));
530         orig_priority = ast_channel_priority(chan);
531         ast_channel_unlock(chan);
532
533         if (ast_parseable_goto(chan, label)) {
534                 ast_log(LOG_ERROR, "%s address is invalid: '%s'\n", app_gosub, data);
535                 goto error_exit;
536         }
537
538         ast_channel_lock(chan);
539         dest_context = ast_strdupa(ast_channel_context(chan));
540         dest_exten = ast_strdupa(ast_channel_exten(chan));
541         dest_priority = ast_channel_priority(chan);
542         if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP)) {
543                 ++dest_priority;
544         }
545         caller_id = S_COR(ast_channel_caller(chan)->id.number.valid,
546                 ast_channel_caller(chan)->id.number.str, NULL);
547         if (caller_id) {
548                 caller_id = ast_strdupa(caller_id);
549         }
550         ast_channel_unlock(chan);
551
552         if (!ast_exists_extension(chan, dest_context, dest_exten, dest_priority, caller_id)) {
553                 ast_log(LOG_ERROR, "Attempt to reach a non-existent destination for %s: (Context:%s, Extension:%s, Priority:%d)\n",
554                         app_gosub, dest_context, dest_exten, dest_priority);
555                 goto error_exit;
556         }
557
558         /* Now we know that we're going to a new location */
559
560         ast_channel_lock(chan);
561
562         /* Find stack datastore return list. */
563         if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
564                 ast_debug(1, "Channel %s has no datastore, so we're allocating one.\n",
565                         ast_channel_name(chan));
566                 stack_store = ast_datastore_alloc(&stack_info, NULL);
567                 if (!stack_store) {
568                         ast_log(LOG_ERROR, "Unable to allocate new datastore.  %s failed.\n",
569                                 app_gosub);
570                         goto error_exit_locked;
571                 }
572
573                 oldlist = ast_calloc(1, sizeof(*oldlist));
574                 if (!oldlist) {
575                         ast_log(LOG_ERROR, "Unable to allocate datastore list head.  %s failed.\n",
576                                 app_gosub);
577                         ast_datastore_free(stack_store);
578                         goto error_exit_locked;
579                 }
580                 AST_LIST_HEAD_INIT(oldlist);
581
582                 stack_store->data = oldlist;
583                 ast_channel_datastore_add(chan, stack_store);
584         } else {
585                 oldlist = stack_store->data;
586         }
587
588         if ((lastframe = AST_LIST_FIRST(oldlist))) {
589                 max_argc = lastframe->arguments;
590         }
591
592         /* Mask out previous Gosub arguments in this invocation */
593         if (args2.argc > max_argc) {
594                 max_argc = args2.argc;
595         }
596
597         /* Create the return address */
598         newframe = gosub_allocate_frame(orig_context, orig_exten, orig_priority + 1, max_argc);
599         if (!newframe) {
600                 goto error_exit_locked;
601         }
602
603         /* Set our arguments */
604         for (i = 0; i < max_argc; i++) {
605                 snprintf(argname, sizeof(argname), "ARG%d", i + 1);
606                 frame_set_var(chan, newframe, argname, i < args2.argc ? args2.argval[i] : "");
607                 ast_debug(1, "Setting '%s' to '%s'\n", argname, i < args2.argc ? args2.argval[i] : "");
608         }
609         snprintf(argname, sizeof(argname), "%d", args2.argc);
610         frame_set_var(chan, newframe, "ARGC", argname);
611
612         /* And finally, save our return address */
613         AST_LIST_LOCK(oldlist);
614         AST_LIST_INSERT_HEAD(oldlist, newframe, entries);
615         AST_LIST_UNLOCK(oldlist);
616         ast_channel_unlock(chan);
617
618         return 0;
619
620 error_exit:
621         ast_channel_lock(chan);
622
623 error_exit_locked:
624         /* Restore the original dialplan location. */
625         ast_channel_context_set(chan, orig_context);
626         ast_channel_exten_set(chan, orig_exten);
627         ast_channel_priority_set(chan, orig_priority);
628         ast_channel_unlock(chan);
629         return -1;
630 }
631
632 static int gosubif_exec(struct ast_channel *chan, const char *data)
633 {
634         char *args;
635         int res=0;
636         AST_DECLARE_APP_ARGS(cond,
637                 AST_APP_ARG(ition);
638                 AST_APP_ARG(labels);
639         );
640         AST_DECLARE_APP_ARGS(label,
641                 AST_APP_ARG(iftrue);
642                 AST_APP_ARG(iffalse);
643         );
644
645         if (ast_strlen_zero(data)) {
646                 ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
647                 return 0;
648         }
649
650         args = ast_strdupa(data);
651         AST_NONSTANDARD_RAW_ARGS(cond, args, '?');
652         if (cond.argc != 2) {
653                 ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
654                 return 0;
655         }
656
657         AST_NONSTANDARD_RAW_ARGS(label, cond.labels, ':');
658
659         if (pbx_checkcondition(cond.ition)) {
660                 if (!ast_strlen_zero(label.iftrue))
661                         res = gosub_exec(chan, label.iftrue);
662         } else if (!ast_strlen_zero(label.iffalse)) {
663                 res = gosub_exec(chan, label.iffalse);
664         }
665
666         return res;
667 }
668
669 static int local_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
670 {
671         struct ast_datastore *stack_store;
672         struct gosub_stack_list *oldlist;
673         struct gosub_stack_frame *frame;
674         struct ast_var_t *variables;
675
676         ast_channel_lock(chan);
677         if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
678                 ast_channel_unlock(chan);
679                 return -1;
680         }
681
682         oldlist = stack_store->data;
683         AST_LIST_LOCK(oldlist);
684         if (!(frame = AST_LIST_FIRST(oldlist))) {
685                 /* Not within a Gosub routine */
686                 AST_LIST_UNLOCK(oldlist);
687                 ast_channel_unlock(chan);
688                 return -1;
689         }
690
691         AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
692                 if (!strcmp(data, ast_var_name(variables))) {
693                         const char *tmp;
694                         tmp = pbx_builtin_getvar_helper(chan, data);
695                         ast_copy_string(buf, S_OR(tmp, ""), len);
696                         break;
697                 }
698         }
699         AST_LIST_UNLOCK(oldlist);
700         ast_channel_unlock(chan);
701         return 0;
702 }
703
704 static int local_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
705 {
706         struct ast_datastore *stack_store;
707         struct gosub_stack_list *oldlist;
708         struct gosub_stack_frame *frame;
709
710         ast_channel_lock(chan);
711         if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
712                 ast_log(LOG_ERROR, "Tried to set LOCAL(%s), but we aren't within a Gosub routine\n", var);
713                 ast_channel_unlock(chan);
714                 return -1;
715         }
716
717         oldlist = stack_store->data;
718         AST_LIST_LOCK(oldlist);
719         frame = AST_LIST_FIRST(oldlist);
720
721         if (frame) {
722                 frame_set_var(chan, frame, var, value);
723         }
724
725         AST_LIST_UNLOCK(oldlist);
726         ast_channel_unlock(chan);
727
728         return 0;
729 }
730
731 static struct ast_custom_function local_function = {
732         .name = "LOCAL",
733         .write = local_write,
734         .read = local_read,
735 };
736
737 static int peek_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
738 {
739         int found = 0, n;
740         struct ast_var_t *variables;
741         AST_DECLARE_APP_ARGS(args,
742                 AST_APP_ARG(n);
743                 AST_APP_ARG(name);
744         );
745
746         if (!chan) {
747                 ast_log(LOG_ERROR, "LOCAL_PEEK must be called on an active channel\n");
748                 return -1;
749         }
750
751         AST_STANDARD_RAW_ARGS(args, data);
752         n = atoi(args.n);
753         *buf = '\0';
754
755         ast_channel_lock(chan);
756         AST_LIST_TRAVERSE(ast_channel_varshead(chan), variables, entries) {
757                 if (!strcmp(args.name, ast_var_name(variables)) && ++found > n) {
758                         ast_copy_string(buf, ast_var_value(variables), len);
759                         break;
760                 }
761         }
762         ast_channel_unlock(chan);
763         return 0;
764 }
765
766 static struct ast_custom_function peek_function = {
767         .name = "LOCAL_PEEK",
768         .read = peek_read,
769 };
770
771 static int stackpeek_read(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **str, ssize_t len)
772 {
773         struct ast_datastore *stack_store;
774         struct gosub_stack_list *oldlist;
775         struct gosub_stack_frame *frame;
776         int n;
777         AST_DECLARE_APP_ARGS(args,
778                 AST_APP_ARG(n);
779                 AST_APP_ARG(which);
780                 AST_APP_ARG(suppress);
781         );
782
783         if (!chan) {
784                 ast_log(LOG_ERROR, "STACK_PEEK must be called on an active channel\n");
785                 return -1;
786         }
787
788         data = ast_strdupa(data);
789         AST_STANDARD_APP_ARGS(args, data);
790
791         n = atoi(args.n);
792         if (n <= 0) {
793                 ast_log(LOG_ERROR, "STACK_PEEK must be called with a positive peek value\n");
794                 return -1;
795         }
796
797         ast_channel_lock(chan);
798         if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
799                 if (!ast_true(args.suppress)) {
800                         ast_log(LOG_ERROR, "STACK_PEEK called on a channel without a gosub stack\n");
801                 }
802                 ast_channel_unlock(chan);
803                 return -1;
804         }
805
806         oldlist = stack_store->data;
807
808         AST_LIST_LOCK(oldlist);
809         AST_LIST_TRAVERSE(oldlist, frame, entries) {
810                 if (--n == 0) {
811                         break;
812                 }
813         }
814
815         if (!frame) {
816                 /* Too deep */
817                 if (!ast_true(args.suppress)) {
818                         ast_log(LOG_ERROR, "Stack peek of '%s' is more stack frames than I have\n", args.n);
819                 }
820                 ast_channel_unlock(chan);
821                 return -1;
822         }
823
824         args.which = ast_skip_blanks(args.which);
825
826         switch (args.which[0]) {
827         case 'l': /* label */
828                 ast_str_set(str, len, "%s,%s,%d", frame->context, frame->extension, frame->priority - 1);
829                 break;
830         case 'c': /* context */
831                 ast_str_set(str, len, "%s", frame->context);
832                 break;
833         case 'e': /* extension */
834                 ast_str_set(str, len, "%s", frame->extension);
835                 break;
836         case 'p': /* priority */
837                 ast_str_set(str, len, "%d", frame->priority - 1);
838                 break;
839         default:
840                 ast_log(LOG_ERROR, "Unknown argument '%s' to STACK_PEEK\n", args.which);
841                 break;
842         }
843
844         AST_LIST_UNLOCK(oldlist);
845         ast_channel_unlock(chan);
846
847         return 0;
848 }
849
850 static struct ast_custom_function stackpeek_function = {
851         .name = "STACK_PEEK",
852         .read2 = stackpeek_read,
853 };
854
855 /*!
856  * \internal
857  * \brief Pop stack frames until remove a special return location.
858  * \since 11.0
859  *
860  * \param chan Channel to balance stack on.
861  *
862  * \note The channel is already locked when called.
863  *
864  * \return Nothing
865  */
866 static void balance_stack(struct ast_channel *chan)
867 {
868         struct ast_datastore *stack_store;
869         struct gosub_stack_list *oldlist;
870         struct gosub_stack_frame *oldframe;
871         int found;
872
873         stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
874         if (!stack_store) {
875                 ast_log(LOG_WARNING, "No %s stack allocated.\n", app_gosub);
876                 return;
877         }
878
879         oldlist = stack_store->data;
880         AST_LIST_LOCK(oldlist);
881         do {
882                 oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
883                 if (!oldframe) {
884                         break;
885                 }
886                 found = oldframe->is_special;
887                 gosub_release_frame(chan, oldframe);
888         } while (!found);
889         AST_LIST_UNLOCK(oldlist);
890 }
891
892 /*!
893  * \internal
894  * \brief Run a subroutine on a channel.
895  * \since 11.0
896  *
897  * \note Absolutely _NO_ channel locks should be held before calling this function.
898  *
899  * \param chan Channel to execute subroutine on.
900  * \param sub_args Gosub application argument string.
901  * \param ignore_hangup TRUE if a hangup does not stop execution of the routine.
902  *
903  * \retval 0 success
904  * \retval -1 on error
905  */
906 static int gosub_run(struct ast_channel *chan, const char *sub_args, int ignore_hangup)
907 {
908         const char *saved_context;
909         const char *saved_exten;
910         int saved_priority;
911         int saved_hangup_flags;
912         int saved_autoloopflag;
913         int res;
914
915         ast_channel_lock(chan);
916
917         ast_verb(3, "%s Internal %s(%s) start\n",
918                 ast_channel_name(chan), app_gosub, sub_args);
919
920         /* Save non-hangup softhangup flags. */
921         saved_hangup_flags = ast_channel_softhangup_internal_flag(chan)
922                 & (AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE);
923         if (saved_hangup_flags) {
924                 ast_channel_clear_softhangup(chan,
925                         AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE);
926         }
927
928         /* Save autoloop flag */
929         saved_autoloopflag = ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
930         ast_set_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
931
932         /* Save current dialplan location */
933         saved_context = ast_strdupa(ast_channel_context(chan));
934         saved_exten = ast_strdupa(ast_channel_exten(chan));
935         saved_priority = ast_channel_priority(chan);
936
937         ast_debug(4, "%s Original location: %s,%s,%d\n", ast_channel_name(chan),
938                 saved_context, saved_exten, saved_priority);
939
940         ast_channel_unlock(chan);
941         res = gosub_exec(chan, sub_args);
942         ast_debug(4, "%s exited with status %d\n", app_gosub, res);
943         ast_channel_lock(chan);
944         if (!res) {
945                 struct ast_datastore *stack_store;
946
947                 /* Mark the return location as special. */
948                 stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
949                 if (!stack_store) {
950                         /* Should never happen! */
951                         ast_log(LOG_ERROR, "No %s stack!\n", app_gosub);
952                         res = -1;
953                 } else {
954                         struct gosub_stack_list *oldlist;
955                         struct gosub_stack_frame *cur;
956
957                         oldlist = stack_store->data;
958                         cur = AST_LIST_FIRST(oldlist);
959                         cur->is_special = 1;
960                 }
961         }
962         if (!res) {
963                 int found = 0;  /* set if we find at least one match */
964
965                 /*
966                  * Run gosub body autoloop.
967                  *
968                  * Note that this loop is inverted from the normal execution
969                  * loop because we just executed the Gosub application as the
970                  * first extension of the autoloop.
971                  */
972                 do {
973                         /* Check for hangup. */
974                         if (ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_UNBRIDGE) {
975                                 saved_hangup_flags |= AST_SOFTHANGUP_UNBRIDGE;
976                                 ast_channel_clear_softhangup(chan, AST_SOFTHANGUP_UNBRIDGE);
977                         }
978                         if (ast_check_hangup(chan)) {
979                                 if (ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO) {
980                                         ast_log(LOG_ERROR, "%s An async goto just messed up our execution location.\n",
981                                                 ast_channel_name(chan));
982                                         break;
983                                 }
984                                 if (!ignore_hangup) {
985                                         break;
986                                 }
987                         }
988
989                         /* Next dialplan priority. */
990                         ast_channel_priority_set(chan, ast_channel_priority(chan) + 1);
991
992                         ast_channel_unlock(chan);
993                         res = ast_spawn_extension(chan, ast_channel_context(chan),
994                                 ast_channel_exten(chan), ast_channel_priority(chan),
995                                 S_COR(ast_channel_caller(chan)->id.number.valid,
996                                         ast_channel_caller(chan)->id.number.str, NULL),
997                                 &found, 1);
998                         ast_channel_lock(chan);
999                 } while (!res);
1000                 if (found && res) {
1001                         /* Something bad happened, or a hangup has been requested. */
1002                         ast_debug(1, "Spawn extension (%s,%s,%d) exited with %d on '%s'\n",
1003                                 ast_channel_context(chan), ast_channel_exten(chan),
1004                                 ast_channel_priority(chan), res, ast_channel_name(chan));
1005                         ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n",
1006                                 ast_channel_context(chan), ast_channel_exten(chan),
1007                                 ast_channel_priority(chan), ast_channel_name(chan));
1008                 }
1009
1010                 /* Did the routine return? */
1011                 if (ast_channel_priority(chan) == saved_priority
1012                         && !strcmp(ast_channel_context(chan), saved_context)
1013                         && !strcmp(ast_channel_exten(chan), saved_exten)) {
1014                         ast_verb(3, "%s Internal %s(%s) complete GOSUB_RETVAL=%s\n",
1015                                 ast_channel_name(chan), app_gosub, sub_args,
1016                                 S_OR(pbx_builtin_getvar_helper(chan, "GOSUB_RETVAL"), ""));
1017                 } else {
1018                         ast_log(LOG_NOTICE, "%s Abnormal '%s(%s)' exit.  Popping routine return locations.\n",
1019                                 ast_channel_name(chan), app_gosub, sub_args);
1020                         balance_stack(chan);
1021                         pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", "");
1022                 }
1023
1024                 /* We executed the requested subroutine to the best of our ability. */
1025                 res = 0;
1026         }
1027
1028         ast_debug(4, "%s Ending location: %s,%s,%d\n", ast_channel_name(chan),
1029                 ast_channel_context(chan), ast_channel_exten(chan),
1030                 ast_channel_priority(chan));
1031
1032         /* Restore dialplan location */
1033         if (!(ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO)) {
1034                 ast_channel_context_set(chan, saved_context);
1035                 ast_channel_exten_set(chan, saved_exten);
1036                 ast_channel_priority_set(chan, saved_priority);
1037         }
1038
1039         /* Restore autoloop flag */
1040         ast_set2_flag(ast_channel_flags(chan), saved_autoloopflag, AST_FLAG_IN_AUTOLOOP);
1041
1042         /* Restore non-hangup softhangup flags. */
1043         if (saved_hangup_flags) {
1044                 ast_softhangup_nolock(chan, saved_hangup_flags);
1045         }
1046
1047         ast_channel_unlock(chan);
1048
1049         return res;
1050 }
1051
1052 static int handle_gosub(struct ast_channel *chan, AGI *agi, int argc, const char * const *argv)
1053 {
1054         int res;
1055         int priority;
1056         int old_autoloopflag;
1057         int old_priority;
1058         const char *old_context;
1059         const char *old_extension;
1060         char *gosub_args;
1061
1062         if (argc < 4 || argc > 5) {
1063                 return RESULT_SHOWUSAGE;
1064         }
1065
1066         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] : "");
1067
1068         if (sscanf(argv[3], "%30d", &priority) != 1 || priority < 1) {
1069                 /* Lookup the priority label */
1070                 priority = ast_findlabel_extension(chan, argv[1], argv[2], argv[3],
1071                         S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL));
1072                 if (priority < 0) {
1073                         ast_log(LOG_ERROR, "Priority '%s' not found in '%s@%s'\n", argv[3], argv[2], argv[1]);
1074                         ast_agi_send(agi->fd, chan, "200 result=-1 Gosub label not found\n");
1075                         return RESULT_FAILURE;
1076                 }
1077         } else if (!ast_exists_extension(chan, argv[1], argv[2], priority,
1078                 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
1079                 ast_agi_send(agi->fd, chan, "200 result=-1 Gosub label not found\n");
1080                 return RESULT_FAILURE;
1081         }
1082
1083         if (argc == 5) {
1084                 if (asprintf(&gosub_args, "%s,%s,%d(%s)", argv[1], argv[2], priority, argv[4]) < 0) {
1085                         ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
1086                         gosub_args = NULL;
1087                 }
1088         } else {
1089                 if (asprintf(&gosub_args, "%s,%s,%d", argv[1], argv[2], priority) < 0) {
1090                         ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
1091                         gosub_args = NULL;
1092                 }
1093         }
1094         if (!gosub_args) {
1095                 ast_agi_send(agi->fd, chan, "503 result=-2 Memory allocation failure\n");
1096                 return RESULT_FAILURE;
1097         }
1098
1099         ast_channel_lock(chan);
1100
1101         ast_verb(3, "%s AGI %s(%s) start\n", ast_channel_name(chan), app_gosub, gosub_args);
1102
1103         /* Save autoloop flag */
1104         old_autoloopflag = ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
1105         ast_set_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
1106
1107         /* Save previous location, since we're going to change it */
1108         old_context = ast_strdupa(ast_channel_context(chan));
1109         old_extension = ast_strdupa(ast_channel_exten(chan));
1110         old_priority = ast_channel_priority(chan);
1111
1112         ast_debug(4, "%s Original location: %s,%s,%d\n", ast_channel_name(chan),
1113                 old_context, old_extension, old_priority);
1114         ast_channel_unlock(chan);
1115
1116         res = gosub_exec(chan, gosub_args);
1117         if (!res) {
1118                 struct ast_datastore *stack_store;
1119
1120                 /* Mark the return location as special. */
1121                 ast_channel_lock(chan);
1122                 stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
1123                 if (!stack_store) {
1124                         /* Should never happen! */
1125                         ast_log(LOG_ERROR, "No %s stack!\n", app_gosub);
1126                         res = -1;
1127                 } else {
1128                         struct gosub_stack_list *oldlist;
1129                         struct gosub_stack_frame *cur;
1130
1131                         oldlist = stack_store->data;
1132                         cur = AST_LIST_FIRST(oldlist);
1133                         cur->is_special = 1;
1134                 }
1135                 ast_channel_unlock(chan);
1136         }
1137         if (!res) {
1138                 struct ast_pbx *pbx;
1139                 struct ast_pbx_args args;
1140                 int abnormal_exit;
1141
1142                 memset(&args, 0, sizeof(args));
1143                 args.no_hangup_chan = 1;
1144
1145                 ast_channel_lock(chan);
1146
1147                 /* Next dialplan priority. */
1148                 ast_channel_priority_set(chan, ast_channel_priority(chan) + 1);
1149
1150                 /* Suppress warning about PBX already existing */
1151                 pbx = ast_channel_pbx(chan);
1152                 ast_channel_pbx_set(chan, NULL);
1153                 ast_channel_unlock(chan);
1154
1155                 ast_agi_send(agi->fd, chan, "100 result=0 Trying...\n");
1156                 ast_pbx_run_args(chan, &args);
1157
1158                 ast_channel_lock(chan);
1159                 ast_free(ast_channel_pbx(chan));
1160                 ast_channel_pbx_set(chan, pbx);
1161
1162                 /* Did the routine return? */
1163                 if (ast_channel_priority(chan) == old_priority
1164                         && !strcmp(ast_channel_context(chan), old_context)
1165                         && !strcmp(ast_channel_exten(chan), old_extension)) {
1166                         ast_verb(3, "%s AGI %s(%s) complete GOSUB_RETVAL=%s\n",
1167                                 ast_channel_name(chan), app_gosub, gosub_args,
1168                                 S_OR(pbx_builtin_getvar_helper(chan, "GOSUB_RETVAL"), ""));
1169                         abnormal_exit = 0;
1170                 } else {
1171                         ast_log(LOG_NOTICE, "%s Abnormal AGI %s(%s) exit.  Popping routine return locations.\n",
1172                                 ast_channel_name(chan), app_gosub, gosub_args);
1173                         balance_stack(chan);
1174                         pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", "");
1175                         abnormal_exit = 1;
1176                 }
1177                 ast_channel_unlock(chan);
1178
1179                 ast_agi_send(agi->fd, chan, "200 result=0 Gosub complete%s\n",
1180                         abnormal_exit ? " (abnormal exit)" : "");
1181         } else {
1182                 ast_agi_send(agi->fd, chan, "200 result=%d Gosub failed\n", res);
1183         }
1184
1185         /* Must use free because the memory was allocated by asprintf(). */
1186         free(gosub_args);
1187
1188         ast_channel_lock(chan);
1189         ast_debug(4, "%s Ending location: %s,%s,%d\n", ast_channel_name(chan),
1190                 ast_channel_context(chan), ast_channel_exten(chan),
1191                 ast_channel_priority(chan));
1192
1193         /* Restore previous location */
1194         ast_channel_context_set(chan, old_context);
1195         ast_channel_exten_set(chan, old_extension);
1196         ast_channel_priority_set(chan, old_priority);
1197
1198         /* Restore autoloop flag */
1199         ast_set2_flag(ast_channel_flags(chan), old_autoloopflag, AST_FLAG_IN_AUTOLOOP);
1200         ast_channel_unlock(chan);
1201
1202         return RESULT_SUCCESS;
1203 }
1204
1205 static struct agi_command gosub_agi_command =
1206         { { "gosub", NULL }, handle_gosub, NULL, NULL, 0 };
1207
1208 static int unload_module(void)
1209 {
1210         ast_install_stack_functions(NULL);
1211
1212         ast_agi_unregister(ast_module_info->self, &gosub_agi_command);
1213
1214         ast_unregister_application(app_return);
1215         ast_unregister_application(app_pop);
1216         ast_unregister_application(app_gosubif);
1217         ast_unregister_application(app_gosub);
1218         ast_custom_function_unregister(&local_function);
1219         ast_custom_function_unregister(&peek_function);
1220         ast_custom_function_unregister(&stackpeek_function);
1221
1222         return 0;
1223 }
1224
1225 static int load_module(void)
1226 {
1227         /* Setup the stack application callback functions. */
1228         static struct ast_app_stack_funcs funcs = {
1229                 .run_sub = gosub_run,
1230                 .expand_sub_args = expand_gosub_args,
1231         };
1232
1233         ast_agi_register(ast_module_info->self, &gosub_agi_command);
1234
1235         ast_register_application_xml(app_pop, pop_exec);
1236         ast_register_application_xml(app_return, return_exec);
1237         ast_register_application_xml(app_gosubif, gosubif_exec);
1238         ast_register_application_xml(app_gosub, gosub_exec);
1239         ast_custom_function_register(&local_function);
1240         ast_custom_function_register(&peek_function);
1241         ast_custom_function_register(&stackpeek_function);
1242
1243         funcs.module = ast_module_info->self,
1244         ast_install_stack_functions(&funcs);
1245
1246         return 0;
1247 }
1248
1249 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT | AST_MODFLAG_LOAD_ORDER, "Dialplan subroutines (Gosub, Return, etc)",
1250                 .load = load_module,
1251                 .unload = unload_module,
1252                 .load_pri = AST_MODPRI_APP_DEPEND,
1253                 .nonoptreq = "res_agi",
1254                 );