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

Generated on Thu Apr 16 06:27:12 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6