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