00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
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: 44343 $")
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
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 "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
00071 static char *if_descrip =
00072 " MacroIf(<expr>?macroname_a[|arg1][:macroname_b[|arg1]])\n"
00073 "Executes macro defined in <macroname_a> if <expr> is true\n"
00074 "(otherwise <macroname_b> if provided)\n"
00075 "Arguments and return values as in application macro()\n";
00076
00077 static char *exit_descrip =
00078 " MacroExit():\n"
00079 "Causes the currently running macro to exit as if it had\n"
00080 "ended normally by running out of priorities to execute.\n"
00081 "If used outside a macro, will likely cause unexpected\n"
00082 "behavior.\n";
00083
00084 static char *app = "Macro";
00085 static char *if_app = "MacroIf";
00086 static char *exit_app = "MacroExit";
00087
00088 static char *synopsis = "Macro Implementation";
00089 static char *if_synopsis = "Conditional Macro Implementation";
00090 static char *exit_synopsis = "Exit From Macro";
00091
00092 STANDARD_LOCAL_USER;
00093
00094 LOCAL_USER_DECL;
00095
00096 static int macro_exec(struct ast_channel *chan, void *data)
00097 {
00098 char *tmp;
00099 char *cur, *rest;
00100 char *macro;
00101 char fullmacro[80];
00102 char varname[80];
00103 char *oldargs[MAX_ARGS + 1] = { NULL, };
00104 int argc, x;
00105 int res=0;
00106 char oldexten[256]="";
00107 int oldpriority;
00108 char pc[80], depthc[12];
00109 char oldcontext[AST_MAX_CONTEXT] = "";
00110 char *offsets, *s;
00111 int offset, depth = 0, maxdepth = 7;
00112 int setmacrocontext=0;
00113 int autoloopflag, dead = 0;
00114
00115 char *save_macro_exten;
00116 char *save_macro_context;
00117 char *save_macro_priority;
00118 char *save_macro_offset;
00119 struct localuser *u;
00120
00121 if (ast_strlen_zero(data)) {
00122 ast_log(LOG_WARNING, "Macro() requires arguments. See \"show application macro\" for help.\n");
00123 return -1;
00124 }
00125
00126 LOCAL_USER_ADD(u);
00127
00128
00129 s = pbx_builtin_getvar_helper(chan, "MACRO_RECURSION");
00130 if (s)
00131 sscanf(s, "%d", &maxdepth);
00132
00133
00134 tmp = pbx_builtin_getvar_helper(chan, "MACRO_DEPTH");
00135 if (tmp) {
00136 sscanf(tmp, "%d", &depth);
00137 } else {
00138 depth = 0;
00139 }
00140
00141 if (depth >= maxdepth) {
00142 ast_log(LOG_ERROR, "Macro(): possible infinite loop detected. Returning early.\n");
00143 LOCAL_USER_REMOVE(u);
00144 return 0;
00145 }
00146 snprintf(depthc, sizeof(depthc), "%d", depth + 1);
00147 pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
00148
00149 tmp = ast_strdupa(data);
00150 rest = tmp;
00151 macro = strsep(&rest, "|");
00152 if (ast_strlen_zero(macro)) {
00153 ast_log(LOG_WARNING, "Invalid macro name specified\n");
00154 LOCAL_USER_REMOVE(u);
00155 return 0;
00156 }
00157 snprintf(fullmacro, sizeof(fullmacro), "macro-%s", macro);
00158 if (!ast_exists_extension(chan, fullmacro, "s", 1, chan->cid.cid_num)) {
00159 if (!ast_context_find(fullmacro))
00160 ast_log(LOG_WARNING, "No such context '%s' for macro '%s'\n", fullmacro, macro);
00161 else
00162 ast_log(LOG_WARNING, "Context '%s' for macro '%s' lacks 's' extension, priority 1\n", fullmacro, macro);
00163 LOCAL_USER_REMOVE(u);
00164 return 0;
00165 }
00166
00167
00168 oldpriority = chan->priority;
00169 ast_copy_string(oldexten, chan->exten, sizeof(oldexten));
00170 ast_copy_string(oldcontext, chan->context, sizeof(oldcontext));
00171 if (ast_strlen_zero(chan->macrocontext)) {
00172 ast_copy_string(chan->macrocontext, chan->context, sizeof(chan->macrocontext));
00173 ast_copy_string(chan->macroexten, chan->exten, sizeof(chan->macroexten));
00174 chan->macropriority = chan->priority;
00175 setmacrocontext=1;
00176 }
00177 argc = 1;
00178
00179 save_macro_exten = pbx_builtin_getvar_helper(chan, "MACRO_EXTEN");
00180 if (save_macro_exten)
00181 save_macro_exten = strdup(save_macro_exten);
00182 pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", oldexten);
00183
00184 save_macro_context = pbx_builtin_getvar_helper(chan, "MACRO_CONTEXT");
00185 if (save_macro_context)
00186 save_macro_context = strdup(save_macro_context);
00187 pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", oldcontext);
00188
00189 save_macro_priority = pbx_builtin_getvar_helper(chan, "MACRO_PRIORITY");
00190 if (save_macro_priority)
00191 save_macro_priority = strdup(save_macro_priority);
00192 snprintf(pc, sizeof(pc), "%d", oldpriority);
00193 pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", pc);
00194
00195 save_macro_offset = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET");
00196 if (save_macro_offset)
00197 save_macro_offset = strdup(save_macro_offset);
00198 pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", NULL);
00199
00200
00201 chan->exten[0] = 's';
00202 chan->exten[1] = '\0';
00203 ast_copy_string(chan->context, fullmacro, sizeof(chan->context));
00204 chan->priority = 1;
00205
00206 while((cur = strsep(&rest, "|")) && (argc < MAX_ARGS)) {
00207
00208
00209 snprintf(varname, sizeof(varname), "ARG%d", argc);
00210 oldargs[argc] = pbx_builtin_getvar_helper(chan, varname);
00211 if (oldargs[argc])
00212 oldargs[argc] = strdup(oldargs[argc]);
00213 pbx_builtin_setvar_helper(chan, varname, cur);
00214 argc++;
00215 }
00216 autoloopflag = ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP);
00217 ast_set_flag(chan, AST_FLAG_IN_AUTOLOOP);
00218 while(ast_exists_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num)) {
00219
00220 pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
00221 if ((res = ast_spawn_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num))) {
00222
00223 if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F')) ||
00224 (res == '*') || (res == '#')) {
00225
00226 ast_log(LOG_DEBUG, "Oooh, got something to jump out with ('%c')!\n", res);
00227 break;
00228 }
00229 switch(res) {
00230 case MACRO_EXIT_RESULT:
00231 res = 0;
00232 goto out;
00233 case AST_PBX_KEEPALIVE:
00234 if (option_debug)
00235 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);
00236 if (option_verbose > 1)
00237 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);
00238 goto out;
00239 break;
00240 default:
00241 if (option_debug)
00242 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);
00243 if (option_verbose > 1)
00244 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);
00245 dead = 1;
00246 goto out;
00247 }
00248 }
00249 if (strcasecmp(chan->context, fullmacro)) {
00250 if (option_verbose > 1)
00251 ast_verbose(VERBOSE_PREFIX_2 "Channel '%s' jumping out of macro '%s'\n", chan->name, macro);
00252 break;
00253 }
00254
00255 if (chan->_softhangup && strcasecmp(oldexten,"h") && strcasecmp(chan->macroexten,"h")) {
00256 ast_log(LOG_DEBUG, "Extension %s, macroexten %s, priority %d returned normally even though call was hung up\n",
00257 chan->exten, chan->macroexten, chan->priority);
00258 goto out;
00259 }
00260 chan->priority++;
00261 }
00262 out:
00263
00264 snprintf(depthc, sizeof(depthc), "%d", depth);
00265 if (!dead) {
00266 pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
00267
00268 ast_set2_flag(chan, autoloopflag, AST_FLAG_IN_AUTOLOOP);
00269 }
00270
00271 for (x = 1; x < argc; x++) {
00272
00273 snprintf(varname, sizeof(varname), "ARG%d", x);
00274 if (oldargs[x]) {
00275 if (!dead)
00276 pbx_builtin_setvar_helper(chan, varname, oldargs[x]);
00277 free(oldargs[x]);
00278 } else if (!dead) {
00279 pbx_builtin_setvar_helper(chan, varname, NULL);
00280 }
00281 }
00282
00283
00284 if (!dead) {
00285 pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", save_macro_exten);
00286 pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", save_macro_context);
00287 pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", save_macro_priority);
00288 }
00289 if (save_macro_exten)
00290 free(save_macro_exten);
00291 if (save_macro_context)
00292 free(save_macro_context);
00293 if (save_macro_priority)
00294 free(save_macro_priority);
00295
00296 if (!dead && setmacrocontext) {
00297 chan->macrocontext[0] = '\0';
00298 chan->macroexten[0] = '\0';
00299 chan->macropriority = 0;
00300 }
00301
00302 if (!dead && !strcasecmp(chan->context, fullmacro)) {
00303
00304 chan->priority = oldpriority;
00305 ast_copy_string(chan->context, oldcontext, sizeof(chan->context));
00306 if (!(chan->_softhangup & AST_SOFTHANGUP_ASYNCGOTO)) {
00307
00308 ast_copy_string(chan->exten, oldexten, sizeof(chan->exten));
00309 if ((offsets = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"))) {
00310
00311
00312 if (sscanf(offsets, "%d", &offset) == 1) {
00313 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + offset + 1, chan->cid.cid_num)) {
00314 chan->priority += offset;
00315 }
00316 }
00317 }
00318 }
00319 }
00320
00321 if (!dead)
00322 pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", save_macro_offset);
00323 if (save_macro_offset)
00324 free(save_macro_offset);
00325 LOCAL_USER_REMOVE(u);
00326 return res;
00327 }
00328
00329 static int macroif_exec(struct ast_channel *chan, void *data)
00330 {
00331 char *expr = NULL, *label_a = NULL, *label_b = NULL;
00332 int res = 0;
00333 struct localuser *u;
00334
00335 LOCAL_USER_ADD(u);
00336
00337 expr = ast_strdupa(data);
00338 if (!expr) {
00339 ast_log(LOG_ERROR, "Out of Memory!\n");
00340 LOCAL_USER_REMOVE(u);
00341 return -1;
00342 }
00343
00344 if ((label_a = strchr(expr, '?'))) {
00345 *label_a = '\0';
00346 label_a++;
00347 if ((label_b = strchr(label_a, ':'))) {
00348 *label_b = '\0';
00349 label_b++;
00350 }
00351 if (pbx_checkcondition(expr))
00352 macro_exec(chan, label_a);
00353 else if (label_b)
00354 macro_exec(chan, label_b);
00355 } else
00356 ast_log(LOG_WARNING, "Invalid Syntax.\n");
00357
00358 LOCAL_USER_REMOVE(u);
00359
00360 return res;
00361 }
00362
00363 static int macro_exit_exec(struct ast_channel *chan, void *data)
00364 {
00365 return MACRO_EXIT_RESULT;
00366 }
00367
00368 int unload_module(void)
00369 {
00370 int res;
00371
00372 res = ast_unregister_application(if_app);
00373 res |= ast_unregister_application(exit_app);
00374 res |= ast_unregister_application(app);
00375
00376 STANDARD_HANGUP_LOCALUSERS;
00377
00378 return res;
00379 }
00380
00381 int load_module(void)
00382 {
00383 int res;
00384
00385 res = ast_register_application(exit_app, macro_exit_exec, exit_synopsis, exit_descrip);
00386 res |= ast_register_application(if_app, macroif_exec, if_synopsis, if_descrip);
00387 res |= ast_register_application(app, macro_exec, synopsis, descrip);
00388
00389 return res;
00390 }
00391
00392 char *description(void)
00393 {
00394 return tdesc;
00395 }
00396
00397 int usecount(void)
00398 {
00399 int res;
00400 STANDARD_USECOUNT(res);
00401 return res;
00402 }
00403
00404 char *key()
00405 {
00406 return ASTERISK_GPL_KEY;
00407 }