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: 430567 $")
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    int save_in_subroutine;
00252    struct ast_datastore *macro_store = ast_channel_datastore_find(chan, &macro_ds_info, NULL);
00253 
00254    if (ast_strlen_zero(data)) {
00255       ast_log(LOG_WARNING, "Macro() requires arguments. See \"core show application macro\" for help.\n");
00256       return -1;
00257    }
00258 
00259    do {
00260       if (macro_store) {
00261          break;
00262       }
00263       if (!(macro_store = ast_datastore_alloc(&macro_ds_info, NULL))) {
00264          ast_log(LOG_WARNING, "Unable to allocate new datastore.\n");
00265          break;
00266       }
00267       /* Just the existence of this datastore is enough. */
00268       macro_store->inheritance = DATASTORE_INHERIT_FOREVER;
00269       ast_channel_datastore_add(chan, macro_store);
00270    } while (0);
00271 
00272    /* does the user want a deeper rabbit hole? */
00273    ast_channel_lock(chan);
00274    if ((s = pbx_builtin_getvar_helper(chan, "MACRO_RECURSION"))) {
00275       sscanf(s, "%30d", &maxdepth);
00276    }
00277    
00278    /* Count how many levels deep the rabbit hole goes */
00279    if ((s = pbx_builtin_getvar_helper(chan, "MACRO_DEPTH"))) {
00280       sscanf(s, "%30d", &depth);
00281    }
00282    
00283    /* Used for detecting whether to return when a Macro is called from another Macro after hangup */
00284    if (strcmp(ast_channel_exten(chan), "h") == 0)
00285       pbx_builtin_setvar_helper(chan, "MACRO_IN_HANGUP", "1");
00286    
00287    if ((inhangupc = pbx_builtin_getvar_helper(chan, "MACRO_IN_HANGUP"))) {
00288       sscanf(inhangupc, "%30d", &inhangup);
00289    }
00290    ast_channel_unlock(chan);
00291 
00292    if (depth >= maxdepth) {
00293       ast_log(LOG_ERROR, "Macro():  possible infinite loop detected.  Returning early.\n");
00294       return 0;
00295    }
00296    snprintf(depthc, sizeof(depthc), "%d", depth + 1);
00297 
00298    tmp = ast_strdupa(data);
00299    rest = tmp;
00300    macro = strsep(&rest, ",");
00301    if (ast_strlen_zero(macro)) {
00302       ast_log(LOG_WARNING, "Invalid macro name specified\n");
00303       return 0;
00304    }
00305 
00306    snprintf(fullmacro, sizeof(fullmacro), "macro-%s", macro);
00307    if (!ast_exists_extension(chan, fullmacro, "s", 1,
00308       S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
00309       if (!ast_context_find(fullmacro)) 
00310          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));
00311       else
00312          ast_log(LOG_WARNING, "Context '%s' for macro '%s' lacks 's' extension, priority 1\n", fullmacro, macro);
00313       return 0;
00314    }
00315 
00316    /* If we are to run the macro exclusively, take the mutex */
00317    if (exclusive) {
00318       ast_debug(1, "Locking macrolock for '%s'\n", fullmacro);
00319       ast_autoservice_start(chan);
00320       if (ast_context_lockmacro(fullmacro)) {
00321          ast_log(LOG_WARNING, "Failed to lock macro '%s' as in-use\n", fullmacro);
00322          ast_autoservice_stop(chan);
00323          return 0;
00324       }
00325       ast_autoservice_stop(chan);
00326    }
00327 
00328    if (!(tmp_subst = ast_str_create(16))) {
00329       return -1;
00330    }
00331 
00332    /* Save old info */
00333    ast_channel_lock(chan);
00334    oldpriority = ast_channel_priority(chan);
00335    ast_copy_string(oldexten, ast_channel_exten(chan), sizeof(oldexten));
00336    ast_copy_string(oldcontext, ast_channel_context(chan), sizeof(oldcontext));
00337    if (ast_strlen_zero(ast_channel_macrocontext(chan))) {
00338       ast_channel_macrocontext_set(chan, ast_channel_context(chan));
00339       ast_channel_macroexten_set(chan, ast_channel_exten(chan));
00340       ast_channel_macropriority_set(chan, ast_channel_priority(chan));
00341       setmacrocontext=1;
00342    }
00343    argc = 1;
00344    /* Save old macro variables */
00345    save_macro_exten = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_EXTEN"));
00346    pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", oldexten);
00347 
00348    save_macro_context = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_CONTEXT"));
00349    pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", oldcontext);
00350 
00351    save_macro_priority = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_PRIORITY"));
00352    snprintf(pc, sizeof(pc), "%d", oldpriority);
00353    pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", pc);
00354   
00355    save_macro_offset = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"));
00356    pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", NULL);
00357 
00358    pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
00359 
00360    save_in_subroutine = ast_test_flag(ast_channel_flags(chan), AST_FLAG_SUBROUTINE_EXEC);
00361    ast_set_flag(ast_channel_flags(chan), AST_FLAG_SUBROUTINE_EXEC);
00362 
00363    /* Setup environment for new run */
00364    ast_channel_exten_set(chan, "s");
00365    ast_channel_context_set(chan, fullmacro);
00366    ast_channel_priority_set(chan, 1);
00367 
00368    while((cur = strsep(&rest, ",")) && (argc < MAX_ARGS)) {
00369       const char *argp;
00370       /* Save copy of old arguments if we're overwriting some, otherwise
00371          let them pass through to the other macro */
00372       snprintf(varname, sizeof(varname), "ARG%d", argc);
00373       if ((argp = pbx_builtin_getvar_helper(chan, varname))) {
00374          oldargs[argc] = ast_strdup(argp);
00375       }
00376       pbx_builtin_setvar_helper(chan, varname, cur);
00377       argc++;
00378    }
00379    autoloopflag = ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
00380    ast_set_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
00381    ast_channel_unlock(chan);
00382 
00383    while (ast_exists_extension(chan, ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan),
00384       S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
00385       struct ast_context *c;
00386       struct ast_exten *e;
00387       int foundx;
00388       runningapp[0] = '\0';
00389       runningdata[0] = '\0';
00390 
00391       /* What application will execute? */
00392       if (ast_rdlock_contexts()) {
00393          ast_log(LOG_WARNING, "Failed to lock contexts list\n");
00394       } else {
00395          for (c = ast_walk_contexts(NULL), e = NULL; c; c = ast_walk_contexts(c)) {
00396             if (!strcmp(ast_get_context_name(c), ast_channel_context(chan))) {
00397                if (ast_rdlock_context(c)) {
00398                   ast_log(LOG_WARNING, "Unable to lock context?\n");
00399                } else {
00400                   e = find_matching_priority(c, ast_channel_exten(chan), ast_channel_priority(chan),
00401                      S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL));
00402                   if (e) { /* This will only be undefined for pbx_realtime, which is majorly broken. */
00403                      ast_copy_string(runningapp, ast_get_extension_app(e), sizeof(runningapp));
00404                      ast_copy_string(runningdata, ast_get_extension_app_data(e), sizeof(runningdata));
00405                   }
00406                   ast_unlock_context(c);
00407                }
00408                break;
00409             }
00410          }
00411       }
00412       ast_unlock_contexts();
00413 
00414       /* Reset the macro depth, if it was changed in the last iteration */
00415       pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
00416 
00417       res = ast_spawn_extension(chan, ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan),
00418          S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
00419          &foundx, 1);
00420       if (res) {
00421          /* Something bad happened, or a hangup has been requested. */
00422          if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F')) ||
00423             (res == '*') || (res == '#')) {
00424             /* Just return result as to the previous application as if it had been dialed */
00425             ast_debug(1, "Oooh, got something to jump out with ('%c')!\n", res);
00426             break;
00427          }
00428          switch(res) {
00429          case MACRO_EXIT_RESULT:
00430             res = 0;
00431             goto out;
00432          default:
00433             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);
00434             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);
00435             goto out;
00436          }
00437       }
00438 
00439       ast_debug(1, "Executed application: %s\n", runningapp);
00440 
00441       if (!strcasecmp(runningapp, "GOSUB")) {
00442          gosub_level++;
00443          ast_debug(1, "Incrementing gosub_level\n");
00444       } else if (!strcasecmp(runningapp, "GOSUBIF")) {
00445          char *cond, *app_arg;
00446          char *app2;
00447          ast_str_substitute_variables(&tmp_subst, 0, chan, runningdata);
00448          app2 = ast_str_buffer(tmp_subst);
00449          cond = strsep(&app2, "?");
00450          app_arg = strsep(&app2, ":");
00451          if (pbx_checkcondition(cond)) {
00452             if (!ast_strlen_zero(app_arg)) {
00453                gosub_level++;
00454                ast_debug(1, "Incrementing gosub_level\n");
00455             }
00456          } else {
00457             if (!ast_strlen_zero(app2)) {
00458                gosub_level++;
00459                ast_debug(1, "Incrementing gosub_level\n");
00460             }
00461          }
00462       } else if (!strcasecmp(runningapp, "RETURN")) {
00463          gosub_level--;
00464          ast_debug(1, "Decrementing gosub_level\n");
00465       } else if (!strcasecmp(runningapp, "STACKPOP")) {
00466          gosub_level--;
00467          ast_debug(1, "Decrementing gosub_level\n");
00468       } else if (!strncasecmp(runningapp, "EXEC", 4)) {
00469          /* Must evaluate args to find actual app */
00470          char *tmp2, *tmp3 = NULL;
00471          ast_str_substitute_variables(&tmp_subst, 0, chan, runningdata);
00472          tmp2 = ast_str_buffer(tmp_subst);
00473          if (!strcasecmp(runningapp, "EXECIF")) {
00474             if ((tmp3 = strchr(tmp2, '|'))) {
00475                *tmp3++ = '\0';
00476             }
00477             if (!pbx_checkcondition(tmp2)) {
00478                tmp3 = NULL;
00479             }
00480          } else {
00481             tmp3 = tmp2;
00482          }
00483 
00484          if (tmp3) {
00485             ast_debug(1, "Last app: %s\n", tmp3);
00486          }
00487 
00488          if (tmp3 && !strncasecmp(tmp3, "GOSUB", 5)) {
00489             gosub_level++;
00490             ast_debug(1, "Incrementing gosub_level\n");
00491          } else if (tmp3 && !strncasecmp(tmp3, "RETURN", 6)) {
00492             gosub_level--;
00493             ast_debug(1, "Decrementing gosub_level\n");
00494          } else if (tmp3 && !strncasecmp(tmp3, "STACKPOP", 8)) {
00495             gosub_level--;
00496             ast_debug(1, "Decrementing gosub_level\n");
00497          }
00498       }
00499 
00500       if (gosub_level == 0 && strcasecmp(ast_channel_context(chan), fullmacro)) {
00501          ast_verb(2, "Channel '%s' jumping out of macro '%s'\n", ast_channel_name(chan), macro);
00502          break;
00503       }
00504 
00505       /* don't stop executing extensions when we're in "h" */
00506       if (ast_check_hangup(chan) && !inhangup) {
00507          ast_debug(1, "Extension %s, macroexten %s, priority %d returned normally even though call was hung up\n",
00508             ast_channel_exten(chan),
00509             ast_channel_macroexten(chan),
00510             ast_channel_priority(chan));
00511          goto out;
00512       }
00513       ast_channel_priority_set(chan, ast_channel_priority(chan) + 1);
00514    }
00515    out:
00516 
00517    /* Don't let the channel change now. */
00518    ast_channel_lock(chan);
00519 
00520    /* Reset the depth back to what it was when the routine was entered (like if we called Macro recursively) */
00521    snprintf(depthc, sizeof(depthc), "%d", depth);
00522    pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
00523    ast_set2_flag(ast_channel_flags(chan), autoloopflag, AST_FLAG_IN_AUTOLOOP);
00524    ast_set2_flag(ast_channel_flags(chan), save_in_subroutine, AST_FLAG_SUBROUTINE_EXEC);
00525 
00526    for (x = 1; x < argc; x++) {
00527       /* Restore old arguments and delete ours */
00528       snprintf(varname, sizeof(varname), "ARG%d", x);
00529       pbx_builtin_setvar_helper(chan, varname, oldargs[x]);
00530       ast_free(oldargs[x]);
00531    }
00532 
00533    /* Restore macro variables */
00534    pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", save_macro_exten);
00535    pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", save_macro_context);
00536    pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", save_macro_priority);
00537    ast_free(save_macro_exten);
00538    ast_free(save_macro_context);
00539    ast_free(save_macro_priority);
00540 
00541    if (setmacrocontext) {
00542       ast_channel_macrocontext_set(chan, "");
00543       ast_channel_macroexten_set(chan, "");
00544       ast_channel_macropriority_set(chan, 0);
00545    }
00546 
00547    if (!strcasecmp(ast_channel_context(chan), fullmacro)
00548       && !(ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO)) {
00549       const char *offsets;
00550 
00551       /* If we're leaving the macro normally, restore original information */
00552       ast_channel_priority_set(chan, oldpriority);
00553       ast_channel_context_set(chan, oldcontext);
00554       ast_channel_exten_set(chan, oldexten);
00555       if ((offsets = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"))) {
00556          /* Handle macro offset if it's set by checking the availability of step n + offset + 1, otherwise continue
00557          normally if there is any problem */
00558          if (sscanf(offsets, "%30d", &offset) == 1) {
00559             if (ast_exists_extension(chan, ast_channel_context(chan), ast_channel_exten(chan),
00560                ast_channel_priority(chan) + offset + 1,
00561                S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
00562                ast_channel_priority_set(chan, ast_channel_priority(chan) + offset);
00563             }
00564          }
00565       }
00566    }
00567 
00568    pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", save_macro_offset);
00569    ast_free(save_macro_offset);
00570 
00571    /* Unlock the macro */
00572    if (exclusive) {
00573       ast_debug(1, "Unlocking macrolock for '%s'\n", fullmacro);
00574       if (ast_context_unlockmacro(fullmacro)) {
00575          ast_log(LOG_ERROR, "Failed to unlock macro '%s' - that isn't good\n", fullmacro);
00576          res = 0;
00577       }
00578    }
00579    ast_channel_unlock(chan);
00580    ast_free(tmp_subst);
00581 
00582    return res;
00583 }
00584 
00585 static int macro_exec(struct ast_channel *chan, const char *data)
00586 {
00587    return _macro_exec(chan, data, 0);
00588 }
00589 
00590 static int macroexclusive_exec(struct ast_channel *chan, const char *data)
00591 {
00592    return _macro_exec(chan, data, 1);
00593 }
00594 
00595 static int macroif_exec(struct ast_channel *chan, const char *data) 
00596 {
00597    char *expr = NULL, *label_a = NULL, *label_b = NULL;
00598    int res = 0;
00599 
00600    expr = ast_strdupa(data);
00601 
00602    if ((label_a = strchr(expr, '?'))) {
00603       *label_a = '\0';
00604       label_a++;
00605       if ((label_b = strchr(label_a, ':'))) {
00606          *label_b = '\0';
00607          label_b++;
00608       }
00609       if (pbx_checkcondition(expr))
00610          res = macro_exec(chan, label_a);
00611       else if (label_b) 
00612          res = macro_exec(chan, label_b);
00613    } else
00614       ast_log(LOG_WARNING, "Invalid Syntax.\n");
00615 
00616    return res;
00617 }
00618          
00619 static int macro_exit_exec(struct ast_channel *chan, const char *data)
00620 {
00621    return MACRO_EXIT_RESULT;
00622 }
00623 
00624 static int unload_module(void)
00625 {
00626    int res;
00627 
00628    res = ast_unregister_application(if_app);
00629    res |= ast_unregister_application(exit_app);
00630    res |= ast_unregister_application(app);
00631    res |= ast_unregister_application(exclusive_app);
00632 
00633    return res;
00634 }
00635 
00636 static int load_module(void)
00637 {
00638    int res;
00639 
00640    res = ast_register_application_xml(exit_app, macro_exit_exec);
00641    res |= ast_register_application_xml(if_app, macroif_exec);
00642    res |= ast_register_application_xml(exclusive_app, macroexclusive_exec);
00643    res |= ast_register_application_xml(app, macro_exec);
00644 
00645    return res;
00646 }
00647 
00648 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Extension Macros");

Generated on Thu Apr 16 06:27:09 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6