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: 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
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
00123 s = pbx_builtin_getvar_helper(chan, "MACRO_RECURSION");
00124 if (s)
00125 sscanf(s, "%d", &maxdepth);
00126
00127
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
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
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
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
00202
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
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
00217 if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F')) ||
00218 (res == '*') || (res == '#')) {
00219
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
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
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
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
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
00298 chan->priority = oldpriority;
00299 ast_copy_string(chan->context, oldcontext, sizeof(chan->context));
00300 if (!(chan->_softhangup & AST_SOFTHANGUP_ASYNCGOTO)) {
00301
00302 ast_copy_string(chan->exten, oldexten, sizeof(chan->exten));
00303 if ((offsets = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"))) {
00304
00305
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 }