Sat Jul 12 2014 17:18:25

Asterisk developer's documentation


app_stack.c
Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (c) 2004-2006 Tilghman Lesher <app_stack_v003@the-tilghman.com>.
00005  *
00006  * This code is released by the author with no restrictions on usage.
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Stack applications Gosub, Return, etc.
00022  *
00023  * \author Tilghman Lesher <app_stack_v003@the-tilghman.com>
00024  * 
00025  * \ingroup applications
00026  */
00027 
00028 /*** MODULEINFO
00029    <use type="module">res_agi</use>
00030    <support_level>core</support_level>
00031  ***/
00032 
00033 #include "asterisk.h"
00034  
00035 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 413587 $")
00036 
00037 #include "asterisk/pbx.h"
00038 #include "asterisk/module.h"
00039 #include "asterisk/app.h"
00040 #include "asterisk/manager.h"
00041 #include "asterisk/channel.h"
00042 #include "asterisk/agi.h"
00043 
00044 /*** DOCUMENTATION
00045    <application name="Gosub" language="en_US">
00046       <synopsis>
00047          Jump to label, saving return address.
00048       </synopsis>
00049       <syntax>
00050          <parameter name="context" />
00051          <parameter name="exten" />
00052          <parameter name="priority" required="true" hasparams="optional">
00053             <argument name="arg1" multiple="true" required="true" />
00054             <argument name="argN" />
00055          </parameter>
00056       </syntax>
00057       <description>
00058          <para>Jumps to the label specified, saving the return address.</para>
00059       </description>
00060       <see-also>
00061          <ref type="application">GosubIf</ref>
00062          <ref type="application">Macro</ref>
00063          <ref type="application">Goto</ref>
00064          <ref type="application">Return</ref>
00065          <ref type="application">StackPop</ref>
00066       </see-also>
00067    </application>
00068    <application name="GosubIf" language="en_US">
00069       <synopsis>
00070          Conditionally jump to label, saving return address.
00071       </synopsis>
00072       <syntax argsep="?">
00073          <parameter name="condition" required="true" />
00074          <parameter name="destination" required="true" argsep=":">
00075             <argument name="labeliftrue" hasparams="optional">
00076                <para>Continue at <replaceable>labeliftrue</replaceable> if the condition is true.
00077                Takes the form similar to Goto() of [[context,]extension,]priority.</para>
00078                <argument name="arg1" required="true" multiple="true" />
00079                <argument name="argN" />
00080             </argument>
00081             <argument name="labeliffalse" hasparams="optional">
00082                <para>Continue at <replaceable>labeliffalse</replaceable> if the condition is false.
00083                Takes the form similar to Goto() of [[context,]extension,]priority.</para>
00084                <argument name="arg1" required="true" multiple="true" />
00085                <argument name="argN" />
00086             </argument>
00087          </parameter>
00088       </syntax>
00089       <description>
00090          <para>If the condition is true, then jump to labeliftrue.  If false, jumps to
00091          labeliffalse, if specified.  In either case, a jump saves the return point
00092          in the dialplan, to be returned to with a Return.</para>
00093       </description>
00094       <see-also>
00095          <ref type="application">Gosub</ref>
00096          <ref type="application">Return</ref>
00097          <ref type="application">MacroIf</ref>
00098          <ref type="function">IF</ref>
00099          <ref type="application">GotoIf</ref>
00100          <ref type="application">Goto</ref>
00101       </see-also>
00102    </application>
00103    <application name="Return" language="en_US">
00104       <synopsis>
00105          Return from gosub routine.
00106       </synopsis>
00107       <syntax>
00108          <parameter name="value">
00109             <para>Return value.</para>
00110          </parameter>
00111       </syntax>
00112       <description>
00113          <para>Jumps to the last label on the stack, removing it. The return <replaceable>value</replaceable>, if
00114          any, is saved in the channel variable <variable>GOSUB_RETVAL</variable>.</para>
00115       </description>
00116       <see-also>
00117          <ref type="application">Gosub</ref>
00118          <ref type="application">StackPop</ref>
00119       </see-also>
00120    </application>
00121    <application name="StackPop" language="en_US">
00122       <synopsis>
00123          Remove one address from gosub stack.
00124       </synopsis>
00125       <syntax />
00126       <description>
00127          <para>Removes last label on the stack, discarding it.</para>
00128       </description>
00129       <see-also>
00130          <ref type="application">Return</ref>
00131          <ref type="application">Gosub</ref>
00132       </see-also>
00133    </application>
00134    <function name="LOCAL" language="en_US">
00135       <synopsis>
00136          Manage variables local to the gosub stack frame.
00137       </synopsis>
00138       <syntax>
00139          <parameter name="varname" required="true" />
00140       </syntax>
00141       <description>
00142          <para>Read and write a variable local to the gosub stack frame, once we Return() it will be lost
00143          (or it will go back to whatever value it had before the Gosub()).</para>
00144       </description>
00145       <see-also>
00146          <ref type="application">Gosub</ref>
00147          <ref type="application">GosubIf</ref>
00148          <ref type="application">Return</ref>
00149       </see-also>
00150    </function>
00151    <function name="LOCAL_PEEK" language="en_US">
00152       <synopsis>
00153          Retrieve variables hidden by the local gosub stack frame.
00154       </synopsis>
00155       <syntax>
00156          <parameter name="n" required="true" />
00157          <parameter name="varname" required="true" />
00158       </syntax>
00159       <description>
00160          <para>Read a variable <replaceable>varname</replaceable> hidden by
00161          <replaceable>n</replaceable> levels of gosub stack frames.  Note that ${LOCAL_PEEK(0,foo)}
00162          is the same as <variable>foo</variable>, since the value of <replaceable>n</replaceable>
00163          peeks under 0 levels of stack frames; in other words, 0 is the current level.  If
00164          <replaceable>n</replaceable> exceeds the available number of stack frames, then an empty
00165          string is returned.</para>
00166       </description>
00167       <see-also>
00168          <ref type="application">Gosub</ref>
00169          <ref type="application">GosubIf</ref>
00170          <ref type="application">Return</ref>
00171       </see-also>
00172    </function>
00173    <function name="STACK_PEEK" language="en_US">
00174       <synopsis>
00175          View info about the location which called Gosub
00176       </synopsis>
00177       <syntax>
00178          <parameter name="n" required="true" />
00179          <parameter name="which" required="true" />
00180          <parameter name="suppress" required="false" />
00181       </syntax>
00182       <description>
00183          <para>Read the calling <literal>c</literal>ontext, <literal>e</literal>xtension,
00184          <literal>p</literal>riority, or <literal>l</literal>abel, as specified by
00185          <replaceable>which</replaceable>, by going up <replaceable>n</replaceable> frames
00186          in the Gosub stack.  If <replaceable>suppress</replaceable> is true, then if the
00187          number of available stack frames is exceeded, then no error message will be
00188          printed.</para>
00189       </description>
00190    </function>
00191    <agi name="gosub" language="en_US">
00192       <synopsis>
00193          Cause the channel to execute the specified dialplan subroutine.
00194       </synopsis>
00195       <syntax>
00196          <parameter name="context" required="true" />
00197          <parameter name="extension" required="true" />
00198          <parameter name="priority" required="true" />
00199          <parameter name="optional-argument" />
00200       </syntax>
00201       <description>
00202          <para>Cause the channel to execute the specified dialplan subroutine,
00203          returning to the dialplan with execution of a Return().</para>
00204       </description>
00205    </agi>
00206  ***/
00207 
00208 static const char app_gosub[] = "Gosub";
00209 static const char app_gosubif[] = "GosubIf";
00210 static const char app_return[] = "Return";
00211 static const char app_pop[] = "StackPop";
00212 
00213 static void gosub_free(void *data);
00214 
00215 static const struct ast_datastore_info stack_info = {
00216    .type = "GOSUB",
00217    .destroy = gosub_free,
00218 };
00219 
00220 struct gosub_stack_frame {
00221    AST_LIST_ENTRY(gosub_stack_frame) entries;
00222    /* 100 arguments is all that we support anyway, but this will handle up to 255 */
00223    unsigned char arguments;
00224    struct varshead varshead;
00225    int priority;
00226    /*! TRUE if the return location marks the end of a special routine. */
00227    unsigned int is_special:1;
00228    char *context;
00229    char extension[0];
00230 };
00231 
00232 AST_LIST_HEAD(gosub_stack_list, gosub_stack_frame);
00233 
00234 static int frame_set_var(struct ast_channel *chan, struct gosub_stack_frame *frame, const char *var, const char *value)
00235 {
00236    struct ast_var_t *variables;
00237    int found = 0;
00238 
00239    /* Does this variable already exist? */
00240    AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
00241       if (!strcmp(var, ast_var_name(variables))) {
00242          found = 1;
00243          break;
00244       }
00245    }
00246 
00247    if (!found) {
00248       if ((variables = ast_var_assign(var, ""))) {
00249          AST_LIST_INSERT_HEAD(&frame->varshead, variables, entries);
00250       }
00251       pbx_builtin_pushvar_helper(chan, var, value);
00252    } else {
00253       pbx_builtin_setvar_helper(chan, var, value);
00254    }
00255 
00256    /*** DOCUMENTATION
00257    <managerEventInstance>
00258       <synopsis>Raised when a LOCAL channel variable is set due to a subroutine call.</synopsis>
00259       <see-also>
00260          <ref type="application">GoSub</ref>
00261       </see-also>
00262    </managerEventInstance>
00263    ***/
00264    manager_event(EVENT_FLAG_DIALPLAN, "VarSet",
00265       "Channel: %s\r\n"
00266       "Variable: LOCAL(%s)\r\n"
00267       "Value: %s\r\n"
00268       "Uniqueid: %s\r\n",
00269       ast_channel_name(chan), var, value, ast_channel_uniqueid(chan));
00270    return 0;
00271 }
00272 
00273 static void gosub_release_frame(struct ast_channel *chan, struct gosub_stack_frame *frame)
00274 {
00275    struct ast_var_t *vardata;
00276 
00277    /* If chan is not defined, then we're calling it as part of gosub_free,
00278     * and the channel variables will be deallocated anyway.  Otherwise, we're
00279     * just releasing a single frame, so we need to clean up the arguments for
00280     * that frame, so that we re-expose the variables from the previous frame
00281     * that were hidden by this one.
00282     */
00283    while ((vardata = AST_LIST_REMOVE_HEAD(&frame->varshead, entries))) {
00284       if (chan)
00285          pbx_builtin_setvar_helper(chan, ast_var_name(vardata), NULL);  
00286       ast_var_delete(vardata);
00287    }
00288 
00289    ast_free(frame);
00290 }
00291 
00292 static struct gosub_stack_frame *gosub_allocate_frame(const char *context, const char *extension, int priority, unsigned char arguments)
00293 {
00294    struct gosub_stack_frame *new = NULL;
00295    int len_extension = strlen(extension), len_context = strlen(context);
00296 
00297    if ((new = ast_calloc(1, sizeof(*new) + 2 + len_extension + len_context))) {
00298       AST_LIST_HEAD_INIT_NOLOCK(&new->varshead);
00299       strcpy(new->extension, extension);
00300       new->context = new->extension + len_extension + 1;
00301       strcpy(new->context, context);
00302       new->priority = priority;
00303       new->arguments = arguments;
00304    }
00305    return new;
00306 }
00307 
00308 static void gosub_free(void *data)
00309 {
00310    struct gosub_stack_list *oldlist = data;
00311    struct gosub_stack_frame *oldframe;
00312 
00313    AST_LIST_LOCK(oldlist);
00314    while ((oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries))) {
00315       gosub_release_frame(NULL, oldframe);
00316    }
00317    AST_LIST_UNLOCK(oldlist);
00318    AST_LIST_HEAD_DESTROY(oldlist);
00319    ast_free(oldlist);
00320 }
00321 
00322 static int pop_exec(struct ast_channel *chan, const char *data)
00323 {
00324    struct ast_datastore *stack_store;
00325    struct gosub_stack_frame *oldframe;
00326    struct gosub_stack_list *oldlist;
00327    int res = 0;
00328 
00329    ast_channel_lock(chan);
00330    if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
00331       ast_log(LOG_WARNING, "%s called with no gosub stack allocated.\n", app_pop);
00332       ast_channel_unlock(chan);
00333       return 0;
00334    }
00335 
00336    oldlist = stack_store->data;
00337    AST_LIST_LOCK(oldlist);
00338    oldframe = AST_LIST_FIRST(oldlist);
00339    if (oldframe) {
00340       if (oldframe->is_special) {
00341          ast_debug(1, "%s attempted to pop special return location.\n", app_pop);
00342 
00343          /* Abort the special routine dialplan execution.  Dialplan programming error. */
00344          res = -1;
00345       } else {
00346          AST_LIST_REMOVE_HEAD(oldlist, entries);
00347          gosub_release_frame(chan, oldframe);
00348       }
00349    } else {
00350       ast_debug(1, "%s called with an empty gosub stack\n", app_pop);
00351    }
00352    AST_LIST_UNLOCK(oldlist);
00353    ast_channel_unlock(chan);
00354    return res;
00355 }
00356 
00357 static int return_exec(struct ast_channel *chan, const char *data)
00358 {
00359    struct ast_datastore *stack_store;
00360    struct gosub_stack_frame *oldframe;
00361    struct gosub_stack_list *oldlist;
00362    const char *retval = data;
00363    int res = 0;
00364 
00365    ast_channel_lock(chan);
00366    if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
00367       ast_log(LOG_ERROR, "Return without Gosub: stack is unallocated\n");
00368       ast_channel_unlock(chan);
00369       return -1;
00370    }
00371 
00372    oldlist = stack_store->data;
00373    AST_LIST_LOCK(oldlist);
00374    oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
00375    AST_LIST_UNLOCK(oldlist);
00376 
00377    if (!oldframe) {
00378       ast_log(LOG_ERROR, "Return without Gosub: stack is empty\n");
00379       ast_channel_unlock(chan);
00380       return -1;
00381    }
00382    if (oldframe->is_special) {
00383       /* Exit from special routine. */
00384       res = -1;
00385    }
00386 
00387    /*
00388     * We cannot use ast_explicit_goto() because we MUST restore
00389     * what was there before.  Channels that do not have a PBX may
00390     * not have the context or exten set.
00391     */
00392    ast_channel_context_set(chan, oldframe->context);
00393    ast_channel_exten_set(chan, oldframe->extension);
00394    if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP)) {
00395       --oldframe->priority;
00396    }
00397    ast_channel_priority_set(chan, oldframe->priority);
00398 
00399    gosub_release_frame(chan, oldframe);
00400 
00401    /* Set a return value, if any */
00402    pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", S_OR(retval, ""));
00403    ast_channel_unlock(chan);
00404    return res;
00405 }
00406 
00407 /*!
00408  * \internal
00409  * \brief Add missing context and/or exten to Gosub application argument string.
00410  * \since 11.0
00411  *
00412  * \param chan Channel to obtain context/exten.
00413  * \param args Gosub application argument string.
00414  *
00415  * \details
00416  * Fills in the optional context and exten from the given channel.
00417  * Convert: [[context,]exten,]priority[(arg1[,...][,argN])]
00418  * To: context,exten,priority[(arg1[,...][,argN])]
00419  *
00420  * \retval expanded Gosub argument string on success.  Must be freed.
00421  * \retval NULL on error.
00422  *
00423  * \note The parsing needs to be kept in sync with the
00424  * gosub_exec() argument format.
00425  */
00426 static const char *expand_gosub_args(struct ast_channel *chan, const char *args)
00427 {
00428    int len;
00429    char *parse;
00430    char *label;
00431    char *new_args;
00432    const char *context;
00433    const char *exten;
00434    const char *pri;
00435 
00436    /* Separate the context,exten,pri from the optional routine arguments. */
00437    parse = ast_strdupa(args);
00438    label = strsep(&parse, "(");
00439    if (parse) {
00440       char *endparen;
00441 
00442       endparen = strrchr(parse, ')');
00443       if (endparen) {
00444          *endparen = '\0';
00445       } else {
00446          ast_log(LOG_WARNING, "Ouch.  No closing paren: '%s'?\n", args);
00447       }
00448    }
00449 
00450    /* Split context,exten,pri */
00451    context = strsep(&label, ",");
00452    exten = strsep(&label, ",");
00453    pri = strsep(&label, ",");
00454    if (!exten) {
00455       /* Only a priority in this one */
00456       pri = context;
00457       exten = NULL;
00458       context = NULL;
00459    } else if (!pri) {
00460       /* Only an extension and priority in this one */
00461       pri = exten;
00462       exten = context;
00463       context = NULL;
00464    }
00465 
00466    ast_channel_lock(chan);
00467    if (ast_strlen_zero(exten)) {
00468       exten = ast_channel_exten(chan);
00469    }
00470    if (ast_strlen_zero(context)) {
00471       context = ast_channel_context(chan);
00472    }
00473    len = strlen(context) + strlen(exten) + strlen(pri) + 3;
00474    if (!ast_strlen_zero(parse)) {
00475       len += 2 + strlen(parse);
00476    }
00477    new_args = ast_malloc(len);
00478    if (new_args) {
00479       if (ast_strlen_zero(parse)) {
00480          snprintf(new_args, len, "%s,%s,%s", context, exten, pri);
00481       } else {
00482          snprintf(new_args, len, "%s,%s,%s(%s)", context, exten, pri, parse);
00483       }
00484    }
00485    ast_channel_unlock(chan);
00486 
00487    ast_debug(4, "Gosub args:%s new_args:%s\n", args, new_args ? new_args : "");
00488 
00489    return new_args;
00490 }
00491 
00492 static int gosub_exec(struct ast_channel *chan, const char *data)
00493 {
00494    struct ast_datastore *stack_store;
00495    struct gosub_stack_list *oldlist;
00496    struct gosub_stack_frame *newframe;
00497    struct gosub_stack_frame *lastframe;
00498    char argname[15];
00499    char *parse;
00500    char *label;
00501    char *caller_id;
00502    char *orig_context;
00503    char *orig_exten;
00504    char *dest_context;
00505    char *dest_exten;
00506    int orig_priority;
00507    int dest_priority;
00508    int i;
00509    int max_argc = 0;
00510    AST_DECLARE_APP_ARGS(args2,
00511       AST_APP_ARG(argval)[100];
00512    );
00513 
00514    if (ast_strlen_zero(data)) {
00515       ast_log(LOG_ERROR, "%s requires an argument: %s([[context,]exten,]priority[(arg1[,...][,argN])])\n", app_gosub, app_gosub);
00516       return -1;
00517    }
00518 
00519    /*
00520     * Separate the arguments from the label
00521     *
00522     * NOTE:  You cannot use ast_app_separate_args for this, because
00523     * '(' cannot be used as a delimiter.
00524     */
00525    parse = ast_strdupa(data);
00526    label = strsep(&parse, "(");
00527    if (parse) {
00528       char *endparen;
00529 
00530       endparen = strrchr(parse, ')');
00531       if (endparen) {
00532          *endparen = '\0';
00533       } else {
00534          ast_log(LOG_WARNING, "Ouch.  No closing paren: '%s'?\n", data);
00535       }
00536       AST_STANDARD_RAW_ARGS(args2, parse);
00537    } else {
00538       args2.argc = 0;
00539    }
00540 
00541    ast_channel_lock(chan);
00542    orig_context = ast_strdupa(ast_channel_context(chan));
00543    orig_exten = ast_strdupa(ast_channel_exten(chan));
00544    orig_priority = ast_channel_priority(chan);
00545    ast_channel_unlock(chan);
00546 
00547    if (ast_parseable_goto(chan, label)) {
00548       ast_log(LOG_ERROR, "%s address is invalid: '%s'\n", app_gosub, data);
00549       goto error_exit;
00550    }
00551 
00552    ast_channel_lock(chan);
00553    dest_context = ast_strdupa(ast_channel_context(chan));
00554    dest_exten = ast_strdupa(ast_channel_exten(chan));
00555    dest_priority = ast_channel_priority(chan);
00556    if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP)) {
00557       ++dest_priority;
00558    }
00559    caller_id = S_COR(ast_channel_caller(chan)->id.number.valid,
00560       ast_channel_caller(chan)->id.number.str, NULL);
00561    if (caller_id) {
00562       caller_id = ast_strdupa(caller_id);
00563    }
00564    ast_channel_unlock(chan);
00565 
00566    if (!ast_exists_extension(chan, dest_context, dest_exten, dest_priority, caller_id)) {
00567       ast_log(LOG_ERROR, "Attempt to reach a non-existent destination for %s: (Context:%s, Extension:%s, Priority:%d)\n",
00568          app_gosub, dest_context, dest_exten, dest_priority);
00569       goto error_exit;
00570    }
00571 
00572    /* Now we know that we're going to a new location */
00573 
00574    ast_channel_lock(chan);
00575 
00576    /* Find stack datastore return list. */
00577    if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
00578       ast_debug(1, "Channel %s has no datastore, so we're allocating one.\n",
00579          ast_channel_name(chan));
00580       stack_store = ast_datastore_alloc(&stack_info, NULL);
00581       if (!stack_store) {
00582          ast_log(LOG_ERROR, "Unable to allocate new datastore.  %s failed.\n",
00583             app_gosub);
00584          goto error_exit_locked;
00585       }
00586 
00587       oldlist = ast_calloc(1, sizeof(*oldlist));
00588       if (!oldlist) {
00589          ast_log(LOG_ERROR, "Unable to allocate datastore list head.  %s failed.\n",
00590             app_gosub);
00591          ast_datastore_free(stack_store);
00592          goto error_exit_locked;
00593       }
00594       AST_LIST_HEAD_INIT(oldlist);
00595 
00596       stack_store->data = oldlist;
00597       ast_channel_datastore_add(chan, stack_store);
00598    } else {
00599       oldlist = stack_store->data;
00600    }
00601 
00602    if ((lastframe = AST_LIST_FIRST(oldlist))) {
00603       max_argc = lastframe->arguments;
00604    }
00605 
00606    /* Mask out previous Gosub arguments in this invocation */
00607    if (args2.argc > max_argc) {
00608       max_argc = args2.argc;
00609    }
00610 
00611    /* Create the return address */
00612    newframe = gosub_allocate_frame(orig_context, orig_exten, orig_priority + 1, max_argc);
00613    if (!newframe) {
00614       goto error_exit_locked;
00615    }
00616 
00617    /* Set our arguments */
00618    for (i = 0; i < max_argc; i++) {
00619       snprintf(argname, sizeof(argname), "ARG%d", i + 1);
00620       frame_set_var(chan, newframe, argname, i < args2.argc ? args2.argval[i] : "");
00621       ast_debug(1, "Setting '%s' to '%s'\n", argname, i < args2.argc ? args2.argval[i] : "");
00622    }
00623    snprintf(argname, sizeof(argname), "%u", args2.argc);
00624    frame_set_var(chan, newframe, "ARGC", argname);
00625 
00626    /* And finally, save our return address */
00627    AST_LIST_LOCK(oldlist);
00628    AST_LIST_INSERT_HEAD(oldlist, newframe, entries);
00629    AST_LIST_UNLOCK(oldlist);
00630    ast_channel_unlock(chan);
00631 
00632    return 0;
00633 
00634 error_exit:
00635    ast_channel_lock(chan);
00636 
00637 error_exit_locked:
00638    /* Restore the original dialplan location. */
00639    ast_channel_context_set(chan, orig_context);
00640    ast_channel_exten_set(chan, orig_exten);
00641    ast_channel_priority_set(chan, orig_priority);
00642    ast_channel_unlock(chan);
00643    return -1;
00644 }
00645 
00646 static int gosubif_exec(struct ast_channel *chan, const char *data)
00647 {
00648    char *args;
00649    int res=0;
00650    AST_DECLARE_APP_ARGS(cond,
00651       AST_APP_ARG(ition);
00652       AST_APP_ARG(labels);
00653    );
00654    AST_DECLARE_APP_ARGS(label,
00655       AST_APP_ARG(iftrue);
00656       AST_APP_ARG(iffalse);
00657    );
00658 
00659    if (ast_strlen_zero(data)) {
00660       ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
00661       return 0;
00662    }
00663 
00664    args = ast_strdupa(data);
00665    AST_NONSTANDARD_RAW_ARGS(cond, args, '?');
00666    if (cond.argc != 2) {
00667       ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
00668       return 0;
00669    }
00670 
00671    AST_NONSTANDARD_RAW_ARGS(label, cond.labels, ':');
00672 
00673    if (pbx_checkcondition(cond.ition)) {
00674       if (!ast_strlen_zero(label.iftrue))
00675          res = gosub_exec(chan, label.iftrue);
00676    } else if (!ast_strlen_zero(label.iffalse)) {
00677       res = gosub_exec(chan, label.iffalse);
00678    }
00679 
00680    return res;
00681 }
00682 
00683 static int local_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00684 {
00685    struct ast_datastore *stack_store;
00686    struct gosub_stack_list *oldlist;
00687    struct gosub_stack_frame *frame;
00688    struct ast_var_t *variables;
00689 
00690    if (!chan) {
00691       ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
00692       return -1;
00693    }
00694 
00695    ast_channel_lock(chan);
00696    if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
00697       ast_channel_unlock(chan);
00698       return -1;
00699    }
00700 
00701    oldlist = stack_store->data;
00702    AST_LIST_LOCK(oldlist);
00703    if (!(frame = AST_LIST_FIRST(oldlist))) {
00704       /* Not within a Gosub routine */
00705       AST_LIST_UNLOCK(oldlist);
00706       ast_channel_unlock(chan);
00707       return -1;
00708    }
00709 
00710    AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
00711       if (!strcmp(data, ast_var_name(variables))) {
00712          const char *tmp;
00713          tmp = pbx_builtin_getvar_helper(chan, data);
00714          ast_copy_string(buf, S_OR(tmp, ""), len);
00715          break;
00716       }
00717    }
00718    AST_LIST_UNLOCK(oldlist);
00719    ast_channel_unlock(chan);
00720    return 0;
00721 }
00722 
00723 static int local_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
00724 {
00725    struct ast_datastore *stack_store;
00726    struct gosub_stack_list *oldlist;
00727    struct gosub_stack_frame *frame;
00728 
00729    if (!chan) {
00730       ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
00731       return -1;
00732    }
00733 
00734    ast_channel_lock(chan);
00735    if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
00736       ast_log(LOG_ERROR, "Tried to set LOCAL(%s), but we aren't within a Gosub routine\n", var);
00737       ast_channel_unlock(chan);
00738       return -1;
00739    }
00740 
00741    oldlist = stack_store->data;
00742    AST_LIST_LOCK(oldlist);
00743    frame = AST_LIST_FIRST(oldlist);
00744 
00745    if (frame) {
00746       frame_set_var(chan, frame, var, value);
00747    }
00748 
00749    AST_LIST_UNLOCK(oldlist);
00750    ast_channel_unlock(chan);
00751 
00752    return 0;
00753 }
00754 
00755 static struct ast_custom_function local_function = {
00756    .name = "LOCAL",
00757    .write = local_write,
00758    .read = local_read,
00759 };
00760 
00761 static int peek_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00762 {
00763    int found = 0, n;
00764    struct ast_var_t *variables;
00765    AST_DECLARE_APP_ARGS(args,
00766       AST_APP_ARG(n);
00767       AST_APP_ARG(name);
00768    );
00769 
00770    if (!chan) {
00771       ast_log(LOG_ERROR, "LOCAL_PEEK must be called on an active channel\n");
00772       return -1;
00773    }
00774 
00775    AST_STANDARD_RAW_ARGS(args, data);
00776 
00777    if (ast_strlen_zero(args.n) || ast_strlen_zero(args.name)) {
00778       ast_log(LOG_ERROR, "LOCAL_PEEK requires parameters n and varname\n");
00779       return -1;
00780    }
00781 
00782    n = atoi(args.n);
00783    *buf = '\0';
00784 
00785    ast_channel_lock(chan);
00786    AST_LIST_TRAVERSE(ast_channel_varshead(chan), variables, entries) {
00787       if (!strcmp(args.name, ast_var_name(variables)) && ++found > n) {
00788          ast_copy_string(buf, ast_var_value(variables), len);
00789          break;
00790       }
00791    }
00792    ast_channel_unlock(chan);
00793    return 0;
00794 }
00795 
00796 static struct ast_custom_function peek_function = {
00797    .name = "LOCAL_PEEK",
00798    .read = peek_read,
00799 };
00800 
00801 static int stackpeek_read(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **str, ssize_t len)
00802 {
00803    struct ast_datastore *stack_store;
00804    struct gosub_stack_list *oldlist;
00805    struct gosub_stack_frame *frame;
00806    int n;
00807    AST_DECLARE_APP_ARGS(args,
00808       AST_APP_ARG(n);
00809       AST_APP_ARG(which);
00810       AST_APP_ARG(suppress);
00811    );
00812 
00813    if (!chan) {
00814       ast_log(LOG_ERROR, "STACK_PEEK must be called on an active channel\n");
00815       return -1;
00816    }
00817 
00818    data = ast_strdupa(data);
00819    AST_STANDARD_APP_ARGS(args, data);
00820 
00821    if (ast_strlen_zero(args.n) || ast_strlen_zero(args.which)) {
00822       ast_log(LOG_ERROR, "STACK_PEEK requires parameters n and which\n");
00823       return -1;
00824    }
00825 
00826    n = atoi(args.n);
00827    if (n <= 0) {
00828       ast_log(LOG_ERROR, "STACK_PEEK must be called with a positive peek value\n");
00829       return -1;
00830    }
00831 
00832    ast_channel_lock(chan);
00833    if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
00834       if (!ast_true(args.suppress)) {
00835          ast_log(LOG_ERROR, "STACK_PEEK called on a channel without a gosub stack\n");
00836       }
00837       ast_channel_unlock(chan);
00838       return -1;
00839    }
00840 
00841    oldlist = stack_store->data;
00842 
00843    AST_LIST_LOCK(oldlist);
00844    AST_LIST_TRAVERSE(oldlist, frame, entries) {
00845       if (--n == 0) {
00846          break;
00847       }
00848    }
00849 
00850    if (!frame) {
00851       /* Too deep */
00852       if (!ast_true(args.suppress)) {
00853          ast_log(LOG_ERROR, "Stack peek of '%s' is more stack frames than I have\n", args.n);
00854       }
00855       AST_LIST_UNLOCK(oldlist);
00856       ast_channel_unlock(chan);
00857       return -1;
00858    }
00859 
00860    args.which = ast_skip_blanks(args.which);
00861 
00862    switch (args.which[0]) {
00863    case 'l': /* label */
00864       ast_str_set(str, len, "%s,%s,%d", frame->context, frame->extension, frame->priority - 1);
00865       break;
00866    case 'c': /* context */
00867       ast_str_set(str, len, "%s", frame->context);
00868       break;
00869    case 'e': /* extension */
00870       ast_str_set(str, len, "%s", frame->extension);
00871       break;
00872    case 'p': /* priority */
00873       ast_str_set(str, len, "%d", frame->priority - 1);
00874       break;
00875    default:
00876       ast_log(LOG_ERROR, "Unknown argument '%s' to STACK_PEEK\n", args.which);
00877       break;
00878    }
00879 
00880    AST_LIST_UNLOCK(oldlist);
00881    ast_channel_unlock(chan);
00882 
00883    return 0;
00884 }
00885 
00886 static struct ast_custom_function stackpeek_function = {
00887    .name = "STACK_PEEK",
00888    .read2 = stackpeek_read,
00889 };
00890 
00891 /*!
00892  * \internal
00893  * \brief Pop stack frames until remove a special return location.
00894  * \since 11.0
00895  *
00896  * \param chan Channel to balance stack on.
00897  *
00898  * \note The channel is already locked when called.
00899  *
00900  * \return Nothing
00901  */
00902 static void balance_stack(struct ast_channel *chan)
00903 {
00904    struct ast_datastore *stack_store;
00905    struct gosub_stack_list *oldlist;
00906    struct gosub_stack_frame *oldframe;
00907    int found;
00908 
00909    stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
00910    if (!stack_store) {
00911       ast_log(LOG_WARNING, "No %s stack allocated.\n", app_gosub);
00912       return;
00913    }
00914 
00915    oldlist = stack_store->data;
00916    AST_LIST_LOCK(oldlist);
00917    do {
00918       oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
00919       if (!oldframe) {
00920          break;
00921       }
00922       found = oldframe->is_special;
00923       gosub_release_frame(chan, oldframe);
00924    } while (!found);
00925    AST_LIST_UNLOCK(oldlist);
00926 }
00927 
00928 /*!
00929  * \internal
00930  * \brief Run a subroutine on a channel.
00931  * \since 11.0
00932  *
00933  * \note Absolutely _NO_ channel locks should be held before calling this function.
00934  *
00935  * \param chan Channel to execute subroutine on.
00936  * \param sub_args Gosub application argument string.
00937  * \param ignore_hangup TRUE if a hangup does not stop execution of the routine.
00938  *
00939  * \retval 0 success
00940  * \retval -1 on error
00941  */
00942 static int gosub_run(struct ast_channel *chan, const char *sub_args, int ignore_hangup)
00943 {
00944    const char *saved_context;
00945    const char *saved_exten;
00946    int saved_priority;
00947    int saved_hangup_flags;
00948    int saved_autoloopflag;
00949    int res;
00950 
00951    ast_channel_lock(chan);
00952 
00953    ast_verb(3, "%s Internal %s(%s) start\n",
00954       ast_channel_name(chan), app_gosub, sub_args);
00955 
00956    /* Save non-hangup softhangup flags. */
00957    saved_hangup_flags = ast_channel_softhangup_internal_flag(chan)
00958       & (AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE);
00959    if (saved_hangup_flags) {
00960       ast_channel_clear_softhangup(chan,
00961          AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE);
00962    }
00963 
00964    /* Save autoloop flag */
00965    saved_autoloopflag = ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
00966    ast_set_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
00967 
00968    /* Save current dialplan location */
00969    saved_context = ast_strdupa(ast_channel_context(chan));
00970    saved_exten = ast_strdupa(ast_channel_exten(chan));
00971    saved_priority = ast_channel_priority(chan);
00972 
00973    ast_debug(4, "%s Original location: %s,%s,%d\n", ast_channel_name(chan),
00974       saved_context, saved_exten, saved_priority);
00975 
00976    ast_channel_unlock(chan);
00977    res = gosub_exec(chan, sub_args);
00978    ast_debug(4, "%s exited with status %d\n", app_gosub, res);
00979    ast_channel_lock(chan);
00980    if (!res) {
00981       struct ast_datastore *stack_store;
00982 
00983       /* Mark the return location as special. */
00984       stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
00985       if (!stack_store) {
00986          /* Should never happen! */
00987          ast_log(LOG_ERROR, "No %s stack!\n", app_gosub);
00988          res = -1;
00989       } else {
00990          struct gosub_stack_list *oldlist;
00991          struct gosub_stack_frame *cur;
00992 
00993          oldlist = stack_store->data;
00994          cur = AST_LIST_FIRST(oldlist);
00995          cur->is_special = 1;
00996       }
00997    }
00998    if (!res) {
00999       int found = 0; /* set if we find at least one match */
01000 
01001       /*
01002        * Run gosub body autoloop.
01003        *
01004        * Note that this loop is inverted from the normal execution
01005        * loop because we just executed the Gosub application as the
01006        * first extension of the autoloop.
01007        */
01008       do {
01009          /* Check for hangup. */
01010          if (ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_UNBRIDGE) {
01011             saved_hangup_flags |= AST_SOFTHANGUP_UNBRIDGE;
01012             ast_channel_clear_softhangup(chan, AST_SOFTHANGUP_UNBRIDGE);
01013          }
01014          if (ast_check_hangup(chan)) {
01015             if (ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO) {
01016                ast_log(LOG_ERROR, "%s An async goto just messed up our execution location.\n",
01017                   ast_channel_name(chan));
01018                break;
01019             }
01020             if (!ignore_hangup) {
01021                break;
01022             }
01023          }
01024 
01025          /* Next dialplan priority. */
01026          ast_channel_priority_set(chan, ast_channel_priority(chan) + 1);
01027 
01028          ast_channel_unlock(chan);
01029          res = ast_spawn_extension(chan, ast_channel_context(chan),
01030             ast_channel_exten(chan), ast_channel_priority(chan),
01031             S_COR(ast_channel_caller(chan)->id.number.valid,
01032                ast_channel_caller(chan)->id.number.str, NULL),
01033             &found, 1);
01034          ast_channel_lock(chan);
01035       } while (!res);
01036       if (found && res) {
01037          /* Something bad happened, or a hangup has been requested. */
01038          ast_debug(1, "Spawn extension (%s,%s,%d) exited with %d on '%s'\n",
01039             ast_channel_context(chan), ast_channel_exten(chan),
01040             ast_channel_priority(chan), res, ast_channel_name(chan));
01041          ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n",
01042             ast_channel_context(chan), ast_channel_exten(chan),
01043             ast_channel_priority(chan), ast_channel_name(chan));
01044       }
01045 
01046       /* Did the routine return? */
01047       if (ast_channel_priority(chan) == saved_priority
01048          && !strcmp(ast_channel_context(chan), saved_context)
01049          && !strcmp(ast_channel_exten(chan), saved_exten)) {
01050          ast_verb(3, "%s Internal %s(%s) complete GOSUB_RETVAL=%s\n",
01051             ast_channel_name(chan), app_gosub, sub_args,
01052             S_OR(pbx_builtin_getvar_helper(chan, "GOSUB_RETVAL"), ""));
01053       } else {
01054          ast_log(LOG_NOTICE, "%s Abnormal '%s(%s)' exit.  Popping routine return locations.\n",
01055             ast_channel_name(chan), app_gosub, sub_args);
01056          balance_stack(chan);
01057          pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", "");
01058       }
01059 
01060       /* We executed the requested subroutine to the best of our ability. */
01061       res = 0;
01062    }
01063 
01064    ast_debug(4, "%s Ending location: %s,%s,%d\n", ast_channel_name(chan),
01065       ast_channel_context(chan), ast_channel_exten(chan),
01066       ast_channel_priority(chan));
01067 
01068    /* Restore dialplan location */
01069    if (!(ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO)) {
01070       ast_channel_context_set(chan, saved_context);
01071       ast_channel_exten_set(chan, saved_exten);
01072       ast_channel_priority_set(chan, saved_priority);
01073    }
01074 
01075    /* Restore autoloop flag */
01076    ast_set2_flag(ast_channel_flags(chan), saved_autoloopflag, AST_FLAG_IN_AUTOLOOP);
01077 
01078    /* Restore non-hangup softhangup flags. */
01079    if (saved_hangup_flags) {
01080       ast_softhangup_nolock(chan, saved_hangup_flags);
01081    }
01082 
01083    ast_channel_unlock(chan);
01084 
01085    return res;
01086 }
01087 
01088 static int handle_gosub(struct ast_channel *chan, AGI *agi, int argc, const char * const *argv)
01089 {
01090    int res;
01091    int priority;
01092    int old_autoloopflag;
01093    int old_priority;
01094    const char *old_context;
01095    const char *old_extension;
01096    char *gosub_args;
01097 
01098    if (argc < 4 || argc > 5) {
01099       return RESULT_SHOWUSAGE;
01100    }
01101 
01102    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] : "");
01103 
01104    if (sscanf(argv[3], "%30d", &priority) != 1 || priority < 1) {
01105       /* Lookup the priority label */
01106       priority = ast_findlabel_extension(chan, argv[1], argv[2], argv[3],
01107          S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL));
01108       if (priority < 0) {
01109          ast_log(LOG_ERROR, "Priority '%s' not found in '%s@%s'\n", argv[3], argv[2], argv[1]);
01110          ast_agi_send(agi->fd, chan, "200 result=-1 Gosub label not found\n");
01111          return RESULT_FAILURE;
01112       }
01113    } else if (!ast_exists_extension(chan, argv[1], argv[2], priority,
01114       S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
01115       ast_agi_send(agi->fd, chan, "200 result=-1 Gosub label not found\n");
01116       return RESULT_FAILURE;
01117    }
01118 
01119    if (argc == 5) {
01120       if (ast_asprintf(&gosub_args, "%s,%s,%d(%s)", argv[1], argv[2], priority, argv[4]) < 0) {
01121          gosub_args = NULL;
01122       }
01123    } else {
01124       if (ast_asprintf(&gosub_args, "%s,%s,%d", argv[1], argv[2], priority) < 0) {
01125          gosub_args = NULL;
01126       }
01127    }
01128    if (!gosub_args) {
01129       ast_agi_send(agi->fd, chan, "503 result=-2 Memory allocation failure\n");
01130       return RESULT_FAILURE;
01131    }
01132 
01133    ast_channel_lock(chan);
01134 
01135    ast_verb(3, "%s AGI %s(%s) start\n", ast_channel_name(chan), app_gosub, gosub_args);
01136 
01137    /* Save autoloop flag */
01138    old_autoloopflag = ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
01139    ast_set_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
01140 
01141    /* Save previous location, since we're going to change it */
01142    old_context = ast_strdupa(ast_channel_context(chan));
01143    old_extension = ast_strdupa(ast_channel_exten(chan));
01144    old_priority = ast_channel_priority(chan);
01145 
01146    ast_debug(4, "%s Original location: %s,%s,%d\n", ast_channel_name(chan),
01147       old_context, old_extension, old_priority);
01148    ast_channel_unlock(chan);
01149 
01150    res = gosub_exec(chan, gosub_args);
01151    if (!res) {
01152       struct ast_datastore *stack_store;
01153 
01154       /* Mark the return location as special. */
01155       ast_channel_lock(chan);
01156       stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
01157       if (!stack_store) {
01158          /* Should never happen! */
01159          ast_log(LOG_ERROR, "No %s stack!\n", app_gosub);
01160          res = -1;
01161       } else {
01162          struct gosub_stack_list *oldlist;
01163          struct gosub_stack_frame *cur;
01164 
01165          oldlist = stack_store->data;
01166          cur = AST_LIST_FIRST(oldlist);
01167          cur->is_special = 1;
01168       }
01169       ast_channel_unlock(chan);
01170    }
01171    if (!res) {
01172       struct ast_pbx *pbx;
01173       struct ast_pbx_args args;
01174       int abnormal_exit;
01175 
01176       memset(&args, 0, sizeof(args));
01177       args.no_hangup_chan = 1;
01178 
01179       ast_channel_lock(chan);
01180 
01181       /* Next dialplan priority. */
01182       ast_channel_priority_set(chan, ast_channel_priority(chan) + 1);
01183 
01184       /* Suppress warning about PBX already existing */
01185       pbx = ast_channel_pbx(chan);
01186       ast_channel_pbx_set(chan, NULL);
01187       ast_channel_unlock(chan);
01188 
01189       ast_agi_send(agi->fd, chan, "100 result=0 Trying...\n");
01190       ast_pbx_run_args(chan, &args);
01191 
01192       ast_channel_lock(chan);
01193       ast_free(ast_channel_pbx(chan));
01194       ast_channel_pbx_set(chan, pbx);
01195 
01196       /* Did the routine return? */
01197       if (ast_channel_priority(chan) == old_priority
01198          && !strcmp(ast_channel_context(chan), old_context)
01199          && !strcmp(ast_channel_exten(chan), old_extension)) {
01200          ast_verb(3, "%s AGI %s(%s) complete GOSUB_RETVAL=%s\n",
01201             ast_channel_name(chan), app_gosub, gosub_args,
01202             S_OR(pbx_builtin_getvar_helper(chan, "GOSUB_RETVAL"), ""));
01203          abnormal_exit = 0;
01204       } else {
01205          ast_log(LOG_NOTICE, "%s Abnormal AGI %s(%s) exit.  Popping routine return locations.\n",
01206             ast_channel_name(chan), app_gosub, gosub_args);
01207          balance_stack(chan);
01208          pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", "");
01209          abnormal_exit = 1;
01210       }
01211       ast_channel_unlock(chan);
01212 
01213       ast_agi_send(agi->fd, chan, "200 result=0 Gosub complete%s\n",
01214          abnormal_exit ? " (abnormal exit)" : "");
01215    } else {
01216       ast_agi_send(agi->fd, chan, "200 result=%d Gosub failed\n", res);
01217    }
01218 
01219    /* Must use free because the memory was allocated by asprintf(). */
01220    free(gosub_args);
01221 
01222    ast_channel_lock(chan);
01223    ast_debug(4, "%s Ending location: %s,%s,%d\n", ast_channel_name(chan),
01224       ast_channel_context(chan), ast_channel_exten(chan),
01225       ast_channel_priority(chan));
01226 
01227    /* Restore previous location */
01228    ast_channel_context_set(chan, old_context);
01229    ast_channel_exten_set(chan, old_extension);
01230    ast_channel_priority_set(chan, old_priority);
01231 
01232    /* Restore autoloop flag */
01233    ast_set2_flag(ast_channel_flags(chan), old_autoloopflag, AST_FLAG_IN_AUTOLOOP);
01234    ast_channel_unlock(chan);
01235 
01236    return RESULT_SUCCESS;
01237 }
01238 
01239 static struct agi_command gosub_agi_command =
01240    { { "gosub", NULL }, handle_gosub, NULL, NULL, 0 };
01241 
01242 static int unload_module(void)
01243 {
01244    ast_install_stack_functions(NULL);
01245 
01246    ast_agi_unregister(ast_module_info->self, &gosub_agi_command);
01247 
01248    ast_unregister_application(app_return);
01249    ast_unregister_application(app_pop);
01250    ast_unregister_application(app_gosubif);
01251    ast_unregister_application(app_gosub);
01252    ast_custom_function_unregister(&local_function);
01253    ast_custom_function_unregister(&peek_function);
01254    ast_custom_function_unregister(&stackpeek_function);
01255 
01256    return 0;
01257 }
01258 
01259 static int load_module(void)
01260 {
01261    /* Setup the stack application callback functions. */
01262    static struct ast_app_stack_funcs funcs = {
01263       .run_sub = gosub_run,
01264       .expand_sub_args = expand_gosub_args,
01265    };
01266 
01267    ast_agi_register(ast_module_info->self, &gosub_agi_command);
01268 
01269    ast_register_application_xml(app_pop, pop_exec);
01270    ast_register_application_xml(app_return, return_exec);
01271    ast_register_application_xml(app_gosubif, gosubif_exec);
01272    ast_register_application_xml(app_gosub, gosub_exec);
01273    ast_custom_function_register(&local_function);
01274    ast_custom_function_register(&peek_function);
01275    ast_custom_function_register(&stackpeek_function);
01276 
01277    funcs.module = ast_module_info->self,
01278    ast_install_stack_functions(&funcs);
01279 
01280    return 0;
01281 }
01282 
01283 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT | AST_MODFLAG_LOAD_ORDER, "Dialplan subroutines (Gosub, Return, etc)",
01284       .load = load_module,
01285       .unload = unload_module,
01286       .load_pri = AST_MODPRI_APP_DEPEND,
01287       .nonoptreq = "res_agi",
01288       );