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