Sat Apr 26 2014 22:01:26

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