Wed Apr 6 11:29:38 2011

Asterisk developer's documentation


app_macro.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
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 Dial plan macro Implementation
00022  *
00023  * \author Mark Spencer <markster@digium.com>
00024  * 
00025  * \ingroup applications
00026  */
00027 
00028 #include "asterisk.h"
00029 
00030 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 295866 $")
00031 
00032 #include "asterisk/file.h"
00033 #include "asterisk/channel.h"
00034 #include "asterisk/pbx.h"
00035 #include "asterisk/module.h"
00036 #include "asterisk/config.h"
00037 #include "asterisk/utils.h"
00038 #include "asterisk/lock.h"
00039 #include "asterisk/app.h"
00040 
00041 /*** DOCUMENTATION
00042    <application name="Macro" language="en_US">
00043       <synopsis>
00044          Macro Implementation.
00045       </synopsis>
00046       <syntax>
00047          <parameter name="name" required="true">
00048             <para>The name of the macro</para>
00049          </parameter>
00050          <parameter name="args">
00051             <argument name="arg1" required="true" />
00052             <argument name="arg2" multiple="true" />
00053          </parameter>
00054       </syntax>
00055       <description>
00056          <para>Executes a macro using the context macro-<replaceable>name</replaceable>,
00057          jumping to the <literal>s</literal> extension of that context and executing each step,
00058          then returning when the steps end.</para>
00059          <para>The calling extension, context, and priority are stored in <variable>MACRO_EXTEN</variable>,
00060          <variable>MACRO_CONTEXT</variable> and <variable>MACRO_PRIORITY</variable> respectively. Arguments
00061          become <variable>ARG1</variable>, <variable>ARG2</variable>, etc in the macro context.</para>
00062          <para>If you Goto out of the Macro context, the Macro will terminate and control will be returned
00063          at the location of the Goto.</para>
00064          <para>If <variable>MACRO_OFFSET</variable> is set at termination, Macro will attempt to continue
00065          at priority MACRO_OFFSET + N + 1 if such a step exists, and N + 1 otherwise.</para>
00066          <warning><para>Because of the way Macro is implemented (it executes the priorities contained within
00067          it via sub-engine), and a fixed per-thread memory stack allowance, macros are limited to 7 levels
00068          of nesting (macro calling macro calling macro, etc.); It may be possible that stack-intensive
00069          applications in deeply nested macros could cause asterisk to crash earlier than this limit.
00070          It is advised that if you need to deeply nest macro calls, that you use the Gosub application
00071          (now allows arguments like a Macro) with explict Return() calls instead.</para></warning>
00072          <warning><para>Use of the application <literal>WaitExten</literal> within a macro will not function
00073          as expected. Please use the <literal>Read</literal> application in order to read DTMF from a channel
00074          currently executing a macro.</para></warning>
00075       </description>
00076       <see-also>
00077          <ref type="application">MacroExit</ref>
00078          <ref type="application">Goto</ref>
00079          <ref type="application">Gosub</ref>
00080       </see-also>
00081    </application>
00082    <application name="MacroIf" language="en_US">
00083       <synopsis>
00084          Conditional Macro implementation.
00085       </synopsis>
00086       <syntax argsep="?">
00087          <parameter name="expr" required="true" />
00088          <parameter name="destination" required="true" argsep=":">
00089             <argument name="macroiftrue" required="true">
00090                <argument name="macroiftrue" required="true" />
00091                <argument name="arg1" multiple="true" />
00092             </argument>
00093             <argument name="macroiffalse">
00094                <argument name="macroiffalse" required="true" />
00095                <argument name="arg1" multiple="true" />
00096             </argument>
00097          </parameter>
00098       </syntax>
00099       <description>
00100          <para>Executes macro defined in <replaceable>macroiftrue</replaceable> if
00101          <replaceable>expr</replaceable> is true (otherwise <replaceable>macroiffalse</replaceable>
00102          if provided)</para>
00103          <para>Arguments and return values as in application Macro()</para>
00104          <xi:include xpointer="xpointer(/docs/application[@name='Macro']/description/warning[2])" />
00105       </description>
00106       <see-also>
00107          <ref type="application">GotoIf</ref>
00108          <ref type="application">GosubIf</ref>
00109          <ref type="function">IF</ref>
00110       </see-also>
00111    </application>
00112    <application name="MacroExclusive" language="en_US">
00113       <synopsis>
00114          Exclusive Macro Implementation.
00115       </synopsis>
00116       <syntax>
00117          <parameter name="name" required="true">
00118             <para>The name of the macro</para>
00119          </parameter>
00120          <parameter name="arg1" />
00121          <parameter name="arg2" multiple="true" />
00122       </syntax>
00123       <description>
00124          <para>Executes macro defined in the context macro-<replaceable>name</replaceable>.
00125          Only one call at a time may run the macro. (we'll wait if another call is busy
00126          executing in the Macro)</para>
00127          <para>Arguments and return values as in application Macro()</para>
00128          <xi:include xpointer="xpointer(/docs/application[@name='Macro']/description/warning[2])" />
00129       </description>
00130       <see-also>
00131          <ref type="application">Macro</ref>
00132       </see-also>
00133    </application>
00134    <application name="MacroExit" language="en_US">
00135       <synopsis>
00136          Exit from Macro.
00137       </synopsis>
00138       <syntax />
00139       <description>
00140          <para>Causes the currently running macro to exit as if it had
00141          ended normally by running out of priorities to execute.
00142          If used outside a macro, will likely cause unexpected behavior.</para>
00143       </description>
00144       <see-also>
00145          <ref type="application">Macro</ref>
00146       </see-also>
00147    </application>
00148  ***/
00149 
00150 #define MAX_ARGS 80
00151 
00152 /* special result value used to force macro exit */
00153 #define MACRO_EXIT_RESULT 1024
00154 
00155 static char *app = "Macro";
00156 static char *if_app = "MacroIf";
00157 static char *exclusive_app = "MacroExclusive";
00158 static char *exit_app = "MacroExit";
00159 
00160 static void macro_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan);
00161 
00162 static struct ast_datastore_info macro_ds_info = {
00163    .type = "MACRO",
00164    .chan_fixup = macro_fixup,
00165 };
00166 
00167 static void macro_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
00168 {
00169    int i;
00170    char varname[10];
00171    pbx_builtin_setvar_helper(new_chan, "MACRO_DEPTH", "0");
00172    pbx_builtin_setvar_helper(new_chan, "MACRO_CONTEXT", NULL);
00173    pbx_builtin_setvar_helper(new_chan, "MACRO_EXTEN", NULL);
00174    pbx_builtin_setvar_helper(new_chan, "MACRO_PRIORITY", NULL);
00175    pbx_builtin_setvar_helper(new_chan, "MACRO_OFFSET", NULL);
00176    for (i = 1; i < 100; i++) {
00177       snprintf(varname, sizeof(varname), "ARG%d", i);
00178       while (pbx_builtin_getvar_helper(new_chan, varname)) {
00179          /* Kill all levels of arguments */
00180          pbx_builtin_setvar_helper(new_chan, varname, NULL);
00181       }
00182    }
00183 }
00184 
00185 static struct ast_exten *find_matching_priority(struct ast_context *c, const char *exten, int priority, const char *callerid)
00186 {
00187    struct ast_exten *e;
00188    struct ast_include *i;
00189    struct ast_context *c2;
00190 
00191    for (e=ast_walk_context_extensions(c, NULL); e; e=ast_walk_context_extensions(c, e)) {
00192       if (ast_extension_match(ast_get_extension_name(e), exten)) {
00193          int needmatch = ast_get_extension_matchcid(e);
00194          if ((needmatch && ast_extension_match(ast_get_extension_cidmatch(e), callerid)) ||
00195             (!needmatch)) {
00196             /* This is the matching extension we want */
00197             struct ast_exten *p;
00198             for (p=ast_walk_extension_priorities(e, NULL); p; p=ast_walk_extension_priorities(e, p)) {
00199                if (priority != ast_get_extension_priority(p))
00200                   continue;
00201                return p;
00202             }
00203          }
00204       }
00205    }
00206 
00207    /* No match; run through includes */
00208    for (i=ast_walk_context_includes(c, NULL); i; i=ast_walk_context_includes(c, i)) {
00209       for (c2=ast_walk_contexts(NULL); c2; c2=ast_walk_contexts(c2)) {
00210          if (!strcmp(ast_get_context_name(c2), ast_get_include_name(i))) {
00211             e = find_matching_priority(c2, exten, priority, callerid);
00212             if (e)
00213                return e;
00214          }
00215       }
00216    }
00217    return NULL;
00218 }
00219 
00220 static int _macro_exec(struct ast_channel *chan, const char *data, int exclusive)
00221 {
00222    const char *s;
00223    char *tmp;
00224    char *cur, *rest;
00225    char *macro;
00226    char fullmacro[80];
00227    char varname[80];
00228    char runningapp[80], runningdata[1024];
00229    char *oldargs[MAX_ARGS + 1] = { NULL, };
00230    int argc, x;
00231    int res=0;
00232    char oldexten[256]="";
00233    int oldpriority, gosub_level = 0;
00234    char pc[80], depthc[12];
00235    char oldcontext[AST_MAX_CONTEXT] = "";
00236    const char *inhangupc;
00237    int offset, depth = 0, maxdepth = 7;
00238    int setmacrocontext=0;
00239    int autoloopflag, inhangup = 0;
00240    struct ast_str *tmp_subst = NULL;
00241   
00242    char *save_macro_exten;
00243    char *save_macro_context;
00244    char *save_macro_priority;
00245    char *save_macro_offset;
00246    struct ast_datastore *macro_store = ast_channel_datastore_find(chan, &macro_ds_info, NULL);
00247 
00248    if (ast_strlen_zero(data)) {
00249       ast_log(LOG_WARNING, "Macro() requires arguments. See \"core show application macro\" for help.\n");
00250       return -1;
00251    }
00252 
00253    do {
00254       if (macro_store) {
00255          break;
00256       }
00257       if (!(macro_store = ast_datastore_alloc(&macro_ds_info, NULL))) {
00258          ast_log(LOG_WARNING, "Unable to allocate new datastore.\n");
00259          break;
00260       }
00261       /* Just the existence of this datastore is enough. */
00262       macro_store->inheritance = DATASTORE_INHERIT_FOREVER;
00263       ast_channel_datastore_add(chan, macro_store);
00264    } while (0);
00265 
00266    /* does the user want a deeper rabbit hole? */
00267    ast_channel_lock(chan);
00268    if ((s = pbx_builtin_getvar_helper(chan, "MACRO_RECURSION"))) {
00269       sscanf(s, "%30d", &maxdepth);
00270    }
00271    
00272    /* Count how many levels deep the rabbit hole goes */
00273    if ((s = pbx_builtin_getvar_helper(chan, "MACRO_DEPTH"))) {
00274       sscanf(s, "%30d", &depth);
00275    }
00276    
00277    /* Used for detecting whether to return when a Macro is called from another Macro after hangup */
00278    if (strcmp(chan->exten, "h") == 0)
00279       pbx_builtin_setvar_helper(chan, "MACRO_IN_HANGUP", "1");
00280    
00281    if ((inhangupc = pbx_builtin_getvar_helper(chan, "MACRO_IN_HANGUP"))) {
00282       sscanf(inhangupc, "%30d", &inhangup);
00283    }
00284    ast_channel_unlock(chan);
00285 
00286    if (depth >= maxdepth) {
00287       ast_log(LOG_ERROR, "Macro():  possible infinite loop detected.  Returning early.\n");
00288       return 0;
00289    }
00290    snprintf(depthc, sizeof(depthc), "%d", depth + 1);
00291 
00292    tmp = ast_strdupa(data);
00293    rest = tmp;
00294    macro = strsep(&rest, ",");
00295    if (ast_strlen_zero(macro)) {
00296       ast_log(LOG_WARNING, "Invalid macro name specified\n");
00297       return 0;
00298    }
00299 
00300    snprintf(fullmacro, sizeof(fullmacro), "macro-%s", macro);
00301    if (!ast_exists_extension(chan, fullmacro, "s", 1,
00302       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
00303       if (!ast_context_find(fullmacro)) 
00304          ast_log(LOG_WARNING, "No such context '%s' for macro '%s'\n", fullmacro, macro);
00305       else
00306          ast_log(LOG_WARNING, "Context '%s' for macro '%s' lacks 's' extension, priority 1\n", fullmacro, macro);
00307       return 0;
00308    }
00309 
00310    /* If we are to run the macro exclusively, take the mutex */
00311    if (exclusive) {
00312       ast_debug(1, "Locking macrolock for '%s'\n", fullmacro);
00313       ast_autoservice_start(chan);
00314       if (ast_context_lockmacro(fullmacro)) {
00315          ast_log(LOG_WARNING, "Failed to lock macro '%s' as in-use\n", fullmacro);
00316          ast_autoservice_stop(chan);
00317          return 0;
00318       }
00319       ast_autoservice_stop(chan);
00320    }
00321 
00322    if (!(tmp_subst = ast_str_create(16))) {
00323       return -1;
00324    }
00325 
00326    /* Save old info */
00327    oldpriority = chan->priority;
00328    ast_copy_string(oldexten, chan->exten, sizeof(oldexten));
00329    ast_copy_string(oldcontext, chan->context, sizeof(oldcontext));
00330    if (ast_strlen_zero(chan->macrocontext)) {
00331       ast_copy_string(chan->macrocontext, chan->context, sizeof(chan->macrocontext));
00332       ast_copy_string(chan->macroexten, chan->exten, sizeof(chan->macroexten));
00333       chan->macropriority = chan->priority;
00334       setmacrocontext=1;
00335    }
00336    argc = 1;
00337    /* Save old macro variables */
00338    save_macro_exten = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_EXTEN"));
00339    pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", oldexten);
00340 
00341    save_macro_context = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_CONTEXT"));
00342    pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", oldcontext);
00343 
00344    save_macro_priority = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_PRIORITY"));
00345    snprintf(pc, sizeof(pc), "%d", oldpriority);
00346    pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", pc);
00347   
00348    save_macro_offset = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"));
00349    pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", NULL);
00350 
00351    pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
00352 
00353    /* Setup environment for new run */
00354    chan->exten[0] = 's';
00355    chan->exten[1] = '\0';
00356    ast_copy_string(chan->context, fullmacro, sizeof(chan->context));
00357    chan->priority = 1;
00358 
00359    ast_channel_lock(chan);
00360    while((cur = strsep(&rest, ",")) && (argc < MAX_ARGS)) {
00361       const char *argp;
00362       /* Save copy of old arguments if we're overwriting some, otherwise
00363          let them pass through to the other macro */
00364       snprintf(varname, sizeof(varname), "ARG%d", argc);
00365       if ((argp = pbx_builtin_getvar_helper(chan, varname))) {
00366          oldargs[argc] = ast_strdup(argp);
00367       }
00368       pbx_builtin_setvar_helper(chan, varname, cur);
00369       argc++;
00370    }
00371    ast_channel_unlock(chan);
00372    autoloopflag = ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP);
00373    ast_set_flag(chan, AST_FLAG_IN_AUTOLOOP);
00374    while (ast_exists_extension(chan, chan->context, chan->exten, chan->priority,
00375       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
00376       struct ast_context *c;
00377       struct ast_exten *e;
00378       int foundx;
00379       runningapp[0] = '\0';
00380       runningdata[0] = '\0';
00381 
00382       /* What application will execute? */
00383       if (ast_rdlock_contexts()) {
00384          ast_log(LOG_WARNING, "Failed to lock contexts list\n");
00385       } else {
00386          for (c = ast_walk_contexts(NULL), e = NULL; c; c = ast_walk_contexts(c)) {
00387             if (!strcmp(ast_get_context_name(c), chan->context)) {
00388                if (ast_rdlock_context(c)) {
00389                   ast_log(LOG_WARNING, "Unable to lock context?\n");
00390                } else {
00391                   e = find_matching_priority(c, chan->exten, chan->priority,
00392                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL));
00393                   if (e) { /* This will only be undefined for pbx_realtime, which is majorly broken. */
00394                      ast_copy_string(runningapp, ast_get_extension_app(e), sizeof(runningapp));
00395                      ast_copy_string(runningdata, ast_get_extension_app_data(e), sizeof(runningdata));
00396                   }
00397                   ast_unlock_context(c);
00398                }
00399                break;
00400             }
00401          }
00402       }
00403       ast_unlock_contexts();
00404 
00405       /* Reset the macro depth, if it was changed in the last iteration */
00406       pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
00407 
00408       res = ast_spawn_extension(chan, chan->context, chan->exten, chan->priority,
00409          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
00410          &foundx, 1);
00411       if (res) {
00412          /* Something bad happened, or a hangup has been requested. */
00413          if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F')) ||
00414             (res == '*') || (res == '#')) {
00415             /* Just return result as to the previous application as if it had been dialed */
00416             ast_debug(1, "Oooh, got something to jump out with ('%c')!\n", res);
00417             break;
00418          }
00419          switch(res) {
00420          case MACRO_EXIT_RESULT:
00421             res = 0;
00422             goto out;
00423          default:
00424             ast_debug(2, "Spawn extension (%s,%s,%d) exited non-zero on '%s' in macro '%s'\n", chan->context, chan->exten, chan->priority, chan->name, macro);
00425             ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s' in macro '%s'\n", chan->context, chan->exten, chan->priority, chan->name, macro);
00426             goto out;
00427          }
00428       }
00429 
00430       ast_debug(1, "Executed application: %s\n", runningapp);
00431 
00432       if (!strcasecmp(runningapp, "GOSUB")) {
00433          gosub_level++;
00434          ast_debug(1, "Incrementing gosub_level\n");
00435       } else if (!strcasecmp(runningapp, "GOSUBIF")) {
00436          char *cond, *app_arg;
00437          char *app2;
00438          ast_str_substitute_variables(&tmp_subst, 0, chan, runningdata);
00439          app2 = ast_str_buffer(tmp_subst);
00440          cond = strsep(&app2, "?");
00441          app_arg = strsep(&app2, ":");
00442          if (pbx_checkcondition(cond)) {
00443             if (!ast_strlen_zero(app_arg)) {
00444                gosub_level++;
00445                ast_debug(1, "Incrementing gosub_level\n");
00446             }
00447          } else {
00448             if (!ast_strlen_zero(app2)) {
00449                gosub_level++;
00450                ast_debug(1, "Incrementing gosub_level\n");
00451             }
00452          }
00453       } else if (!strcasecmp(runningapp, "RETURN")) {
00454          gosub_level--;
00455          ast_debug(1, "Decrementing gosub_level\n");
00456       } else if (!strcasecmp(runningapp, "STACKPOP")) {
00457          gosub_level--;
00458          ast_debug(1, "Decrementing gosub_level\n");
00459       } else if (!strncasecmp(runningapp, "EXEC", 4)) {
00460          /* Must evaluate args to find actual app */
00461          char *tmp2, *tmp3 = NULL;
00462          ast_str_substitute_variables(&tmp_subst, 0, chan, runningdata);
00463          tmp2 = ast_str_buffer(tmp_subst);
00464          if (!strcasecmp(runningapp, "EXECIF")) {
00465             if ((tmp3 = strchr(tmp2, '|'))) {
00466                *tmp3++ = '\0';
00467             }
00468             if (!pbx_checkcondition(tmp2)) {
00469                tmp3 = NULL;
00470             }
00471          } else {
00472             tmp3 = tmp2;
00473          }
00474 
00475          if (tmp3) {
00476             ast_debug(1, "Last app: %s\n", tmp3);
00477          }
00478 
00479          if (tmp3 && !strncasecmp(tmp3, "GOSUB", 5)) {
00480             gosub_level++;
00481             ast_debug(1, "Incrementing gosub_level\n");
00482          } else if (tmp3 && !strncasecmp(tmp3, "RETURN", 6)) {
00483             gosub_level--;
00484             ast_debug(1, "Decrementing gosub_level\n");
00485          } else if (tmp3 && !strncasecmp(tmp3, "STACKPOP", 8)) {
00486             gosub_level--;
00487             ast_debug(1, "Decrementing gosub_level\n");
00488          }
00489       }
00490 
00491       if (gosub_level == 0 && strcasecmp(chan->context, fullmacro)) {
00492          ast_verb(2, "Channel '%s' jumping out of macro '%s'\n", chan->name, macro);
00493          break;
00494       }
00495 
00496       /* don't stop executing extensions when we're in "h" */
00497       if (ast_check_hangup(chan) && !inhangup) {
00498          ast_debug(1, "Extension %s, macroexten %s, priority %d returned normally even though call was hung up\n", chan->exten, chan->macroexten, chan->priority);
00499          goto out;
00500       }
00501       chan->priority++;
00502    }
00503    out:
00504 
00505    /* Don't let the channel change now. */
00506    ast_channel_lock(chan);
00507 
00508    /* Reset the depth back to what it was when the routine was entered (like if we called Macro recursively) */
00509    snprintf(depthc, sizeof(depthc), "%d", depth);
00510    pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
00511    ast_set2_flag(chan, autoloopflag, AST_FLAG_IN_AUTOLOOP);
00512 
00513    for (x = 1; x < argc; x++) {
00514       /* Restore old arguments and delete ours */
00515       snprintf(varname, sizeof(varname), "ARG%d", x);
00516       if (oldargs[x]) {
00517          pbx_builtin_setvar_helper(chan, varname, oldargs[x]);
00518          ast_free(oldargs[x]);
00519       } else {
00520          pbx_builtin_setvar_helper(chan, varname, NULL);
00521       }
00522    }
00523 
00524    /* Restore macro variables */
00525    pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", save_macro_exten);
00526    pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", save_macro_context);
00527    pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", save_macro_priority);
00528    if (save_macro_exten)
00529       ast_free(save_macro_exten);
00530    if (save_macro_context)
00531       ast_free(save_macro_context);
00532    if (save_macro_priority)
00533       ast_free(save_macro_priority);
00534 
00535    if (setmacrocontext) {
00536       chan->macrocontext[0] = '\0';
00537       chan->macroexten[0] = '\0';
00538       chan->macropriority = 0;
00539    }
00540 
00541    if (!strcasecmp(chan->context, fullmacro)) {
00542       const char *offsets;
00543 
00544       /* If we're leaving the macro normally, restore original information */
00545       chan->priority = oldpriority;
00546       ast_copy_string(chan->context, oldcontext, sizeof(chan->context));
00547       ast_copy_string(chan->exten, oldexten, sizeof(chan->exten));
00548       if ((offsets = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"))) {
00549          /* Handle macro offset if it's set by checking the availability of step n + offset + 1, otherwise continue
00550          normally if there is any problem */
00551          if (sscanf(offsets, "%30d", &offset) == 1) {
00552             if (ast_exists_extension(chan, chan->context, chan->exten,
00553                chan->priority + offset + 1,
00554                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
00555                chan->priority += offset;
00556             }
00557          }
00558       }
00559    }
00560 
00561    pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", save_macro_offset);
00562    if (save_macro_offset)
00563       ast_free(save_macro_offset);
00564 
00565    /* Unlock the macro */
00566    if (exclusive) {
00567       ast_debug(1, "Unlocking macrolock for '%s'\n", fullmacro);
00568       if (ast_context_unlockmacro(fullmacro)) {
00569          ast_log(LOG_ERROR, "Failed to unlock macro '%s' - that isn't good\n", fullmacro);
00570          res = 0;
00571       }
00572    }
00573    ast_channel_unlock(chan);
00574    ast_free(tmp_subst);
00575 
00576    return res;
00577 }
00578 
00579 static int macro_exec(struct ast_channel *chan, const char *data)
00580 {
00581    return _macro_exec(chan, data, 0);
00582 }
00583 
00584 static int macroexclusive_exec(struct ast_channel *chan, const char *data)
00585 {
00586    return _macro_exec(chan, data, 1);
00587 }
00588 
00589 static int macroif_exec(struct ast_channel *chan, const char *data) 
00590 {
00591    char *expr = NULL, *label_a = NULL, *label_b = NULL;
00592    int res = 0;
00593 
00594    if (!(expr = ast_strdupa(data)))
00595       return -1;
00596 
00597    if ((label_a = strchr(expr, '?'))) {
00598       *label_a = '\0';
00599       label_a++;
00600       if ((label_b = strchr(label_a, ':'))) {
00601          *label_b = '\0';
00602          label_b++;
00603       }
00604       if (pbx_checkcondition(expr))
00605          res = macro_exec(chan, label_a);
00606       else if (label_b) 
00607          res = macro_exec(chan, label_b);
00608    } else
00609       ast_log(LOG_WARNING, "Invalid Syntax.\n");
00610 
00611    return res;
00612 }
00613          
00614 static int macro_exit_exec(struct ast_channel *chan, const char *data)
00615 {
00616    return MACRO_EXIT_RESULT;
00617 }
00618 
00619 static int unload_module(void)
00620 {
00621    int res;
00622 
00623    res = ast_unregister_application(if_app);
00624    res |= ast_unregister_application(exit_app);
00625    res |= ast_unregister_application(app);
00626    res |= ast_unregister_application(exclusive_app);
00627 
00628    return res;
00629 }
00630 
00631 static int load_module(void)
00632 {
00633    int res;
00634 
00635    res = ast_register_application_xml(exit_app, macro_exit_exec);
00636    res |= ast_register_application_xml(if_app, macroif_exec);
00637    res |= ast_register_application_xml(exclusive_app, macroexclusive_exec);
00638    res |= ast_register_application_xml(app, macro_exec);
00639 
00640    return res;
00641 }
00642 
00643 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Extension Macros");

Generated on Wed Apr 6 11:29:38 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7