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