Mon Mar 20 08:25:36 2006

Asterisk developer's documentation


Main Page | Modules | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

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  * \ingroup applications
00024  */
00025 
00026 #include <stdlib.h>
00027 #include <stdio.h>
00028 #include <string.h>
00029 #include <unistd.h>
00030 #include <sys/types.h>
00031 
00032 #include "asterisk.h"
00033 
00034 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 9156 $")
00035 
00036 #include "asterisk/file.h"
00037 #include "asterisk/logger.h"
00038 #include "asterisk/channel.h"
00039 #include "asterisk/pbx.h"
00040 #include "asterisk/module.h"
00041 #include "asterisk/options.h"
00042 #include "asterisk/config.h"
00043 #include "asterisk/utils.h"
00044 #include "asterisk/lock.h"
00045 
00046 #define MAX_ARGS 80
00047 
00048 /* special result value used to force macro exit */
00049 #define MACRO_EXIT_RESULT 1024
00050 
00051 static char *tdesc = "Extension Macros";
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 
00065 static char *if_descrip =
00066 "  MacroIf(<expr>?macroname_a[|arg1][:macroname_b[|arg1]])\n"
00067 "Executes macro defined in <macroname_a> if <expr> is true\n"
00068 "(otherwise <macroname_b> if provided)\n"
00069 "Arguments and return values as in application macro()\n";
00070 
00071 static char *exit_descrip =
00072 "  MacroExit():\n"
00073 "Causes the currently running macro to exit as if it had\n"
00074 "ended normally by running out of priorities to execute.\n"
00075 "If used outside a macro, will likely cause unexpected\n"
00076 "behavior.\n";
00077 
00078 static char *app = "Macro";
00079 static char *if_app = "MacroIf";
00080 static char *exit_app = "MacroExit";
00081 
00082 static char *synopsis = "Macro Implementation";
00083 static char *if_synopsis = "Conditional Macro Implementation";
00084 static char *exit_synopsis = "Exit From Macro";
00085 
00086 STANDARD_LOCAL_USER;
00087 
00088 LOCAL_USER_DECL;
00089 
00090 static int macro_exec(struct ast_channel *chan, void *data)
00091 {
00092    char *tmp;
00093    char *cur, *rest;
00094    char *macro;
00095    char fullmacro[80];
00096    char varname[80];
00097    char *oldargs[MAX_ARGS + 1] = { NULL, };
00098    int argc, x;
00099    int res=0;
00100    char oldexten[256]="";
00101    int oldpriority;
00102    char pc[80], depthc[12];
00103    char oldcontext[AST_MAX_CONTEXT] = "";
00104    char *offsets;
00105    int offset, depth;
00106    int setmacrocontext=0;
00107    int autoloopflag, dead = 0;
00108   
00109    char *save_macro_exten;
00110    char *save_macro_context;
00111    char *save_macro_priority;
00112    char *save_macro_offset;
00113    struct localuser *u;
00114  
00115    if (ast_strlen_zero(data)) {
00116       ast_log(LOG_WARNING, "Macro() requires arguments. See \"show application macro\" for help.\n");
00117       return -1;
00118    }
00119 
00120    LOCAL_USER_ADD(u);
00121 
00122    /* Count how many levels deep the rabbit hole goes */
00123    tmp = pbx_builtin_getvar_helper(chan, "MACRO_DEPTH");
00124    if (tmp) {
00125       sscanf(tmp, "%d", &depth);
00126    } else {
00127       depth = 0;
00128    }
00129 
00130    if (depth >= 7) {
00131       ast_log(LOG_ERROR, "Macro():  possible infinite loop detected.  Returning early.\n");
00132       LOCAL_USER_REMOVE(u);
00133       return 0;
00134    }
00135    snprintf(depthc, sizeof(depthc), "%d", depth + 1);
00136    pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
00137 
00138    tmp = ast_strdupa(data);
00139    rest = tmp;
00140    macro = strsep(&rest, "|");
00141    if (ast_strlen_zero(macro)) {
00142       ast_log(LOG_WARNING, "Invalid macro name specified\n");
00143       LOCAL_USER_REMOVE(u);
00144       return 0;
00145    }
00146    snprintf(fullmacro, sizeof(fullmacro), "macro-%s", macro);
00147    if (!ast_exists_extension(chan, fullmacro, "s", 1, chan->cid.cid_num)) {
00148       if (!ast_context_find(fullmacro)) 
00149          ast_log(LOG_WARNING, "No such context '%s' for macro '%s'\n", fullmacro, macro);
00150       else
00151          ast_log(LOG_WARNING, "Context '%s' for macro '%s' lacks 's' extension, priority 1\n", fullmacro, macro);
00152       LOCAL_USER_REMOVE(u);
00153       return 0;
00154    }
00155    
00156    /* Save old info */
00157    oldpriority = chan->priority;
00158    ast_copy_string(oldexten, chan->exten, sizeof(oldexten));
00159    ast_copy_string(oldcontext, chan->context, sizeof(oldcontext));
00160    if (ast_strlen_zero(chan->macrocontext)) {
00161       ast_copy_string(chan->macrocontext, chan->context, sizeof(chan->macrocontext));
00162       ast_copy_string(chan->macroexten, chan->exten, sizeof(chan->macroexten));
00163       chan->macropriority = chan->priority;
00164       setmacrocontext=1;
00165    }
00166    argc = 1;
00167    /* Save old macro variables */
00168    save_macro_exten = pbx_builtin_getvar_helper(chan, "MACRO_EXTEN");
00169    if (save_macro_exten) 
00170       save_macro_exten = strdup(save_macro_exten);
00171    pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", oldexten);
00172 
00173    save_macro_context = pbx_builtin_getvar_helper(chan, "MACRO_CONTEXT");
00174    if (save_macro_context)
00175       save_macro_context = strdup(save_macro_context);
00176    pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", oldcontext);
00177 
00178    save_macro_priority = pbx_builtin_getvar_helper(chan, "MACRO_PRIORITY");
00179    if (save_macro_priority) 
00180       save_macro_priority = strdup(save_macro_priority);
00181    snprintf(pc, sizeof(pc), "%d", oldpriority);
00182    pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", pc);
00183   
00184    save_macro_offset = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET");
00185    if (save_macro_offset) 
00186       save_macro_offset = strdup(save_macro_offset);
00187    pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", NULL);
00188 
00189    /* Setup environment for new run */
00190    chan->exten[0] = 's';
00191    chan->exten[1] = '\0';
00192    ast_copy_string(chan->context, fullmacro, sizeof(chan->context));
00193    chan->priority = 1;
00194 
00195    while((cur = strsep(&rest, "|")) && (argc < MAX_ARGS)) {
00196       /* Save copy of old arguments if we're overwriting some, otherwise
00197          let them pass through to the other macro */
00198       snprintf(varname, sizeof(varname), "ARG%d", argc);
00199       oldargs[argc] = pbx_builtin_getvar_helper(chan, varname);
00200       if (oldargs[argc])
00201          oldargs[argc] = strdup(oldargs[argc]);
00202       pbx_builtin_setvar_helper(chan, varname, cur);
00203       argc++;
00204    }
00205    autoloopflag = ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP);
00206    ast_set_flag(chan, AST_FLAG_IN_AUTOLOOP);
00207    while(ast_exists_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num)) {
00208       /* Reset the macro depth, if it was changed in the last iteration */
00209       pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
00210       if ((res = ast_spawn_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num))) {
00211          /* Something bad happened, or a hangup has been requested. */
00212          if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F')) ||
00213             (res == '*') || (res == '#')) {
00214             /* Just return result as to the previous application as if it had been dialed */
00215             ast_log(LOG_DEBUG, "Oooh, got something to jump out with ('%c')!\n", res);
00216             break;
00217          }
00218          switch(res) {
00219          case MACRO_EXIT_RESULT:
00220             res = 0;
00221             goto out;
00222          case AST_PBX_KEEPALIVE:
00223             if (option_debug)
00224                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);
00225             else if (option_verbose > 1)
00226                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);
00227             goto out;
00228             break;
00229          default:
00230             if (option_debug)
00231                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);
00232             else if (option_verbose > 1)
00233                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);
00234             dead = 1;
00235             goto out;
00236          }
00237       }
00238       if (strcasecmp(chan->context, fullmacro)) {
00239          if (option_verbose > 1)
00240             ast_verbose(VERBOSE_PREFIX_2 "Channel '%s' jumping out of macro '%s'\n", chan->name, macro);
00241          break;
00242       }
00243       /* don't stop executing extensions when we're in "h" */
00244       if (chan->_softhangup && strcasecmp(oldexten,"h")) {
00245          ast_log(LOG_DEBUG, "Extension %s, priority %d returned normally even though call was hung up\n",
00246             chan->exten, chan->priority);
00247          goto out;
00248       }
00249       chan->priority++;
00250    }
00251    out:
00252    /* Reset the depth back to what it was when the routine was entered (like if we called Macro recursively) */
00253    snprintf(depthc, sizeof(depthc), "%d", depth);
00254    if (!dead) {
00255       pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
00256 
00257       ast_set2_flag(chan, autoloopflag, AST_FLAG_IN_AUTOLOOP);
00258    }
00259 
00260    for (x = 1; x < argc; x++) {
00261       /* Restore old arguments and delete ours */
00262       snprintf(varname, sizeof(varname), "ARG%d", x);
00263       if (oldargs[x]) {
00264          if (!dead)
00265             pbx_builtin_setvar_helper(chan, varname, oldargs[x]);
00266          free(oldargs[x]);
00267       } else if (!dead) {
00268          pbx_builtin_setvar_helper(chan, varname, NULL);
00269       }
00270    }
00271 
00272    /* Restore macro variables */
00273    if (!dead) {
00274       pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", save_macro_exten);
00275       pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", save_macro_context);
00276       pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", save_macro_priority);
00277    }
00278    if (save_macro_exten)
00279       free(save_macro_exten);
00280    if (save_macro_context)
00281       free(save_macro_context);
00282    if (save_macro_priority)
00283       free(save_macro_priority);
00284 
00285    if (!dead && setmacrocontext) {
00286       chan->macrocontext[0] = '\0';
00287       chan->macroexten[0] = '\0';
00288       chan->macropriority = 0;
00289    }
00290 
00291    if (!dead && !strcasecmp(chan->context, fullmacro)) {
00292       /* If we're leaving the macro normally, restore original information */
00293       chan->priority = oldpriority;
00294       ast_copy_string(chan->context, oldcontext, sizeof(chan->context));
00295       if (!(chan->_softhangup & AST_SOFTHANGUP_ASYNCGOTO)) {
00296          /* Copy the extension, so long as we're not in softhangup, where we could be given an asyncgoto */
00297          ast_copy_string(chan->exten, oldexten, sizeof(chan->exten));
00298          if ((offsets = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"))) {
00299             /* Handle macro offset if it's set by checking the availability of step n + offset + 1, otherwise continue
00300                normally if there is any problem */
00301             if (sscanf(offsets, "%d", &offset) == 1) {
00302                if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + offset + 1, chan->cid.cid_num)) {
00303                   chan->priority += offset;
00304                }
00305             }
00306          }
00307       }
00308    }
00309 
00310    if (!dead)
00311       pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", save_macro_offset);
00312    if (save_macro_offset)
00313       free(save_macro_offset);
00314    LOCAL_USER_REMOVE(u);
00315    return res;
00316 }
00317 
00318 static int macroif_exec(struct ast_channel *chan, void *data) 
00319 {
00320    char *expr = NULL, *label_a = NULL, *label_b = NULL;
00321    int res = 0;
00322    struct localuser *u;
00323 
00324    LOCAL_USER_ADD(u);
00325 
00326    expr = ast_strdupa(data);
00327    if (!expr) {
00328       ast_log(LOG_ERROR, "Out of Memory!\n");
00329       LOCAL_USER_REMOVE(u);
00330       return -1;
00331    }
00332 
00333    if ((label_a = strchr(expr, '?'))) {
00334       *label_a = '\0';
00335       label_a++;
00336       if ((label_b = strchr(label_a, ':'))) {
00337          *label_b = '\0';
00338          label_b++;
00339       }
00340       if (ast_true(expr))
00341          macro_exec(chan, label_a);
00342       else if (label_b) 
00343          macro_exec(chan, label_b);
00344    } else
00345       ast_log(LOG_WARNING, "Invalid Syntax.\n");
00346 
00347    LOCAL_USER_REMOVE(u);
00348 
00349    return res;
00350 }
00351          
00352 static int macro_exit_exec(struct ast_channel *chan, void *data)
00353 {
00354    return MACRO_EXIT_RESULT;
00355 }
00356 
00357 int unload_module(void)
00358 {
00359    int res;
00360 
00361    res = ast_unregister_application(if_app);
00362    res |= ast_unregister_application(exit_app);
00363    res |= ast_unregister_application(app);
00364 
00365    STANDARD_HANGUP_LOCALUSERS;
00366 
00367    return res;
00368 }
00369 
00370 int load_module(void)
00371 {
00372    int res;
00373 
00374    res = ast_register_application(exit_app, macro_exit_exec, exit_synopsis, exit_descrip);
00375    res |= ast_register_application(if_app, macroif_exec, if_synopsis, if_descrip);
00376    res |= ast_register_application(app, macro_exec, synopsis, descrip);
00377 
00378    return res;
00379 }
00380 
00381 char *description(void)
00382 {
00383    return tdesc;
00384 }
00385 
00386 int usecount(void)
00387 {
00388    int res;
00389    STANDARD_USECOUNT(res);
00390    return res;
00391 }
00392 
00393 char *key()
00394 {
00395    return ASTERISK_GPL_KEY;
00396 }

Generated on Mon Mar 20 08:25:36 2006 for Asterisk - the Open Source PBX by  doxygen 1.3.9.1