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