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