Thu Apr 28 2011 17:13:28

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