Wed May 16 06:33:21 2012

Asterisk developer's documentation


app_stack.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (c) 2004-2006 Tilghman Lesher <app_stack_v003@the-tilghman.com>.
00005  *
00006  * This code is released by the author with no restrictions on usage.
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Stack applications Gosub, Return, etc.
00022  *
00023  * \author Tilghman Lesher <app_stack_v003@the-tilghman.com>
00024  * 
00025  * \ingroup applications
00026  */
00027 
00028 /*** MODULEINFO
00029    <use type="module">res_agi</use>
00030    <support_level>core</support_level>
00031  ***/
00032 
00033 #include "asterisk.h"
00034  
00035 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 365400 $")
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                <argument name="arg1" required="true" multiple="true" />
00077                <argument name="argN" />
00078             </argument>
00079             <argument name="labeliffalse" hasparams="optional">
00080                <argument name="arg1" required="true" multiple="true" />
00081                <argument name="argN" />
00082             </argument>
00083          </parameter>
00084       </syntax>
00085       <description>
00086          <para>If the condition is true, then jump to labeliftrue.  If false, jumps to
00087          labeliffalse, if specified.  In either case, a jump saves the return point
00088          in the dialplan, to be returned to with a Return.</para>
00089       </description>
00090       <see-also>
00091          <ref type="application">Gosub</ref>
00092          <ref type="application">Return</ref>
00093          <ref type="application">MacroIf</ref>
00094          <ref type="function">IF</ref>
00095          <ref type="application">GotoIf</ref>
00096       </see-also>
00097    </application>
00098    <application name="Return" language="en_US">
00099       <synopsis>
00100          Return from gosub routine.
00101       </synopsis>
00102       <syntax>
00103          <parameter name="value">
00104             <para>Return value.</para>
00105          </parameter>
00106       </syntax>
00107       <description>
00108          <para>Jumps to the last label on the stack, removing it. The return <replaceable>value</replaceable>, if
00109          any, is saved in the channel variable <variable>GOSUB_RETVAL</variable>.</para>
00110       </description>
00111       <see-also>
00112          <ref type="application">Gosub</ref>
00113          <ref type="application">StackPop</ref>
00114       </see-also>
00115    </application>
00116    <application name="StackPop" language="en_US">
00117       <synopsis>
00118          Remove one address from gosub stack.
00119       </synopsis>
00120       <syntax />
00121       <description>
00122          <para>Removes last label on the stack, discarding it.</para>
00123       </description>
00124       <see-also>
00125          <ref type="application">Return</ref>
00126          <ref type="application">Gosub</ref>
00127       </see-also>
00128    </application>
00129    <function name="LOCAL" language="en_US">
00130       <synopsis>
00131          Manage variables local to the gosub stack frame.
00132       </synopsis>
00133       <syntax>
00134          <parameter name="varname" required="true" />
00135       </syntax>
00136       <description>
00137          <para>Read and write a variable local to the gosub stack frame, once we Return() it will be lost
00138          (or it will go back to whatever value it had before the Gosub()).</para>
00139       </description>
00140       <see-also>
00141          <ref type="application">Gosub</ref>
00142          <ref type="application">GosubIf</ref>
00143          <ref type="application">Return</ref>
00144       </see-also>
00145    </function>
00146    <function name="LOCAL_PEEK" language="en_US">
00147       <synopsis>
00148          Retrieve variables hidden by the local gosub stack frame.
00149       </synopsis>
00150       <syntax>
00151          <parameter name="n" required="true" />
00152          <parameter name="varname" required="true" />
00153       </syntax>
00154       <description>
00155          <para>Read a variable <replaceable>varname</replaceable> hidden by
00156          <replaceable>n</replaceable> levels of gosub stack frames.  Note that ${LOCAL_PEEK(0,foo)}
00157          is the same as <variable>foo</variable>, since the value of <replaceable>n</replaceable>
00158          peeks under 0 levels of stack frames; in other words, 0 is the current level.  If
00159          <replaceable>n</replaceable> exceeds the available number of stack frames, then an empty
00160          string is returned.</para>
00161       </description>
00162       <see-also>
00163          <ref type="application">Gosub</ref>
00164          <ref type="application">GosubIf</ref>
00165          <ref type="application">Return</ref>
00166       </see-also>
00167    </function>
00168    <function name="STACK_PEEK" language="en_US">
00169       <synopsis>
00170          View info about the location which called Gosub
00171       </synopsis>
00172       <syntax>
00173          <parameter name="n" required="true" />
00174          <parameter name="which" required="true" />
00175          <parameter name="suppress" required="false" />
00176       </syntax>
00177       <description>
00178          <para>Read the calling <literal>c</literal>ontext, <literal>e</literal>xtension,
00179          <literal>p</literal>riority, or <literal>l</literal>abel, as specified by
00180          <replaceable>which</replaceable>, by going up <replaceable>n</replaceable> frames
00181          in the Gosub stack.  If <replaceable>suppress</replaceable> is true, then if the
00182          number of available stack frames is exceeded, then no error message will be
00183          printed.</para>
00184       </description>
00185    </function>
00186    <agi name="gosub" language="en_US">
00187       <synopsis>
00188          Cause the channel to execute the specified dialplan subroutine.
00189       </synopsis>
00190       <syntax>
00191          <parameter name="context" required="true" />
00192          <parameter name="extension" required="true" />
00193          <parameter name="priority" required="true" />
00194          <parameter name="optional-argument" />
00195       </syntax>
00196       <description>
00197          <para>Cause the channel to execute the specified dialplan subroutine,
00198          returning to the dialplan with execution of a Return().</para>
00199       </description>
00200    </agi>
00201  ***/
00202 
00203 static const char * const app_gosub = "Gosub";
00204 static const char * const app_gosubif = "GosubIf";
00205 static const char * const app_return = "Return";
00206 static const char * const app_pop = "StackPop";
00207 
00208 static void gosub_free(void *data);
00209 
00210 static struct ast_datastore_info stack_info = {
00211    .type = "GOSUB",
00212    .destroy = gosub_free,
00213 };
00214 
00215 struct gosub_stack_frame {
00216    AST_LIST_ENTRY(gosub_stack_frame) entries;
00217    /* 100 arguments is all that we support anyway, but this will handle up to 255 */
00218    unsigned char arguments;
00219    struct varshead varshead;
00220    int priority;
00221    unsigned int is_agi:1;
00222    char *context;
00223    char extension[0];
00224 };
00225 
00226 static int frame_set_var(struct ast_channel *chan, struct gosub_stack_frame *frame, const char *var, const char *value)
00227 {
00228    struct ast_var_t *variables;
00229    int found = 0;
00230 
00231    /* Does this variable already exist? */
00232    AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
00233       if (!strcmp(var, ast_var_name(variables))) {
00234          found = 1;
00235          break;
00236       }
00237    }
00238 
00239    if (!found) {
00240       variables = ast_var_assign(var, "");
00241       AST_LIST_INSERT_HEAD(&frame->varshead, variables, entries);
00242       pbx_builtin_pushvar_helper(chan, var, value);
00243    } else {
00244       pbx_builtin_setvar_helper(chan, var, value);
00245    }
00246 
00247    manager_event(EVENT_FLAG_DIALPLAN, "VarSet",
00248       "Channel: %s\r\n"
00249       "Variable: LOCAL(%s)\r\n"
00250       "Value: %s\r\n"
00251       "Uniqueid: %s\r\n",
00252       ast_channel_name(chan), var, value, ast_channel_uniqueid(chan));
00253    return 0;
00254 }
00255 
00256 static void gosub_release_frame(struct ast_channel *chan, struct gosub_stack_frame *frame)
00257 {
00258    struct ast_var_t *vardata;
00259 
00260    /* If chan is not defined, then we're calling it as part of gosub_free,
00261     * and the channel variables will be deallocated anyway.  Otherwise, we're
00262     * just releasing a single frame, so we need to clean up the arguments for
00263     * that frame, so that we re-expose the variables from the previous frame
00264     * that were hidden by this one.
00265     */
00266    while ((vardata = AST_LIST_REMOVE_HEAD(&frame->varshead, entries))) {
00267       if (chan)
00268          pbx_builtin_setvar_helper(chan, ast_var_name(vardata), NULL);  
00269       ast_var_delete(vardata);
00270    }
00271 
00272    ast_free(frame);
00273 }
00274 
00275 static struct gosub_stack_frame *gosub_allocate_frame(const char *context, const char *extension, int priority, unsigned char arguments)
00276 {
00277    struct gosub_stack_frame *new = NULL;
00278    int len_extension = strlen(extension), len_context = strlen(context);
00279 
00280    if ((new = ast_calloc(1, sizeof(*new) + 2 + len_extension + len_context))) {
00281       AST_LIST_HEAD_INIT_NOLOCK(&new->varshead);
00282       strcpy(new->extension, extension);
00283       new->context = new->extension + len_extension + 1;
00284       strcpy(new->context, context);
00285       new->priority = priority;
00286       new->arguments = arguments;
00287    }
00288    return new;
00289 }
00290 
00291 static void gosub_free(void *data)
00292 {
00293    AST_LIST_HEAD(, gosub_stack_frame) *oldlist = data;
00294    struct gosub_stack_frame *oldframe;
00295    AST_LIST_LOCK(oldlist);
00296    while ((oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries))) {
00297       gosub_release_frame(NULL, oldframe);
00298    }
00299    AST_LIST_UNLOCK(oldlist);
00300    AST_LIST_HEAD_DESTROY(oldlist);
00301    ast_free(oldlist);
00302 }
00303 
00304 static int pop_exec(struct ast_channel *chan, const char *data)
00305 {
00306    struct ast_datastore *stack_store;
00307    struct gosub_stack_frame *oldframe;
00308    AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
00309 
00310    ast_channel_lock(chan);
00311    if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
00312       ast_log(LOG_WARNING, "%s called with no gosub stack allocated.\n", app_pop);
00313       ast_channel_unlock(chan);
00314       return 0;
00315    }
00316 
00317    oldlist = stack_store->data;
00318    AST_LIST_LOCK(oldlist);
00319    oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
00320    AST_LIST_UNLOCK(oldlist);
00321 
00322    if (oldframe) {
00323       gosub_release_frame(chan, oldframe);
00324    } else {
00325       ast_debug(1, "%s called with an empty gosub stack\n", app_pop);
00326    }
00327    ast_channel_unlock(chan);
00328    return 0;
00329 }
00330 
00331 static int return_exec(struct ast_channel *chan, const char *data)
00332 {
00333    struct ast_datastore *stack_store;
00334    struct gosub_stack_frame *oldframe;
00335    AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
00336    const char *retval = data;
00337    int res = 0;
00338 
00339    ast_channel_lock(chan);
00340    if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
00341       ast_log(LOG_ERROR, "Return without Gosub: stack is unallocated\n");
00342       ast_channel_unlock(chan);
00343       return -1;
00344    }
00345 
00346    oldlist = stack_store->data;
00347    AST_LIST_LOCK(oldlist);
00348    oldframe = AST_LIST_REMOVE_HEAD(oldlist, entries);
00349    AST_LIST_UNLOCK(oldlist);
00350 
00351    if (!oldframe) {
00352       ast_log(LOG_ERROR, "Return without Gosub: stack is empty\n");
00353       ast_channel_unlock(chan);
00354       return -1;
00355    } else if (oldframe->is_agi) {
00356       /* Exit from AGI */
00357       res = -1;
00358    }
00359 
00360    ast_explicit_goto(chan, oldframe->context, oldframe->extension, oldframe->priority);
00361    gosub_release_frame(chan, oldframe);
00362 
00363    /* Set a return value, if any */
00364    pbx_builtin_setvar_helper(chan, "GOSUB_RETVAL", S_OR(retval, ""));
00365    ast_channel_unlock(chan);
00366    return res;
00367 }
00368 
00369 static int gosub_exec(struct ast_channel *chan, const char *data)
00370 {
00371    struct ast_datastore *stack_store;
00372    AST_LIST_HEAD(,gosub_stack_frame) *oldlist;
00373    struct gosub_stack_frame *newframe, *lastframe;
00374    char argname[15], *tmp = ast_strdupa(data), *label, *endparen;
00375    int i, max_argc = 0;
00376    AST_DECLARE_APP_ARGS(args2,
00377       AST_APP_ARG(argval)[100];
00378    );
00379 
00380    if (ast_strlen_zero(data)) {
00381       ast_log(LOG_ERROR, "%s requires an argument: %s([[context,]exten,]priority[(arg1[,...][,argN])])\n", app_gosub, app_gosub);
00382       return -1;
00383    }
00384 
00385    ast_channel_lock(chan);
00386    if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
00387       ast_debug(1, "Channel %s has no datastore, so we're allocating one.\n", ast_channel_name(chan));
00388       stack_store = ast_datastore_alloc(&stack_info, NULL);
00389       if (!stack_store) {
00390          ast_log(LOG_ERROR, "Unable to allocate new datastore.  Gosub will fail.\n");
00391          ast_channel_unlock(chan);
00392          return -1;
00393       }
00394 
00395       oldlist = ast_calloc(1, sizeof(*oldlist));
00396       if (!oldlist) {
00397          ast_log(LOG_ERROR, "Unable to allocate datastore list head.  Gosub will fail.\n");
00398          ast_datastore_free(stack_store);
00399          ast_channel_unlock(chan);
00400          return -1;
00401       }
00402 
00403       stack_store->data = oldlist;
00404       AST_LIST_HEAD_INIT(oldlist);
00405       ast_channel_datastore_add(chan, stack_store);
00406    } else {
00407       oldlist = stack_store->data;
00408    }
00409 
00410    if ((lastframe = AST_LIST_FIRST(oldlist))) {
00411       max_argc = lastframe->arguments;
00412    }
00413 
00414    /* Separate the arguments from the label */
00415    /* NOTE:  you cannot use ast_app_separate_args for this, because '(' cannot be used as a delimiter. */
00416    label = strsep(&tmp, "(");
00417    if (tmp) {
00418       endparen = strrchr(tmp, ')');
00419       if (endparen)
00420          *endparen = '\0';
00421       else
00422          ast_log(LOG_WARNING, "Ouch.  No closing paren: '%s'?\n", (char *)data);
00423       AST_STANDARD_RAW_ARGS(args2, tmp);
00424    } else
00425       args2.argc = 0;
00426 
00427    /* Mask out previous arguments in this invocation */
00428    if (args2.argc > max_argc) {
00429       max_argc = args2.argc;
00430    }
00431 
00432    /* Create the return address, but don't save it until we know that the Gosub destination exists */
00433    newframe = gosub_allocate_frame(ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan) + 1, max_argc);
00434 
00435    if (!newframe) {
00436       ast_channel_unlock(chan);
00437       return -1;
00438    }
00439 
00440    if (ast_parseable_goto(chan, label)) {
00441       ast_log(LOG_ERROR, "Gosub address is invalid: '%s'\n", (char *)data);
00442       ast_free(newframe);
00443       ast_channel_unlock(chan);
00444       return -1;
00445    }
00446 
00447    if (!ast_exists_extension(chan, ast_channel_context(chan), ast_channel_exten(chan),
00448       ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP) ? ast_channel_priority(chan) + 1 : ast_channel_priority(chan),
00449       S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
00450       ast_log(LOG_ERROR, "Attempt to reach a non-existent destination for gosub: (Context:%s, Extension:%s, Priority:%d)\n",
00451             ast_channel_context(chan), ast_channel_exten(chan), ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP) ? ast_channel_priority(chan) + 1 : ast_channel_priority(chan));
00452       ast_channel_context_set(chan, newframe->context);
00453       ast_channel_exten_set(chan, newframe->extension);
00454       ast_channel_priority_set(chan, newframe->priority - 1);
00455       ast_free(newframe);
00456       ast_channel_unlock(chan);
00457       return -1;
00458    }
00459 
00460    /* Now that we know for certain that we're going to a new location, set our arguments */
00461    for (i = 0; i < max_argc; i++) {
00462       snprintf(argname, sizeof(argname), "ARG%d", i + 1);
00463       frame_set_var(chan, newframe, argname, i < args2.argc ? args2.argval[i] : "");
00464       ast_debug(1, "Setting '%s' to '%s'\n", argname, i < args2.argc ? args2.argval[i] : "");
00465    }
00466    snprintf(argname, sizeof(argname), "%d", args2.argc);
00467    frame_set_var(chan, newframe, "ARGC", argname);
00468 
00469    /* And finally, save our return address */
00470    oldlist = stack_store->data;
00471    AST_LIST_LOCK(oldlist);
00472    AST_LIST_INSERT_HEAD(oldlist, newframe, entries);
00473    AST_LIST_UNLOCK(oldlist);
00474    ast_channel_unlock(chan);
00475 
00476    return 0;
00477 }
00478 
00479 static int gosubif_exec(struct ast_channel *chan, const char *data)
00480 {
00481    char *args;
00482    int res=0;
00483    AST_DECLARE_APP_ARGS(cond,
00484       AST_APP_ARG(ition);
00485       AST_APP_ARG(labels);
00486    );
00487    AST_DECLARE_APP_ARGS(label,
00488       AST_APP_ARG(iftrue);
00489       AST_APP_ARG(iffalse);
00490    );
00491 
00492    if (ast_strlen_zero(data)) {
00493       ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
00494       return 0;
00495    }
00496 
00497    args = ast_strdupa(data);
00498    AST_NONSTANDARD_RAW_ARGS(cond, args, '?');
00499    if (cond.argc != 2) {
00500       ast_log(LOG_WARNING, "GosubIf requires an argument: GosubIf(cond?label1(args):label2(args)\n");
00501       return 0;
00502    }
00503 
00504    AST_NONSTANDARD_RAW_ARGS(label, cond.labels, ':');
00505 
00506    if (pbx_checkcondition(cond.ition)) {
00507       if (!ast_strlen_zero(label.iftrue))
00508          res = gosub_exec(chan, label.iftrue);
00509    } else if (!ast_strlen_zero(label.iffalse)) {
00510       res = gosub_exec(chan, label.iffalse);
00511    }
00512 
00513    return res;
00514 }
00515 
00516 static int local_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00517 {
00518    struct ast_datastore *stack_store;
00519    AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
00520    struct gosub_stack_frame *frame;
00521    struct ast_var_t *variables;
00522 
00523    ast_channel_lock(chan);
00524    if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
00525       ast_channel_unlock(chan);
00526       return -1;
00527    }
00528 
00529    oldlist = stack_store->data;
00530    AST_LIST_LOCK(oldlist);
00531    if (!(frame = AST_LIST_FIRST(oldlist))) {
00532       /* Not within a Gosub routine */
00533       AST_LIST_UNLOCK(oldlist);
00534       ast_channel_unlock(chan);
00535       return -1;
00536    }
00537 
00538    AST_LIST_TRAVERSE(&frame->varshead, variables, entries) {
00539       if (!strcmp(data, ast_var_name(variables))) {
00540          const char *tmp;
00541          tmp = pbx_builtin_getvar_helper(chan, data);
00542          ast_copy_string(buf, S_OR(tmp, ""), len);
00543          break;
00544       }
00545    }
00546    AST_LIST_UNLOCK(oldlist);
00547    ast_channel_unlock(chan);
00548    return 0;
00549 }
00550 
00551 static int local_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
00552 {
00553    struct ast_datastore *stack_store;
00554    AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
00555    struct gosub_stack_frame *frame;
00556 
00557    ast_channel_lock(chan);
00558    if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
00559       ast_log(LOG_ERROR, "Tried to set LOCAL(%s), but we aren't within a Gosub routine\n", var);
00560       ast_channel_unlock(chan);
00561       return -1;
00562    }
00563 
00564    oldlist = stack_store->data;
00565    AST_LIST_LOCK(oldlist);
00566    frame = AST_LIST_FIRST(oldlist);
00567 
00568    if (frame) {
00569       frame_set_var(chan, frame, var, value);
00570    }
00571 
00572    AST_LIST_UNLOCK(oldlist);
00573    ast_channel_unlock(chan);
00574 
00575    return 0;
00576 }
00577 
00578 static struct ast_custom_function local_function = {
00579    .name = "LOCAL",
00580    .write = local_write,
00581    .read = local_read,
00582 };
00583 
00584 static int peek_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00585 {
00586    int found = 0, n;
00587    struct ast_var_t *variables;
00588    AST_DECLARE_APP_ARGS(args,
00589       AST_APP_ARG(n);
00590       AST_APP_ARG(name);
00591    );
00592 
00593    if (!chan) {
00594       ast_log(LOG_ERROR, "LOCAL_PEEK must be called on an active channel\n");
00595       return -1;
00596    }
00597 
00598    AST_STANDARD_RAW_ARGS(args, data);
00599    n = atoi(args.n);
00600    *buf = '\0';
00601 
00602    ast_channel_lock(chan);
00603    AST_LIST_TRAVERSE(ast_channel_varshead(chan), variables, entries) {
00604       if (!strcmp(args.name, ast_var_name(variables)) && ++found > n) {
00605          ast_copy_string(buf, ast_var_value(variables), len);
00606          break;
00607       }
00608    }
00609    ast_channel_unlock(chan);
00610    return 0;
00611 }
00612 
00613 static struct ast_custom_function peek_function = {
00614    .name = "LOCAL_PEEK",
00615    .read = peek_read,
00616 };
00617 
00618 static int stackpeek_read(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **str, ssize_t len)
00619 {
00620    struct ast_datastore *stack_store;
00621    AST_LIST_HEAD(, gosub_stack_frame) *oldlist;
00622    struct gosub_stack_frame *frame;
00623    int n;
00624    AST_DECLARE_APP_ARGS(args,
00625       AST_APP_ARG(n);
00626       AST_APP_ARG(which);
00627       AST_APP_ARG(suppress);
00628    );
00629 
00630    if (!chan) {
00631       ast_log(LOG_ERROR, "STACK_PEEK must be called on an active channel\n");
00632       return -1;
00633    }
00634 
00635    data = ast_strdupa(data);
00636    AST_STANDARD_APP_ARGS(args, data);
00637 
00638    n = atoi(args.n);
00639    if (n <= 0) {
00640       ast_log(LOG_ERROR, "STACK_PEEK must be called with a positive peek value\n");
00641       return -1;
00642    }
00643 
00644    ast_channel_lock(chan);
00645    if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
00646       if (!ast_true(args.suppress)) {
00647          ast_log(LOG_ERROR, "STACK_PEEK called on a channel without a gosub stack\n");
00648       }
00649       ast_channel_unlock(chan);
00650       return -1;
00651    }
00652 
00653    oldlist = stack_store->data;
00654 
00655    AST_LIST_LOCK(oldlist);
00656    AST_LIST_TRAVERSE(oldlist, frame, entries) {
00657       if (--n == 0) {
00658          break;
00659       }
00660    }
00661 
00662    if (!frame) {
00663       /* Too deep */
00664       if (!ast_true(args.suppress)) {
00665          ast_log(LOG_ERROR, "Stack peek of '%s' is more stack frames than I have\n", args.n);
00666       }
00667       ast_channel_unlock(chan);
00668       return -1;
00669    }
00670 
00671    args.which = ast_skip_blanks(args.which);
00672 
00673    switch (args.which[0]) {
00674    case 'l': /* label */
00675       ast_str_set(str, len, "%s,%s,%d", frame->context, frame->extension, frame->priority - 1);
00676       break;
00677    case 'c': /* context */
00678       ast_str_set(str, len, "%s", frame->context);
00679       break;
00680    case 'e': /* extension */
00681       ast_str_set(str, len, "%s", frame->extension);
00682       break;
00683    case 'p': /* priority */
00684       ast_str_set(str, len, "%d", frame->priority - 1);
00685       break;
00686    default:
00687       ast_log(LOG_ERROR, "Unknown argument '%s' to STACK_PEEK\n", args.which);
00688    }
00689 
00690    AST_LIST_UNLOCK(oldlist);
00691    ast_channel_unlock(chan);
00692 
00693    return 0;
00694 }
00695 
00696 static struct ast_custom_function stackpeek_function = {
00697    .name = "STACK_PEEK",
00698    .read2 = stackpeek_read,
00699 };
00700 
00701 static int handle_gosub(struct ast_channel *chan, AGI *agi, int argc, const char * const *argv)
00702 {
00703    int old_priority, priority;
00704    char old_context[AST_MAX_CONTEXT], old_extension[AST_MAX_EXTENSION];
00705    struct ast_app *theapp;
00706    char *gosub_args;
00707 
00708    if (argc < 4 || argc > 5) {
00709       return RESULT_SHOWUSAGE;
00710    }
00711 
00712    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] : "");
00713 
00714    if (sscanf(argv[3], "%30d", &priority) != 1 || priority < 1) {
00715       /* Lookup the priority label */
00716       priority = ast_findlabel_extension(chan, argv[1], argv[2], argv[3],
00717          S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL));
00718       if (priority < 0) {
00719          ast_log(LOG_ERROR, "Priority '%s' not found in '%s@%s'\n", argv[3], argv[2], argv[1]);
00720          ast_agi_send(agi->fd, chan, "200 result=-1 Gosub label not found\n");
00721          return RESULT_FAILURE;
00722       }
00723    } else if (!ast_exists_extension(chan, argv[1], argv[2], priority,
00724       S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
00725       ast_agi_send(agi->fd, chan, "200 result=-1 Gosub label not found\n");
00726       return RESULT_FAILURE;
00727    }
00728 
00729    /* Save previous location, since we're going to change it */
00730    ast_copy_string(old_context, ast_channel_context(chan), sizeof(old_context));
00731    ast_copy_string(old_extension, ast_channel_exten(chan), sizeof(old_extension));
00732    old_priority = ast_channel_priority(chan);
00733 
00734    if (!(theapp = pbx_findapp("Gosub"))) {
00735       ast_log(LOG_ERROR, "Gosub() cannot be found in the list of loaded applications\n");
00736       ast_agi_send(agi->fd, chan, "503 result=-2 Gosub is not loaded\n");
00737       return RESULT_FAILURE;
00738    }
00739 
00740    /* Apparently, if you run ast_pbx_run on a channel that already has a pbx
00741     * structure, you need to add 1 to the priority to get it to go to the
00742     * right place.  But if it doesn't have a pbx structure, then leaving off
00743     * the 1 is the right thing to do.  See how this code differs when we
00744     * call a Gosub for the CALLEE channel in Dial or Queue.
00745     */
00746    if (argc == 5) {
00747       if (asprintf(&gosub_args, "%s,%s,%d(%s)", argv[1], argv[2], priority + (ast_channel_pbx(chan) ? 1 : 0), argv[4]) < 0) {
00748          ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00749          gosub_args = NULL;
00750       }
00751    } else {
00752       if (asprintf(&gosub_args, "%s,%s,%d", argv[1], argv[2], priority + (ast_channel_pbx(chan) ? 1 : 0)) < 0) {
00753          ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
00754          gosub_args = NULL;
00755       }
00756    }
00757 
00758    if (gosub_args) {
00759       int res;
00760 
00761       ast_debug(1, "Trying gosub with arguments '%s'\n", gosub_args);
00762 
00763       if ((res = pbx_exec(chan, theapp, gosub_args)) == 0) {
00764          struct ast_pbx *pbx = ast_channel_pbx(chan);
00765          struct ast_pbx_args args;
00766          struct ast_datastore *stack_store = ast_channel_datastore_find(chan, &stack_info, NULL);
00767          AST_LIST_HEAD(,gosub_stack_frame) *oldlist;
00768          struct gosub_stack_frame *cur;
00769          if (!stack_store) {
00770             ast_log(LOG_WARNING, "No GoSub stack remaining after AGI GoSub execution.\n");
00771             ast_free(gosub_args);
00772             return RESULT_FAILURE;
00773          }
00774          oldlist = stack_store->data;
00775          cur = AST_LIST_FIRST(oldlist);
00776          cur->is_agi = 1;
00777 
00778          memset(&args, 0, sizeof(args));
00779          args.no_hangup_chan = 1;
00780          /* Suppress warning about PBX already existing */
00781          ast_channel_pbx_set(chan, NULL);
00782          ast_agi_send(agi->fd, chan, "100 result=0 Trying...\n");
00783          ast_pbx_run_args(chan, &args);
00784          ast_agi_send(agi->fd, chan, "200 result=0 Gosub complete\n");
00785          if (ast_channel_pbx(chan)) {
00786             ast_free(ast_channel_pbx(chan));
00787          }
00788          ast_channel_pbx_set(chan, pbx);
00789       } else {
00790          ast_agi_send(agi->fd, chan, "200 result=%d Gosub failed\n", res);
00791       }
00792       ast_free(gosub_args);
00793    } else {
00794       ast_agi_send(agi->fd, chan, "503 result=-2 Memory allocation failure\n");
00795       return RESULT_FAILURE;
00796    }
00797 
00798    /* Restore previous location */
00799    ast_channel_context_set(chan, old_context);
00800    ast_channel_exten_set(chan, old_extension);
00801    ast_channel_priority_set(chan, old_priority);
00802 
00803    return RESULT_SUCCESS;
00804 }
00805 
00806 static struct agi_command gosub_agi_command =
00807    { { "gosub", NULL }, handle_gosub, NULL, NULL, 0 };
00808 
00809 static int unload_module(void)
00810 {
00811    struct ast_context *con;
00812 
00813    ast_agi_unregister(ast_module_info->self, &gosub_agi_command);
00814 
00815    ast_unregister_application(app_return);
00816    ast_unregister_application(app_pop);
00817    ast_unregister_application(app_gosubif);
00818    ast_unregister_application(app_gosub);
00819    ast_custom_function_unregister(&local_function);
00820    ast_custom_function_unregister(&peek_function);
00821    ast_custom_function_unregister(&stackpeek_function);
00822 
00823    con = ast_context_find("gosub_virtual_context");
00824    if (con) {
00825       /* leave nothing behind */
00826       ast_context_remove_extension2(con, "s", 1, NULL, 0);
00827       ast_context_destroy(con, "app_stack");
00828    }
00829 
00830    return 0;
00831 }
00832 
00833 static int load_module(void)
00834 {
00835    struct ast_context *con;
00836 
00837    /* Create internal gosub return target to indicate successful completion. */
00838    con = ast_context_find_or_create(NULL, NULL, "gosub_virtual_context", "app_stack");
00839    if (!con) {
00840       ast_log(LOG_ERROR, "'gosub_virtual_context' does not exist and unable to create\n");
00841    } else {
00842       ast_add_extension2(con, 1, "s", 1, NULL, NULL, "NoOp",
00843          ast_strdup("Internal Gosub call complete GOSUB_RETVAL=${GOSUB_RETVAL}"),
00844          ast_free_ptr, "app_stack");
00845    }
00846 
00847    ast_agi_register(ast_module_info->self, &gosub_agi_command);
00848 
00849    ast_register_application_xml(app_pop, pop_exec);
00850    ast_register_application_xml(app_return, return_exec);
00851    ast_register_application_xml(app_gosubif, gosubif_exec);
00852    ast_register_application_xml(app_gosub, gosub_exec);
00853    ast_custom_function_register(&local_function);
00854    ast_custom_function_register(&peek_function);
00855    ast_custom_function_register(&stackpeek_function);
00856 
00857    return 0;
00858 }
00859 
00860 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT | AST_MODFLAG_LOAD_ORDER, "Dialplan subroutines (Gosub, Return, etc)",
00861       .load = load_module,
00862       .unload = unload_module,
00863       .load_pri = AST_MODPRI_APP_DEPEND,
00864       .nonoptreq = "res_agi",
00865       );

Generated on Wed May 16 06:33:21 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6