Sat Aug 6 00:39:20 2011

Asterisk developer's documentation


app_macro.c

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

Generated on Sat Aug 6 00:39:20 2011 for Asterisk - the Open Source PBX by  doxygen 1.4.7