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: 24837 $")
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
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
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
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
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
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
00197
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
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
00212 if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F')) ||
00213 (res == '*') || (res == '#')) {
00214
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
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
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
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
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
00293 chan->priority = oldpriority;
00294 ast_copy_string(chan->context, oldcontext, sizeof(chan->context));
00295 if (!(chan->_softhangup & AST_SOFTHANGUP_ASYNCGOTO)) {
00296
00297 ast_copy_string(chan->exten, oldexten, sizeof(chan->exten));
00298 if ((offsets = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"))) {
00299
00300
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 (pbx_checkcondition(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 }