Mon Apr 28 2014 10:05:35

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>res_agi</use>
00030    <support_level>core</support_level>
00031  ***/
00032 
00033 #include "asterisk.h"
00034  
00035 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 411313 $")
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 * const app_gosub = "Gosub";
00209 static const char * const app_gosubif = "GosubIf";
00210 static const char * const app_return = "Return";
00211 static const char * const 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    unsigned int is_agi:1;
00227    char *context;
00228    char extension[0];
00229 };
00230 
00231 static int frame_set_var(struct ast_channel *chan, struct gosub_stack_frame *frame, const char *var, const char *value)
00232 {
00233    struct ast_var_t *variables;
00234    int found = 0;
00235 
00236    /* Does this variable already exist? */
00237    AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
00238       if (!strcmp(var, ast_var_name(variables))) {
00239          found = 1;
00240          break;
00241       }
00242    }
00243 
00244    if (!found) {
00245       if ((variables = ast_var_assign(var, ""))) {
00246          AST_LIST_INSERT_HEAD(&frame->varshead, variables, entries);
00247       }
00248       pbx_builtin_pushvar_helper(chan, var, value);
00249    } else {
00250       pbx_builtin_setvar_helper(chan, var, value);
00251    }
00252 
00253    manager_event(EVENT_FLAG_DIALPLAN, "VarSet",
00254       "Channel: %s\r\n"
00255       "Variable: LOCAL(%s)\r\n"
00256       "Value: %s\r\n"
00257       "Uniqueid: %s\r\n",
00258       chan->name, var, value, chan->uniqueid);
00259    return 0;
00260 }
00261 
00262 static void gosub_release_frame(struct ast_channel *chan, struct gosub_stack_frame *frame)
00263 {
00264    struct ast_var_t *vardata;
00265 
00266    /* If chan is not defined, then we're calling it as part of gosub_free,
00267     * and the channel variables will be deallocated anyway.  Otherwise, we're
00268     * just releasing a single frame, so we need to clean up the arguments for
00269     * that frame, so that we re-expose the variables from the previous frame
00270     * that were hidden by this one.
00271     */
00272    while ((vardata = AST_LIST_REMOVE_HEAD(&frame->varshead, entries))) {
00273       if (chan)
00274          pbx_builtin_setvar_helper(chan, ast_var_name(vardata), NULL);  
00275       ast_var_delete(vardata);
00276    }
00277 
00278    ast_free(frame);
00279 }
00280 
00281 static struct gosub_stack_frame *gosub_allocate_frame(const char *context, const char *extension, int priority, unsigned char arguments)
00282 {
00283    struct gosub_stack_frame *new = NULL;
00284    int len_extension = strlen(extension), len_context = strlen(context);
00285 
00286    if ((new = ast_calloc(1, sizeof(*new) + 2 + len_extension + len_context))) {
00287       AST_LIST_HEAD_INIT_NOLOCK(&new->varshead);
00288       strcpy(new->extension, extension);
00289       new->context = new->extension + len_extension + 1;
00290       strcpy(new->context, context);
00291       new->priority = priority;
00292       new->arguments = arguments;
00293    }
00294    return new;
00295 }
00296 
00297 static void gosub_free(void *data)
00298 {
00299    AST_LIST_HEAD(, gosub_stack_frame) *oldlist = data;
00300    struct gosub_stack_frame *oldframe;
00301    AST_LIST_LOCK(oldlist);
00302    while ((oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries))) {
00303       gosub_release_frame(NULL, oldframe);
00304    }
00305    AST_LIST_UNLOCK(oldlist);
00306    AST_LIST_HEAD_DESTROY(oldlist);
00307    ast_free(oldlist);
00308 }
00309 
00310 static int pop_exec(struct ast_channel *chan, const char *data)
00311 {
00312    struct ast_datastore *stack_store;
00313    struct gosub_stack_frame *oldframe;
00314    AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
00315 
00316    ast_channel_lock(chan);
00317    if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
00318       ast_log(LOG_WARNING, "%s called with no gosub stack allocated.\n", app_pop);
00319       ast_channel_unlock(chan);
00320       return 0;
00321    }
00322 
00323    oldlist = stack_store->data;
00324    AST_LIST_LOCK(oldlist);
00325    oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
00326    AST_LIST_UNLOCK(oldlist);
00327 
00328    if (oldframe) {
00329       gosub_release_frame(chan, oldframe);
00330    } else {
00331       ast_debug(1, "%s called with an empty gosub stack\n", app_pop);
00332    }
00333    ast_channel_unlock(chan);
00334    return 0;
00335 }
00336 
00337 static int return_exec(struct ast_channel *chan, const char *data)
00338 {
00339    struct ast_datastore *stack_store;
00340    struct gosub_stack_frame *oldframe;
00341    AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
00342    const char *retval = data;
00343    int res = 0;
00344 
00345    ast_channel_lock(chan);
00346    if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
00347       ast_log(LOG_ERROR, "Return without Gosub: stack is unallocated\n");
00348       ast_channel_unlock(chan);
00349       return -1;
00350    }
00351 
00352    oldlist = stack_store->data;
00353    AST_LIST_LOCK(oldlist);
00354    oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
00355    AST_LIST_UNLOCK(oldlist);
00356 
00357    if (!oldframe) {
00358       ast_log(LOG_ERROR, "Return without Gosub: stack is empty\n");
00359       ast_channel_unlock(chan);
00360       return -1;
00361    } else if (oldframe->is_agi) {
00362       /* Exit from AGI */
00363       res = -1;
00364    }
00365 
00366    ast_explicit_goto(chan, oldframe->context, oldframe->extension, oldframe->priority);
00367    gosub_release_frame(chan, oldframe);
00368 
00369    /* Set a return value, if any */
00370    pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", S_OR(retval, ""));
00371    ast_channel_unlock(chan);
00372    return res;
00373 }
00374 
00375 static int gosub_exec(struct ast_channel *chan, const char *data)
00376 {
00377    struct ast_datastore *stack_store;
00378    AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
00379    struct gosub_stack_frame *newframe;
00380    struct gosub_stack_frame *lastframe;
00381    char argname[15];
00382    char *parse;
00383    char *label;
00384    char *caller_id;
00385    char *orig_context;
00386    char *orig_exten;
00387    char *dest_context;
00388    char *dest_exten;
00389    int orig_priority;
00390    int dest_priority;
00391    int i;
00392    int max_argc = 0;
00393    AST_DECLARE_APP_ARGS(args2,
00394       AST_APP_ARG(argval)[100];
00395    );
00396 
00397    if (ast_strlen_zero(data)) {
00398       ast_log(LOG_ERROR, "%s requires an argument: %s([[context,]exten,]priority[(arg1[,...][,argN])])\n", app_gosub, app_gosub);
00399       return -1;
00400    }
00401 
00402    /*
00403     * Separate the arguments from the label
00404     *
00405     * NOTE:  You cannot use ast_app_separate_args for this, because
00406     * '(' cannot be used as a delimiter.
00407     */
00408    parse = ast_strdupa(data);
00409    label = strsep(&parse, "(");
00410    if (parse) {
00411       char *endparen;
00412 
00413       endparen = strrchr(parse, ')');
00414       if (endparen) {
00415          *endparen = '\0';
00416       } else {
00417          ast_log(LOG_WARNING, "Ouch.  No closing paren: '%s'?\n", data);
00418       }
00419       AST_STANDARD_RAW_ARGS(args2, parse);
00420    } else {
00421       args2.argc = 0;
00422    }
00423 
00424    ast_channel_lock(chan);
00425    orig_context = ast_strdupa(chan->context);
00426    orig_exten = ast_strdupa(chan->exten);
00427    orig_priority = chan->priority;
00428    ast_channel_unlock(chan);
00429 
00430    if (ast_parseable_goto(chan, label)) {
00431       ast_log(LOG_ERROR, "%s address is invalid: '%s'\n", app_gosub, data);
00432       goto error_exit;
00433    }
00434 
00435    ast_channel_lock(chan);
00436    dest_context = ast_strdupa(chan->context);
00437    dest_exten = ast_strdupa(chan->exten);
00438    dest_priority = chan->priority;
00439    if (ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP)) {
00440       ++dest_priority;
00441    }
00442    caller_id = S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL);
00443    if (caller_id) {
00444       caller_id = ast_strdupa(caller_id);
00445    }
00446    ast_channel_unlock(chan);
00447 
00448    if (!ast_exists_extension(chan, dest_context, dest_exten, dest_priority, caller_id)) {
00449       ast_log(LOG_ERROR, "Attempt to reach a non-existent destination for %s: (Context:%s, Extension:%s, Priority:%d)\n",
00450          app_gosub, dest_context, dest_exten, dest_priority);
00451       goto error_exit;
00452    }
00453 
00454    /* Now we know that we're going to a new location */
00455 
00456    ast_channel_lock(chan);
00457 
00458    /* Find stack datastore return list. */
00459    if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
00460       ast_debug(1, "Channel %s has no datastore, so we're allocating one.\n",
00461          chan->name);
00462       stack_store = ast_datastore_alloc(&stack_info, NULL);
00463       if (!stack_store) {
00464          ast_log(LOG_ERROR, "Unable to allocate new datastore.  %s failed.\n",
00465             app_gosub);
00466          goto error_exit_locked;
00467       }
00468 
00469       oldlist = ast_calloc(1, sizeof(*oldlist));
00470       if (!oldlist) {
00471          ast_log(LOG_ERROR, "Unable to allocate datastore list head.  %s failed.\n",
00472             app_gosub);
00473          ast_datastore_free(stack_store);
00474          goto error_exit_locked;
00475       }
00476       AST_LIST_HEAD_INIT(oldlist);
00477 
00478       stack_store->data = oldlist;
00479       ast_channel_datastore_add(chan, stack_store);
00480    } else {
00481       oldlist = stack_store->data;
00482    }
00483 
00484    if ((lastframe = AST_LIST_FIRST(oldlist))) {
00485       max_argc = lastframe->arguments;
00486    }
00487 
00488    /* Mask out previous Gosub arguments in this invocation */
00489    if (args2.argc > max_argc) {
00490       max_argc = args2.argc;
00491    }
00492 
00493    /* Create the return address */
00494    newframe = gosub_allocate_frame(orig_context, orig_exten, orig_priority + 1, max_argc);
00495    if (!newframe) {
00496       goto error_exit_locked;
00497    }
00498 
00499    /* Set our arguments */
00500    for (i = 0; i < max_argc; i++) {
00501       snprintf(argname, sizeof(argname), "ARG%d", i + 1);
00502       frame_set_var(chan, newframe, argname, i < args2.argc ? args2.argval[i] : "");
00503       ast_debug(1, "Setting '%s' to '%s'\n", argname, i < args2.argc ? args2.argval[i] : "");
00504    }
00505    snprintf(argname, sizeof(argname), "%d", args2.argc);
00506    frame_set_var(chan, newframe, "ARGC", argname);
00507 
00508    /* And finally, save our return address */
00509    AST_LIST_LOCK(oldlist);
00510    AST_LIST_INSERT_HEAD(oldlist, newframe, entries);
00511    AST_LIST_UNLOCK(oldlist);
00512    ast_channel_unlock(chan);
00513 
00514    return 0;
00515 
00516 error_exit:
00517    ast_channel_lock(chan);
00518 
00519 error_exit_locked:
00520    /* Restore the original dialplan location. */
00521    ast_copy_string(chan->context, orig_context, sizeof(chan->context));
00522    ast_copy_string(chan->exten, orig_exten, sizeof(chan->exten));
00523    chan->priority = orig_priority;
00524    ast_channel_unlock(chan);
00525    return -1;
00526 }
00527 
00528 static int gosubif_exec(struct ast_channel *chan, const char *data)
00529 {
00530    char *args;
00531    int res=0;
00532    AST_DECLARE_APP_ARGS(cond,
00533       AST_APP_ARG(ition);
00534       AST_APP_ARG(labels);
00535    );
00536    AST_DECLARE_APP_ARGS(label,
00537       AST_APP_ARG(iftrue);
00538       AST_APP_ARG(iffalse);
00539    );
00540 
00541    if (ast_strlen_zero(data)) {
00542       ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
00543       return 0;
00544    }
00545 
00546    args = ast_strdupa(data);
00547    AST_NONSTANDARD_RAW_ARGS(cond, args, '?');
00548    if (cond.argc != 2) {
00549       ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
00550       return 0;
00551    }
00552 
00553    AST_NONSTANDARD_RAW_ARGS(label, cond.labels, ':');
00554 
00555    if (pbx_checkcondition(cond.ition)) {
00556       if (!ast_strlen_zero(label.iftrue))
00557          res = gosub_exec(chan, label.iftrue);
00558    } else if (!ast_strlen_zero(label.iffalse)) {
00559       res = gosub_exec(chan, label.iffalse);
00560    }
00561 
00562    return res;
00563 }
00564 
00565 static int local_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00566 {
00567    struct ast_datastore *stack_store;
00568    AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
00569    struct gosub_stack_frame *frame;
00570    struct ast_var_t *variables;
00571 
00572    if (!chan) {
00573       ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
00574       return -1;
00575    }
00576 
00577    ast_channel_lock(chan);
00578    if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
00579       ast_channel_unlock(chan);
00580       return -1;
00581    }
00582 
00583    oldlist = stack_store->data;
00584    AST_LIST_LOCK(oldlist);
00585    if (!(frame = AST_LIST_FIRST(oldlist))) {
00586       /* Not within a Gosub routine */
00587       AST_LIST_UNLOCK(oldlist);
00588       ast_channel_unlock(chan);
00589       return -1;
00590    }
00591 
00592    AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
00593       if (!strcmp(data, ast_var_name(variables))) {
00594          const char *tmp;
00595          tmp = pbx_builtin_getvar_helper(chan, data);
00596          ast_copy_string(buf, S_OR(tmp, ""), len);
00597          break;
00598       }
00599    }
00600    AST_LIST_UNLOCK(oldlist);
00601    ast_channel_unlock(chan);
00602    return 0;
00603 }
00604 
00605 static int local_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
00606 {
00607    struct ast_datastore *stack_store;
00608    AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
00609    struct gosub_stack_frame *frame;
00610 
00611    if (!chan) {
00612       ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
00613       return -1;
00614    }
00615 
00616    ast_channel_lock(chan);
00617    if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
00618       ast_log(LOG_ERROR, "Tried to set LOCAL(%s), but we aren't within a Gosub routine\n", var);
00619       ast_channel_unlock(chan);
00620       return -1;
00621    }
00622 
00623    oldlist = stack_store->data;
00624    AST_LIST_LOCK(oldlist);
00625    frame = AST_LIST_FIRST(oldlist);
00626 
00627    if (frame) {
00628       frame_set_var(chan, frame, var, value);
00629    }
00630 
00631    AST_LIST_UNLOCK(oldlist);
00632    ast_channel_unlock(chan);
00633 
00634    return 0;
00635 }
00636 
00637 static struct ast_custom_function local_function = {
00638    .name = "LOCAL",
00639    .write = local_write,
00640    .read = local_read,
00641 };
00642 
00643 static int peek_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00644 {
00645    int found = 0, n;
00646    struct ast_var_t *variables;
00647    AST_DECLARE_APP_ARGS(args,
00648       AST_APP_ARG(n);
00649       AST_APP_ARG(name);
00650    );
00651 
00652    if (!chan) {
00653       ast_log(LOG_ERROR, "LOCAL_PEEK must be called on an active channel\n");
00654       return -1;
00655    }
00656 
00657    AST_STANDARD_RAW_ARGS(args, data);
00658 
00659    if (ast_strlen_zero(args.n) || ast_strlen_zero(args.name)) {
00660       ast_log(LOG_ERROR, "LOCAL_PEEK requires parameters n and varname\n");
00661       return -1;
00662    }
00663 
00664    n = atoi(args.n);
00665    *buf = '\0';
00666 
00667    ast_channel_lock(chan);
00668    AST_LIST_TRAVERSE(&chan->varshead, variables, entries) {
00669       if (!strcmp(args.name, ast_var_name(variables)) && ++found > n) {
00670          ast_copy_string(buf, ast_var_value(variables), len);
00671          break;
00672       }
00673    }
00674    ast_channel_unlock(chan);
00675    return 0;
00676 }
00677 
00678 static struct ast_custom_function peek_function = {
00679    .name = "LOCAL_PEEK",
00680    .read = peek_read,
00681 };
00682 
00683 static int stackpeek_read(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **str, ssize_t len)
00684 {
00685    struct ast_datastore *stack_store;
00686    AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
00687    struct gosub_stack_frame *frame;
00688    int n;
00689    AST_DECLARE_APP_ARGS(args,
00690       AST_APP_ARG(n);
00691       AST_APP_ARG(which);
00692       AST_APP_ARG(suppress);
00693    );
00694 
00695    if (!chan) {
00696       ast_log(LOG_ERROR, "STACK_PEEK must be called on an active channel\n");
00697       return -1;
00698    }
00699 
00700    data = ast_strdupa(data);
00701    AST_STANDARD_APP_ARGS(args, data);
00702 
00703    if (ast_strlen_zero(args.n) || ast_strlen_zero(args.which)) {
00704       ast_log(LOG_ERROR, "STACK_PEEK requires parameters n and which\n");
00705       return -1;
00706    }
00707 
00708    n = atoi(args.n);
00709    if (n <= 0) {
00710       ast_log(LOG_ERROR, "STACK_PEEK must be called with a positive peek value\n");
00711       return -1;
00712    }
00713 
00714    ast_channel_lock(chan);
00715    if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
00716       if (!ast_true(args.suppress)) {
00717          ast_log(LOG_ERROR, "STACK_PEEK called on a channel without a gosub stack\n");
00718       }
00719       ast_channel_unlock(chan);
00720       return -1;
00721    }
00722 
00723    oldlist = stack_store->data;
00724 
00725    AST_LIST_LOCK(oldlist);
00726    AST_LIST_TRAVERSE(oldlist, frame, entries) {
00727       if (--n == 0) {
00728          break;
00729       }
00730    }
00731 
00732    if (!frame) {
00733       /* Too deep */
00734       if (!ast_true(args.suppress)) {
00735          ast_log(LOG_ERROR, "Stack peek of '%s' is more stack frames than I have\n", args.n);
00736       }
00737       ast_channel_unlock(chan);
00738       return -1;
00739    }
00740 
00741    args.which = ast_skip_blanks(args.which);
00742 
00743    switch (args.which[0]) {
00744    case 'l': /* label */
00745       ast_str_set(str, len, "%s,%s,%d", frame->context, frame->extension, frame->priority - 1);
00746       break;
00747    case 'c': /* context */
00748       ast_str_set(str, len, "%s", frame->context);
00749       break;
00750    case 'e': /* extension */
00751       ast_str_set(str, len, "%s", frame->extension);
00752       break;
00753    case 'p': /* priority */
00754       ast_str_set(str, len, "%d", frame->priority - 1);
00755       break;
00756    default:
00757       ast_log(LOG_ERROR, "Unknown argument '%s' to STACK_PEEK\n", args.which);
00758    }
00759 
00760    AST_LIST_UNLOCK(oldlist);
00761    ast_channel_unlock(chan);
00762 
00763    return 0;
00764 }
00765 
00766 static struct ast_custom_function stackpeek_function = {
00767    .name = "STACK_PEEK",
00768    .read2 = stackpeek_read,
00769 };
00770 
00771 static int handle_gosub(struct ast_channel *chan, AGI *agi, int argc, const char * const *argv)
00772 {
00773    int old_priority, priority;
00774    char old_context[AST_MAX_CONTEXT], old_extension[AST_MAX_EXTENSION];
00775    struct ast_app *theapp;
00776    char *gosub_args;
00777 
00778    if (argc < 4 || argc > 5) {
00779       return RESULT_SHOWUSAGE;
00780    }
00781 
00782    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] : "");
00783 
00784    if (sscanf(argv[3], "%30d", &priority) != 1 || priority < 1) {
00785       /* Lookup the priority label */
00786       priority = ast_findlabel_extension(chan, argv[1], argv[2], argv[3],
00787          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL));
00788       if (priority < 0) {
00789          ast_log(LOG_ERROR, "Priority '%s' not found in '%s@%s'\n", argv[3], argv[2], argv[1]);
00790          ast_agi_send(agi->fd, chan, "200 result=-1 Gosub label not found\n");
00791          return RESULT_FAILURE;
00792       }
00793    } else if (!ast_exists_extension(chan, argv[1], argv[2], priority,
00794       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
00795       ast_agi_send(agi->fd, chan, "200 result=-1 Gosub label not found\n");
00796       return RESULT_FAILURE;
00797    }
00798 
00799    /* Save previous location, since we're going to change it */
00800    ast_copy_string(old_context, chan->context, sizeof(old_context));
00801    ast_copy_string(old_extension, chan->exten, sizeof(old_extension));
00802    old_priority = chan->priority;
00803 
00804    if (!(theapp = pbx_findapp("Gosub"))) {
00805       ast_log(LOG_ERROR, "Gosub() cannot be found in the list of loaded applications\n");
00806       ast_agi_send(agi->fd, chan, "503 result=-2 Gosub is not loaded\n");
00807       return RESULT_FAILURE;
00808    }
00809 
00810    /* Apparently, if you run ast_pbx_run on a channel that already has a pbx
00811     * structure, you need to add 1 to the priority to get it to go to the
00812     * right place.  But if it doesn't have a pbx structure, then leaving off
00813     * the 1 is the right thing to do.  See how this code differs when we
00814     * call a Gosub for the CALLEE channel in Dial or Queue.
00815     */
00816    if (argc == 5) {
00817       if (ast_asprintf(&gosub_args, "%s,%s,%d(%s)", argv[1], argv[2], priority + (chan->pbx ? 1 : 0), argv[4]) < 0) {
00818          gosub_args = NULL;
00819       }
00820    } else {
00821       if (ast_asprintf(&gosub_args, "%s,%s,%d", argv[1], argv[2], priority + (chan->pbx ? 1 : 0)) < 0) {
00822          gosub_args = NULL;
00823       }
00824    }
00825 
00826    if (gosub_args) {
00827       int res;
00828 
00829       ast_debug(1, "Trying gosub with arguments '%s'\n", gosub_args);
00830 
00831       if ((res = pbx_exec(chan, theapp, gosub_args)) == 0) {
00832          struct ast_pbx *pbx = chan->pbx;
00833          struct ast_pbx_args args;
00834          struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
00835          AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
00836          struct gosub_stack_frame *cur;
00837          if (!stack_store) {
00838             ast_log(LOG_WARNING, "No GoSub stack remaining after AGI GoSub execution.\n");
00839             ast_free(gosub_args);
00840             return RESULT_FAILURE;
00841          }
00842          oldlist = stack_store->data;
00843          cur = AST_LIST_FIRST(oldlist);
00844          cur->is_agi = 1;
00845 
00846          memset(&args, 0, sizeof(args));
00847          args.no_hangup_chan = 1;
00848          /* Suppress warning about PBX already existing */
00849          chan->pbx = NULL;
00850          ast_agi_send(agi->fd, chan, "100 result=0 Trying...\n");
00851          ast_pbx_run_args(chan, &args);
00852          ast_agi_send(agi->fd, chan, "200 result=0 Gosub complete\n");
00853          if (chan->pbx) {
00854             ast_free(chan->pbx);
00855          }
00856          chan->pbx = pbx;
00857       } else {
00858          ast_agi_send(agi->fd, chan, "200 result=%d Gosub failed\n", res);
00859       }
00860       ast_free(gosub_args);
00861    } else {
00862       ast_agi_send(agi->fd, chan, "503 result=-2 Memory allocation failure\n");
00863       return RESULT_FAILURE;
00864    }
00865 
00866    /* Restore previous location */
00867    ast_copy_string(chan->context, old_context, sizeof(chan->context));
00868    ast_copy_string(chan->exten, old_extension, sizeof(chan->exten));
00869    chan->priority = old_priority;
00870 
00871    return RESULT_SUCCESS;
00872 }
00873 
00874 static struct agi_command gosub_agi_command =
00875    { { "gosub", NULL }, handle_gosub, NULL, NULL, 0 };
00876 
00877 static int unload_module(void)
00878 {
00879    ast_agi_unregister(ast_module_info->self, &gosub_agi_command);
00880 
00881    ast_unregister_application(app_return);
00882    ast_unregister_application(app_pop);
00883    ast_unregister_application(app_gosubif);
00884    ast_unregister_application(app_gosub);
00885    ast_custom_function_unregister(&local_function);
00886    ast_custom_function_unregister(&peek_function);
00887    ast_custom_function_unregister(&stackpeek_function);
00888 
00889    return 0;
00890 }
00891 
00892 static int load_module(void)
00893 {
00894    ast_agi_register(ast_module_info->self, &gosub_agi_command);
00895 
00896    ast_register_application_xml(app_pop, pop_exec);
00897    ast_register_application_xml(app_return, return_exec);
00898    ast_register_application_xml(app_gosubif, gosubif_exec);
00899    ast_register_application_xml(app_gosub, gosub_exec);
00900    ast_custom_function_register(&local_function);
00901    ast_custom_function_register(&peek_function);
00902    ast_custom_function_register(&stackpeek_function);
00903 
00904    return 0;
00905 }
00906 
00907 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT | AST_MODFLAG_LOAD_ORDER, "Dialplan subroutines (Gosub, Return, etc)",
00908       .load = load_module,
00909       .unload = unload_module,
00910       .load_pri = AST_MODPRI_APP_DEPEND,
00911       .nonoptreq = "res_agi",
00912       );