Tue Aug 20 16:34:21 2013

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: 370642 $")
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(chan->exten, "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(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
00308       if (!ast_context_find(fullmacro)) 
00309          ast_log(LOG_WARNING, "No such context '%s' for macro '%s'\n", fullmacro, macro);
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 = chan->priority;
00333    ast_copy_string(oldexten, chan->exten, sizeof(oldexten));
00334    ast_copy_string(oldcontext, chan->context, sizeof(oldcontext));
00335    if (ast_strlen_zero(chan->macrocontext)) {
00336       ast_copy_string(chan->macrocontext, chan->context, sizeof(chan->macrocontext));
00337       ast_copy_string(chan->macroexten, chan->exten, sizeof(chan->macroexten));
00338       chan->macropriority = chan->priority;
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    chan->exten[0] = 's';
00360    chan->exten[1] = '\0';
00361    ast_copy_string(chan->context, fullmacro, sizeof(chan->context));
00362    chan->priority = 1;
00363 
00364    ast_channel_lock(chan);
00365    while((cur = strsep(&rest, ",")) && (argc < MAX_ARGS)) {
00366       const char *argp;
00367       /* Save copy of old arguments if we're overwriting some, otherwise
00368          let them pass through to the other macro */
00369       snprintf(varname, sizeof(varname), "ARG%d", argc);
00370       if ((argp = pbx_builtin_getvar_helper(chan, varname))) {
00371          oldargs[argc] = ast_strdup(argp);
00372       }
00373       pbx_builtin_setvar_helper(chan, varname, cur);
00374       argc++;
00375    }
00376    ast_channel_unlock(chan);
00377    autoloopflag = ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP);
00378    ast_set_flag(chan, AST_FLAG_IN_AUTOLOOP);
00379    while (ast_exists_extension(chan, chan->context, chan->exten, chan->priority,
00380       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
00381       struct ast_context *c;
00382       struct ast_exten *e;
00383       int foundx;
00384       runningapp[0] = '\0';
00385       runningdata[0] = '\0';
00386 
00387       /* What application will execute? */
00388       if (ast_rdlock_contexts()) {
00389          ast_log(LOG_WARNING, "Failed to lock contexts list\n");
00390       } else {
00391          for (c = ast_walk_contexts(NULL), e = NULL; c; c = ast_walk_contexts(c)) {
00392             if (!strcmp(ast_get_context_name(c), chan->context)) {
00393                if (ast_rdlock_context(c)) {
00394                   ast_log(LOG_WARNING, "Unable to lock context?\n");
00395                } else {
00396                   e = find_matching_priority(c, chan->exten, chan->priority,
00397                      S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL));
00398                   if (e) { /* This will only be undefined for pbx_realtime, which is majorly broken. */
00399                      ast_copy_string(runningapp, ast_get_extension_app(e), sizeof(runningapp));
00400                      ast_copy_string(runningdata, ast_get_extension_app_data(e), sizeof(runningdata));
00401                   }
00402                   ast_unlock_context(c);
00403                }
00404                break;
00405             }
00406          }
00407       }
00408       ast_unlock_contexts();
00409 
00410       /* Reset the macro depth, if it was changed in the last iteration */
00411       pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
00412 
00413       res = ast_spawn_extension(chan, chan->context, chan->exten, chan->priority,
00414          S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
00415          &foundx, 1);
00416       if (res) {
00417          /* Something bad happened, or a hangup has been requested. */
00418          if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F')) ||
00419             (res == '*') || (res == '#')) {
00420             /* Just return result as to the previous application as if it had been dialed */
00421             ast_debug(1, "Oooh, got something to jump out with ('%c')!\n", res);
00422             break;
00423          }
00424          switch(res) {
00425          case MACRO_EXIT_RESULT:
00426             res = 0;
00427             goto out;
00428          default:
00429             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);
00430             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);
00431             goto out;
00432          }
00433       }
00434 
00435       ast_debug(1, "Executed application: %s\n", runningapp);
00436 
00437       if (!strcasecmp(runningapp, "GOSUB")) {
00438          gosub_level++;
00439          ast_debug(1, "Incrementing gosub_level\n");
00440       } else if (!strcasecmp(runningapp, "GOSUBIF")) {
00441          char *cond, *app_arg;
00442          char *app2;
00443          ast_str_substitute_variables(&tmp_subst, 0, chan, runningdata);
00444          app2 = ast_str_buffer(tmp_subst);
00445          cond = strsep(&app2, "?");
00446          app_arg = strsep(&app2, ":");
00447          if (pbx_checkcondition(cond)) {
00448             if (!ast_strlen_zero(app_arg)) {
00449                gosub_level++;
00450                ast_debug(1, "Incrementing gosub_level\n");
00451             }
00452          } else {
00453             if (!ast_strlen_zero(app2)) {
00454                gosub_level++;
00455                ast_debug(1, "Incrementing gosub_level\n");
00456             }
00457          }
00458       } else if (!strcasecmp(runningapp, "RETURN")) {
00459          gosub_level--;
00460          ast_debug(1, "Decrementing gosub_level\n");
00461       } else if (!strcasecmp(runningapp, "STACKPOP")) {
00462          gosub_level--;
00463          ast_debug(1, "Decrementing gosub_level\n");
00464       } else if (!strncasecmp(runningapp, "EXEC", 4)) {
00465          /* Must evaluate args to find actual app */
00466          char *tmp2, *tmp3 = NULL;
00467          ast_str_substitute_variables(&tmp_subst, 0, chan, runningdata);
00468          tmp2 = ast_str_buffer(tmp_subst);
00469          if (!strcasecmp(runningapp, "EXECIF")) {
00470             if ((tmp3 = strchr(tmp2, '|'))) {
00471                *tmp3++ = '\0';
00472             }
00473             if (!pbx_checkcondition(tmp2)) {
00474                tmp3 = NULL;
00475             }
00476          } else {
00477             tmp3 = tmp2;
00478          }
00479 
00480          if (tmp3) {
00481             ast_debug(1, "Last app: %s\n", tmp3);
00482          }
00483 
00484          if (tmp3 && !strncasecmp(tmp3, "GOSUB", 5)) {
00485             gosub_level++;
00486             ast_debug(1, "Incrementing gosub_level\n");
00487          } else if (tmp3 && !strncasecmp(tmp3, "RETURN", 6)) {
00488             gosub_level--;
00489             ast_debug(1, "Decrementing gosub_level\n");
00490          } else if (tmp3 && !strncasecmp(tmp3, "STACKPOP", 8)) {
00491             gosub_level--;
00492             ast_debug(1, "Decrementing gosub_level\n");
00493          }
00494       }
00495 
00496       if (gosub_level == 0 && strcasecmp(chan->context, fullmacro)) {
00497          ast_verb(2, "Channel '%s' jumping out of macro '%s'\n", chan->name, macro);
00498          break;
00499       }
00500 
00501       /* don't stop executing extensions when we're in "h" */
00502       if (ast_check_hangup(chan) && !inhangup) {
00503          ast_debug(1, "Extension %s, macroexten %s, priority %d returned normally even though call was hung up\n", chan->exten, chan->macroexten, chan->priority);
00504          goto out;
00505       }
00506       chan->priority++;
00507    }
00508    out:
00509 
00510    /* Don't let the channel change now. */
00511    ast_channel_lock(chan);
00512 
00513    /* Reset the depth back to what it was when the routine was entered (like if we called Macro recursively) */
00514    snprintf(depthc, sizeof(depthc), "%d", depth);
00515    pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
00516    ast_set2_flag(chan, autoloopflag, AST_FLAG_IN_AUTOLOOP);
00517 
00518    for (x = 1; x < argc; x++) {
00519       /* Restore old arguments and delete ours */
00520       snprintf(varname, sizeof(varname), "ARG%d", x);
00521       if (oldargs[x]) {
00522          pbx_builtin_setvar_helper(chan, varname, oldargs[x]);
00523          ast_free(oldargs[x]);
00524       } else {
00525          pbx_builtin_setvar_helper(chan, varname, NULL);
00526       }
00527    }
00528 
00529    /* Restore macro variables */
00530    pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", save_macro_exten);
00531    pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", save_macro_context);
00532    pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", save_macro_priority);
00533    if (save_macro_exten)
00534       ast_free(save_macro_exten);
00535    if (save_macro_context)
00536       ast_free(save_macro_context);
00537    if (save_macro_priority)
00538       ast_free(save_macro_priority);
00539 
00540    if (setmacrocontext) {
00541       chan->macrocontext[0] = '\0';
00542       chan->macroexten[0] = '\0';
00543       chan->macropriority = 0;
00544    }
00545 
00546    if (!strcasecmp(chan->context, fullmacro)) {
00547       const char *offsets;
00548 
00549       /* If we're leaving the macro normally, restore original information */
00550       chan->priority = oldpriority;
00551       ast_copy_string(chan->context, oldcontext, sizeof(chan->context));
00552       ast_copy_string(chan->exten, oldexten, sizeof(chan->exten));
00553       if ((offsets = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"))) {
00554          /* Handle macro offset if it's set by checking the availability of step n + offset + 1, otherwise continue
00555          normally if there is any problem */
00556          if (sscanf(offsets, "%30d", &offset) == 1) {
00557             if (ast_exists_extension(chan, chan->context, chan->exten,
00558                chan->priority + offset + 1,
00559                S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
00560                chan->priority += offset;
00561             }
00562          }
00563       }
00564    }
00565 
00566    pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", save_macro_offset);
00567    if (save_macro_offset)
00568       ast_free(save_macro_offset);
00569 
00570    /* Unlock the macro */
00571    if (exclusive) {
00572       ast_debug(1, "Unlocking macrolock for '%s'\n", fullmacro);
00573       if (ast_context_unlockmacro(fullmacro)) {
00574          ast_log(LOG_ERROR, "Failed to unlock macro '%s' - that isn't good\n", fullmacro);
00575          res = 0;
00576       }
00577    }
00578    ast_channel_unlock(chan);
00579    ast_free(tmp_subst);
00580 
00581    return res;
00582 }
00583 
00584 static int macro_exec(struct ast_channel *chan, const char *data)
00585 {
00586    return _macro_exec(chan, data, 0);
00587 }
00588 
00589 static int macroexclusive_exec(struct ast_channel *chan, const char *data)
00590 {
00591    return _macro_exec(chan, data, 1);
00592 }
00593 
00594 static int macroif_exec(struct ast_channel *chan, const char *data) 
00595 {
00596    char *expr = NULL, *label_a = NULL, *label_b = NULL;
00597    int res = 0;
00598 
00599    expr = ast_strdupa(data);
00600 
00601    if ((label_a = strchr(expr, '?'))) {
00602       *label_a = '\0';
00603       label_a++;
00604       if ((label_b = strchr(label_a, ':'))) {
00605          *label_b = '\0';
00606          label_b++;
00607       }
00608       if (pbx_checkcondition(expr))
00609          res = macro_exec(chan, label_a);
00610       else if (label_b) 
00611          res = macro_exec(chan, label_b);
00612    } else
00613       ast_log(LOG_WARNING, "Invalid Syntax.\n");
00614 
00615    return res;
00616 }
00617          
00618 static int macro_exit_exec(struct ast_channel *chan, const char *data)
00619 {
00620    return MACRO_EXIT_RESULT;
00621 }
00622 
00623 static int unload_module(void)
00624 {
00625    int res;
00626 
00627    res = ast_unregister_application(if_app);
00628    res |= ast_unregister_application(exit_app);
00629    res |= ast_unregister_application(app);
00630    res |= ast_unregister_application(exclusive_app);
00631 
00632    return res;
00633 }
00634 
00635 static int load_module(void)
00636 {
00637    int res;
00638 
00639    res = ast_register_application_xml(exit_app, macro_exit_exec);
00640    res |= ast_register_application_xml(if_app, macroif_exec);
00641    res |= ast_register_application_xml(exclusive_app, macroexclusive_exec);
00642    res |= ast_register_application_xml(app, macro_exec);
00643 
00644    return res;
00645 }
00646 
00647 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Extension Macros");

Generated on 20 Aug 2013 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1