Tue Aug 20 16:34:22 2013

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

Generated on 20 Aug 2013 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1