Sat Sep 16 05:47:40 2006

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  * \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: 41239 $")
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, *s;
00105    int offset, depth = 0, maxdepth = 7;
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    /* does the user want a deeper rabbit hole? */
00123    s = pbx_builtin_getvar_helper(chan, "MACRO_RECURSION");
00124    if (s)
00125       sscanf(s, "%d", &maxdepth);
00126 
00127    /* Count how many levels deep the rabbit hole goes */
00128    tmp = pbx_builtin_getvar_helper(chan, "MACRO_DEPTH");
00129    if (tmp) {
00130       sscanf(tmp, "%d", &depth);
00131    } else {
00132       depth = 0;
00133    }
00134 
00135    if (depth >= maxdepth) {
00136       ast_log(LOG_ERROR, "Macro():  possible infinite loop detected.  Returning early.\n");
00137       LOCAL_USER_REMOVE(u);
00138       return 0;
00139    }
00140    snprintf(depthc, sizeof(depthc), "%d", depth + 1);
00141    pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
00142 
00143    tmp = ast_strdupa(data);
00144    rest = tmp;
00145    macro = strsep(&rest, "|");
00146    if (ast_strlen_zero(macro)) {
00147       ast_log(LOG_WARNING, "Invalid macro name specified\n");
00148       LOCAL_USER_REMOVE(u);
00149       return 0;
00150    }
00151    snprintf(fullmacro, sizeof(fullmacro), "macro-%s", macro);
00152    if (!ast_exists_extension(chan, fullmacro, "s", 1, chan->cid.cid_num)) {
00153       if (!ast_context_find(fullmacro)) 
00154          ast_log(LOG_WARNING, "No such context '%s' for macro '%s'\n", fullmacro, macro);
00155       else
00156          ast_log(LOG_WARNING, "Context '%s' for macro '%s' lacks 's' extension, priority 1\n", fullmacro, macro);
00157       LOCAL_USER_REMOVE(u);
00158       return 0;
00159    }
00160    
00161    /* Save old info */
00162    oldpriority = chan->priority;
00163    ast_copy_string(oldexten, chan->exten, sizeof(oldexten));
00164    ast_copy_string(oldcontext, chan->context, sizeof(oldcontext));
00165    if (ast_strlen_zero(chan->macrocontext)) {
00166       ast_copy_string(chan->macrocontext, chan->context, sizeof(chan->macrocontext));
00167       ast_copy_string(chan->macroexten, chan->exten, sizeof(chan->macroexten));
00168       chan->macropriority = chan->priority;
00169       setmacrocontext=1;
00170    }
00171    argc = 1;
00172    /* Save old macro variables */
00173    save_macro_exten = pbx_builtin_getvar_helper(chan, "MACRO_EXTEN");
00174    if (save_macro_exten) 
00175       save_macro_exten = strdup(save_macro_exten);
00176    pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", oldexten);
00177 
00178    save_macro_context = pbx_builtin_getvar_helper(chan, "MACRO_CONTEXT");
00179    if (save_macro_context)
00180       save_macro_context = strdup(save_macro_context);
00181    pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", oldcontext);
00182 
00183    save_macro_priority = pbx_builtin_getvar_helper(chan, "MACRO_PRIORITY");
00184    if (save_macro_priority) 
00185       save_macro_priority = strdup(save_macro_priority);
00186    snprintf(pc, sizeof(pc), "%d", oldpriority);
00187    pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", pc);
00188   
00189    save_macro_offset = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET");
00190    if (save_macro_offset) 
00191       save_macro_offset = strdup(save_macro_offset);
00192    pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", NULL);
00193 
00194    /* Setup environment for new run */
00195    chan->exten[0] = 's';
00196    chan->exten[1] = '\0';
00197    ast_copy_string(chan->context, fullmacro, sizeof(chan->context));
00198    chan->priority = 1;
00199 
00200    while((cur = strsep(&rest, "|")) && (argc < MAX_ARGS)) {
00201       /* Save copy of old arguments if we're overwriting some, otherwise
00202          let them pass through to the other macro */
00203       snprintf(varname, sizeof(varname), "ARG%d", argc);
00204       oldargs[argc] = pbx_builtin_getvar_helper(chan, varname);
00205       if (oldargs[argc])
00206          oldargs[argc] = strdup(oldargs[argc]);
00207       pbx_builtin_setvar_helper(chan, varname, cur);
00208       argc++;
00209    }
00210    autoloopflag = ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP);
00211    ast_set_flag(chan, AST_FLAG_IN_AUTOLOOP);
00212    while(ast_exists_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num)) {
00213       /* Reset the macro depth, if it was changed in the last iteration */
00214       pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
00215       if ((res = ast_spawn_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num))) {
00216          /* Something bad happened, or a hangup has been requested. */
00217          if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F')) ||
00218             (res == '*') || (res == '#')) {
00219             /* Just return result as to the previous application as if it had been dialed */
00220             ast_log(LOG_DEBUG, "Oooh, got something to jump out with ('%c')!\n", res);
00221             break;
00222          }
00223          switch(res) {
00224          case MACRO_EXIT_RESULT:
00225             res = 0;
00226             goto out;
00227          case AST_PBX_KEEPALIVE:
00228             if (option_debug)
00229                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);
00230             else if (option_verbose > 1)
00231                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);
00232             goto out;
00233             break;
00234          default:
00235             if (option_debug)
00236                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);
00237             else if (option_verbose > 1)
00238                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);
00239             dead = 1;
00240             goto out;
00241          }
00242       }
00243       if (strcasecmp(chan->context, fullmacro)) {
00244          if (option_verbose > 1)
00245             ast_verbose(VERBOSE_PREFIX_2 "Channel '%s' jumping out of macro '%s'\n", chan->name, macro);
00246          break;
00247       }
00248       /* don't stop executing extensions when we're in "h" */
00249       if (chan->_softhangup && strcasecmp(oldexten,"h") && strcasecmp(chan->macroexten,"h")) {
00250          ast_log(LOG_DEBUG, "Extension %s, macroexten %s, priority %d returned normally even though call was hung up\n",
00251             chan->exten, chan->macroexten, chan->priority);
00252          goto out;
00253       }
00254       chan->priority++;
00255    }
00256    out:
00257    /* Reset the depth back to what it was when the routine was entered (like if we called Macro recursively) */
00258    snprintf(depthc, sizeof(depthc), "%d", depth);
00259    if (!dead) {
00260       pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
00261 
00262       ast_set2_flag(chan, autoloopflag, AST_FLAG_IN_AUTOLOOP);
00263    }
00264 
00265    for (x = 1; x < argc; x++) {
00266       /* Restore old arguments and delete ours */
00267       snprintf(varname, sizeof(varname), "ARG%d", x);
00268       if (oldargs[x]) {
00269          if (!dead)
00270             pbx_builtin_setvar_helper(chan, varname, oldargs[x]);
00271          free(oldargs[x]);
00272       } else if (!dead) {
00273          pbx_builtin_setvar_helper(chan, varname, NULL);
00274       }
00275    }
00276 
00277    /* Restore macro variables */
00278    if (!dead) {
00279       pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", save_macro_exten);
00280       pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", save_macro_context);
00281       pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", save_macro_priority);
00282    }
00283    if (save_macro_exten)
00284       free(save_macro_exten);
00285    if (save_macro_context)
00286       free(save_macro_context);
00287    if (save_macro_priority)
00288       free(save_macro_priority);
00289 
00290    if (!dead && setmacrocontext) {
00291       chan->macrocontext[0] = '\0';
00292       chan->macroexten[0] = '\0';
00293       chan->macropriority = 0;
00294    }
00295 
00296    if (!dead && !strcasecmp(chan->context, fullmacro)) {
00297       /* If we're leaving the macro normally, restore original information */
00298       chan->priority = oldpriority;
00299       ast_copy_string(chan->context, oldcontext, sizeof(chan->context));
00300       if (!(chan->_softhangup & AST_SOFTHANGUP_ASYNCGOTO)) {
00301          /* Copy the extension, so long as we're not in softhangup, where we could be given an asyncgoto */
00302          ast_copy_string(chan->exten, oldexten, sizeof(chan->exten));
00303          if ((offsets = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"))) {
00304             /* Handle macro offset if it's set by checking the availability of step n + offset + 1, otherwise continue
00305                normally if there is any problem */
00306             if (sscanf(offsets, "%d", &offset) == 1) {
00307                if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + offset + 1, chan->cid.cid_num)) {
00308                   chan->priority += offset;
00309                }
00310             }
00311          }
00312       }
00313    }
00314 
00315    if (!dead)
00316       pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", save_macro_offset);
00317    if (save_macro_offset)
00318       free(save_macro_offset);
00319    LOCAL_USER_REMOVE(u);
00320    return res;
00321 }
00322 
00323 static int macroif_exec(struct ast_channel *chan, void *data) 
00324 {
00325    char *expr = NULL, *label_a = NULL, *label_b = NULL;
00326    int res = 0;
00327    struct localuser *u;
00328 
00329    LOCAL_USER_ADD(u);
00330 
00331    expr = ast_strdupa(data);
00332    if (!expr) {
00333       ast_log(LOG_ERROR, "Out of Memory!\n");
00334       LOCAL_USER_REMOVE(u);
00335       return -1;
00336    }
00337 
00338    if ((label_a = strchr(expr, '?'))) {
00339       *label_a = '\0';
00340       label_a++;
00341       if ((label_b = strchr(label_a, ':'))) {
00342          *label_b = '\0';
00343          label_b++;
00344       }
00345       if (pbx_checkcondition(expr))
00346          macro_exec(chan, label_a);
00347       else if (label_b) 
00348          macro_exec(chan, label_b);
00349    } else
00350       ast_log(LOG_WARNING, "Invalid Syntax.\n");
00351 
00352    LOCAL_USER_REMOVE(u);
00353 
00354    return res;
00355 }
00356          
00357 static int macro_exit_exec(struct ast_channel *chan, void *data)
00358 {
00359    return MACRO_EXIT_RESULT;
00360 }
00361 
00362 int unload_module(void)
00363 {
00364    int res;
00365 
00366    res = ast_unregister_application(if_app);
00367    res |= ast_unregister_application(exit_app);
00368    res |= ast_unregister_application(app);
00369 
00370    STANDARD_HANGUP_LOCALUSERS;
00371 
00372    return res;
00373 }
00374 
00375 int load_module(void)
00376 {
00377    int res;
00378 
00379    res = ast_register_application(exit_app, macro_exit_exec, exit_synopsis, exit_descrip);
00380    res |= ast_register_application(if_app, macroif_exec, if_synopsis, if_descrip);
00381    res |= ast_register_application(app, macro_exec, synopsis, descrip);
00382 
00383    return res;
00384 }
00385 
00386 char *description(void)
00387 {
00388    return tdesc;
00389 }
00390 
00391 int usecount(void)
00392 {
00393    int res;
00394    STANDARD_USECOUNT(res);
00395    return res;
00396 }
00397 
00398 char *key()
00399 {
00400    return ASTERISK_GPL_KEY;
00401 }

Generated on Sat Sep 16 05:47:40 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.7