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
00027
00028 #include "asterisk.h"
00029
00030 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 295843 $")
00031
00032 #include "asterisk/file.h"
00033 #include "asterisk/channel.h"
00034 #include "asterisk/pbx.h"
00035 #include "asterisk/module.h"
00036 #include "asterisk/config.h"
00037 #include "asterisk/utils.h"
00038 #include "asterisk/lock.h"
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149 #define MAX_ARGS 80
00150
00151
00152 #define MACRO_EXIT_RESULT 1024
00153
00154 static char *app = "Macro";
00155 static char *if_app = "MacroIf";
00156 static char *exclusive_app = "MacroExclusive";
00157 static char *exit_app = "MacroExit";
00158
00159 static void macro_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan);
00160
00161 struct ast_datastore_info macro_ds_info = {
00162 .type = "MACRO",
00163 .chan_fixup = macro_fixup,
00164 };
00165
00166 static void macro_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
00167 {
00168 int i;
00169 char varname[10];
00170 pbx_builtin_setvar_helper(new_chan, "MACRO_DEPTH", "0");
00171 pbx_builtin_setvar_helper(new_chan, "MACRO_CONTEXT", NULL);
00172 pbx_builtin_setvar_helper(new_chan, "MACRO_EXTEN", NULL);
00173 pbx_builtin_setvar_helper(new_chan, "MACRO_PRIORITY", NULL);
00174 pbx_builtin_setvar_helper(new_chan, "MACRO_OFFSET", NULL);
00175 for (i = 1; i < 100; i++) {
00176 snprintf(varname, sizeof(varname), "ARG%d", i);
00177 while (pbx_builtin_getvar_helper(new_chan, varname)) {
00178
00179 pbx_builtin_setvar_helper(new_chan, varname, NULL);
00180 }
00181 }
00182 }
00183
00184 static struct ast_exten *find_matching_priority(struct ast_context *c, const char *exten, int priority, const char *callerid)
00185 {
00186 struct ast_exten *e;
00187 struct ast_include *i;
00188 struct ast_context *c2;
00189
00190 for (e=ast_walk_context_extensions(c, NULL); e; e=ast_walk_context_extensions(c, e)) {
00191 if (ast_extension_match(ast_get_extension_name(e), exten)) {
00192 int needmatch = ast_get_extension_matchcid(e);
00193 if ((needmatch && ast_extension_match(ast_get_extension_cidmatch(e), callerid)) ||
00194 (!needmatch)) {
00195
00196 struct ast_exten *p;
00197 for (p=ast_walk_extension_priorities(e, NULL); p; p=ast_walk_extension_priorities(e, p)) {
00198 if (priority != ast_get_extension_priority(p))
00199 continue;
00200 return p;
00201 }
00202 }
00203 }
00204 }
00205
00206
00207 for (i=ast_walk_context_includes(c, NULL); i; i=ast_walk_context_includes(c, i)) {
00208 for (c2=ast_walk_contexts(NULL); c2; c2=ast_walk_contexts(c2)) {
00209 if (!strcmp(ast_get_context_name(c2), ast_get_include_name(i))) {
00210 e = find_matching_priority(c2, exten, priority, callerid);
00211 if (e)
00212 return e;
00213 }
00214 }
00215 }
00216 return NULL;
00217 }
00218
00219 static int _macro_exec(struct ast_channel *chan, void *data, int exclusive)
00220 {
00221 const char *s;
00222 char *tmp;
00223 char *cur, *rest;
00224 char *macro;
00225 char fullmacro[80];
00226 char varname[80];
00227 char runningapp[80], runningdata[1024];
00228 char *oldargs[MAX_ARGS + 1] = { NULL, };
00229 int argc, x;
00230 int res=0;
00231 char oldexten[256]="";
00232 int oldpriority, gosub_level = 0;
00233 char pc[80], depthc[12];
00234 char oldcontext[AST_MAX_CONTEXT] = "";
00235 const char *inhangupc;
00236 int offset, depth = 0, maxdepth = 7;
00237 int setmacrocontext=0;
00238 int autoloopflag, inhangup = 0;
00239
00240 char *save_macro_exten;
00241 char *save_macro_context;
00242 char *save_macro_priority;
00243 char *save_macro_offset;
00244 struct ast_datastore *macro_store = ast_channel_datastore_find(chan, ¯o_ds_info, NULL);
00245
00246 if (ast_strlen_zero(data)) {
00247 ast_log(LOG_WARNING, "Macro() requires arguments. See \"core show application macro\" for help.\n");
00248 return -1;
00249 }
00250
00251 do {
00252 if (macro_store) {
00253 break;
00254 }
00255 if (!(macro_store = ast_datastore_alloc(¯o_ds_info, NULL))) {
00256 ast_log(LOG_WARNING, "Unable to allocate new datastore.\n");
00257 break;
00258 }
00259
00260 macro_store->inheritance = DATASTORE_INHERIT_FOREVER;
00261 ast_channel_datastore_add(chan, macro_store);
00262 } while (0);
00263
00264
00265 ast_channel_lock(chan);
00266 if ((s = pbx_builtin_getvar_helper(chan, "MACRO_RECURSION"))) {
00267 sscanf(s, "%30d", &maxdepth);
00268 }
00269
00270
00271 if ((s = pbx_builtin_getvar_helper(chan, "MACRO_DEPTH"))) {
00272 sscanf(s, "%30d", &depth);
00273 }
00274
00275
00276 if (strcmp(chan->exten, "h") == 0)
00277 pbx_builtin_setvar_helper(chan, "MACRO_IN_HANGUP", "1");
00278
00279 if ((inhangupc = pbx_builtin_getvar_helper(chan, "MACRO_IN_HANGUP"))) {
00280 sscanf(inhangupc, "%30d", &inhangup);
00281 }
00282 ast_channel_unlock(chan);
00283
00284 if (depth >= maxdepth) {
00285 ast_log(LOG_ERROR, "Macro(): possible infinite loop detected. Returning early.\n");
00286 return 0;
00287 }
00288 snprintf(depthc, sizeof(depthc), "%d", depth + 1);
00289 pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
00290
00291 tmp = ast_strdupa(data);
00292 rest = tmp;
00293 macro = strsep(&rest, ",");
00294 if (ast_strlen_zero(macro)) {
00295 ast_log(LOG_WARNING, "Invalid macro name specified\n");
00296 return 0;
00297 }
00298
00299 snprintf(fullmacro, sizeof(fullmacro), "macro-%s", macro);
00300 if (!ast_exists_extension(chan, fullmacro, "s", 1, chan->cid.cid_num)) {
00301 if (!ast_context_find(fullmacro))
00302 ast_log(LOG_WARNING, "No such context '%s' for macro '%s'\n", fullmacro, macro);
00303 else
00304 ast_log(LOG_WARNING, "Context '%s' for macro '%s' lacks 's' extension, priority 1\n", fullmacro, macro);
00305 return 0;
00306 }
00307
00308
00309 if (exclusive) {
00310 ast_debug(1, "Locking macrolock for '%s'\n", fullmacro);
00311 ast_autoservice_start(chan);
00312 if (ast_context_lockmacro(fullmacro)) {
00313 ast_log(LOG_WARNING, "Failed to lock macro '%s' as in-use\n", fullmacro);
00314 ast_autoservice_stop(chan);
00315 return 0;
00316 }
00317 ast_autoservice_stop(chan);
00318 }
00319
00320
00321 oldpriority = chan->priority;
00322 ast_copy_string(oldexten, chan->exten, sizeof(oldexten));
00323 ast_copy_string(oldcontext, chan->context, sizeof(oldcontext));
00324 if (ast_strlen_zero(chan->macrocontext)) {
00325 ast_copy_string(chan->macrocontext, chan->context, sizeof(chan->macrocontext));
00326 ast_copy_string(chan->macroexten, chan->exten, sizeof(chan->macroexten));
00327 chan->macropriority = chan->priority;
00328 setmacrocontext=1;
00329 }
00330 argc = 1;
00331
00332 save_macro_exten = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_EXTEN"));
00333 pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", oldexten);
00334
00335 save_macro_context = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_CONTEXT"));
00336 pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", oldcontext);
00337
00338 save_macro_priority = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_PRIORITY"));
00339 snprintf(pc, sizeof(pc), "%d", oldpriority);
00340 pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", pc);
00341
00342 save_macro_offset = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"));
00343 pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", NULL);
00344
00345
00346 chan->exten[0] = 's';
00347 chan->exten[1] = '\0';
00348 ast_copy_string(chan->context, fullmacro, sizeof(chan->context));
00349 chan->priority = 1;
00350
00351 ast_channel_lock(chan);
00352 while((cur = strsep(&rest, ",")) && (argc < MAX_ARGS)) {
00353 const char *argp;
00354
00355
00356 snprintf(varname, sizeof(varname), "ARG%d", argc);
00357 if ((argp = pbx_builtin_getvar_helper(chan, varname))) {
00358 oldargs[argc] = ast_strdup(argp);
00359 }
00360 pbx_builtin_setvar_helper(chan, varname, cur);
00361 argc++;
00362 }
00363 ast_channel_unlock(chan);
00364 autoloopflag = ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP);
00365 ast_set_flag(chan, AST_FLAG_IN_AUTOLOOP);
00366 while(ast_exists_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num)) {
00367 struct ast_context *c;
00368 struct ast_exten *e;
00369 int foundx;
00370 runningapp[0] = '\0';
00371 runningdata[0] = '\0';
00372
00373
00374 if (ast_rdlock_contexts()) {
00375 ast_log(LOG_WARNING, "Failed to lock contexts list\n");
00376 } else {
00377 for (c = ast_walk_contexts(NULL), e = NULL; c; c = ast_walk_contexts(c)) {
00378 if (!strcmp(ast_get_context_name(c), chan->context)) {
00379 if (ast_rdlock_context(c)) {
00380 ast_log(LOG_WARNING, "Unable to lock context?\n");
00381 } else {
00382 e = find_matching_priority(c, chan->exten, chan->priority, chan->cid.cid_num);
00383 if (e) {
00384 ast_copy_string(runningapp, ast_get_extension_app(e), sizeof(runningapp));
00385 ast_copy_string(runningdata, ast_get_extension_app_data(e), sizeof(runningdata));
00386 }
00387 ast_unlock_context(c);
00388 }
00389 break;
00390 }
00391 }
00392 }
00393 ast_unlock_contexts();
00394
00395
00396 pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
00397
00398 if ((res = ast_spawn_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num, &foundx,1))) {
00399
00400 if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F')) ||
00401 (res == '*') || (res == '#')) {
00402
00403 ast_debug(1, "Oooh, got something to jump out with ('%c')!\n", res);
00404 break;
00405 }
00406 switch(res) {
00407 case MACRO_EXIT_RESULT:
00408 res = 0;
00409 goto out;
00410 default:
00411 ast_debug(2, "Spawn extension (%s,%s,%d) exited non-zero on '%s' in macro '%s'\n", chan->context, chan->exten, chan->priority, chan->name, macro);
00412 ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s' in macro '%s'\n", chan->context, chan->exten, chan->priority, chan->name, macro);
00413 goto out;
00414 }
00415 }
00416
00417 ast_debug(1, "Executed application: %s\n", runningapp);
00418
00419 if (!strcasecmp(runningapp, "GOSUB")) {
00420 gosub_level++;
00421 ast_debug(1, "Incrementing gosub_level\n");
00422 } else if (!strcasecmp(runningapp, "GOSUBIF")) {
00423 char tmp2[1024], *cond, *app_arg, *app2 = tmp2;
00424 pbx_substitute_variables_helper(chan, runningdata, tmp2, sizeof(tmp2) - 1);
00425 cond = strsep(&app2, "?");
00426 app_arg = strsep(&app2, ":");
00427 if (pbx_checkcondition(cond)) {
00428 if (!ast_strlen_zero(app_arg)) {
00429 gosub_level++;
00430 ast_debug(1, "Incrementing gosub_level\n");
00431 }
00432 } else {
00433 if (!ast_strlen_zero(app2)) {
00434 gosub_level++;
00435 ast_debug(1, "Incrementing gosub_level\n");
00436 }
00437 }
00438 } else if (!strcasecmp(runningapp, "RETURN")) {
00439 gosub_level--;
00440 ast_debug(1, "Decrementing gosub_level\n");
00441 } else if (!strcasecmp(runningapp, "STACKPOP")) {
00442 gosub_level--;
00443 ast_debug(1, "Decrementing gosub_level\n");
00444 } else if (!strncasecmp(runningapp, "EXEC", 4)) {
00445
00446 char tmp2[1024], *tmp3 = NULL;
00447 pbx_substitute_variables_helper(chan, runningdata, tmp2, sizeof(tmp2) - 1);
00448 if (!strcasecmp(runningapp, "EXECIF")) {
00449 tmp3 = strchr(tmp2, '|');
00450 if (tmp3)
00451 *tmp3++ = '\0';
00452 if (!pbx_checkcondition(tmp2))
00453 tmp3 = NULL;
00454 } else
00455 tmp3 = tmp2;
00456
00457 if (tmp3)
00458 ast_debug(1, "Last app: %s\n", tmp3);
00459
00460 if (tmp3 && !strncasecmp(tmp3, "GOSUB", 5)) {
00461 gosub_level++;
00462 ast_debug(1, "Incrementing gosub_level\n");
00463 } else if (tmp3 && !strncasecmp(tmp3, "RETURN", 6)) {
00464 gosub_level--;
00465 ast_debug(1, "Decrementing gosub_level\n");
00466 } else if (tmp3 && !strncasecmp(tmp3, "STACKPOP", 8)) {
00467 gosub_level--;
00468 ast_debug(1, "Decrementing gosub_level\n");
00469 }
00470 }
00471
00472 if (gosub_level == 0 && strcasecmp(chan->context, fullmacro)) {
00473 ast_verb(2, "Channel '%s' jumping out of macro '%s'\n", chan->name, macro);
00474 break;
00475 }
00476
00477
00478 if (ast_check_hangup(chan) && !inhangup) {
00479 ast_debug(1, "Extension %s, macroexten %s, priority %d returned normally even though call was hung up\n", chan->exten, chan->macroexten, chan->priority);
00480 goto out;
00481 }
00482 chan->priority++;
00483 }
00484 out:
00485
00486
00487 ast_channel_lock(chan);
00488
00489
00490 snprintf(depthc, sizeof(depthc), "%d", depth);
00491 pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
00492 ast_set2_flag(chan, autoloopflag, AST_FLAG_IN_AUTOLOOP);
00493
00494 for (x = 1; x < argc; x++) {
00495
00496 snprintf(varname, sizeof(varname), "ARG%d", x);
00497 if (oldargs[x]) {
00498 pbx_builtin_setvar_helper(chan, varname, oldargs[x]);
00499 ast_free(oldargs[x]);
00500 } else {
00501 pbx_builtin_setvar_helper(chan, varname, NULL);
00502 }
00503 }
00504
00505
00506 pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", save_macro_exten);
00507 pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", save_macro_context);
00508 pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", save_macro_priority);
00509 if (save_macro_exten)
00510 ast_free(save_macro_exten);
00511 if (save_macro_context)
00512 ast_free(save_macro_context);
00513 if (save_macro_priority)
00514 ast_free(save_macro_priority);
00515
00516 if (setmacrocontext) {
00517 chan->macrocontext[0] = '\0';
00518 chan->macroexten[0] = '\0';
00519 chan->macropriority = 0;
00520 }
00521
00522 if (!strcasecmp(chan->context, fullmacro)) {
00523 const char *offsets;
00524
00525
00526 chan->priority = oldpriority;
00527 ast_copy_string(chan->context, oldcontext, sizeof(chan->context));
00528 ast_copy_string(chan->exten, oldexten, sizeof(chan->exten));
00529 if ((offsets = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"))) {
00530
00531
00532 if (sscanf(offsets, "%30d", &offset) == 1) {
00533 if (ast_exists_extension(chan, chan->context, chan->exten,
00534 chan->priority + offset + 1,
00535 chan->cid.cid_num)) {
00536 chan->priority += offset;
00537 }
00538 }
00539 }
00540 }
00541
00542 pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", save_macro_offset);
00543 if (save_macro_offset)
00544 ast_free(save_macro_offset);
00545
00546
00547 if (exclusive) {
00548 ast_debug(1, "Unlocking macrolock for '%s'\n", fullmacro);
00549 if (ast_context_unlockmacro(fullmacro)) {
00550 ast_log(LOG_ERROR, "Failed to unlock macro '%s' - that isn't good\n", fullmacro);
00551 res = 0;
00552 }
00553 }
00554 ast_channel_unlock(chan);
00555
00556 return res;
00557 }
00558
00559 static int macro_exec(struct ast_channel *chan, void *data)
00560 {
00561 return _macro_exec(chan, data, 0);
00562 }
00563
00564 static int macroexclusive_exec(struct ast_channel *chan, void *data)
00565 {
00566 return _macro_exec(chan, data, 1);
00567 }
00568
00569 static int macroif_exec(struct ast_channel *chan, void *data)
00570 {
00571 char *expr = NULL, *label_a = NULL, *label_b = NULL;
00572 int res = 0;
00573
00574 if (!(expr = ast_strdupa(data)))
00575 return -1;
00576
00577 if ((label_a = strchr(expr, '?'))) {
00578 *label_a = '\0';
00579 label_a++;
00580 if ((label_b = strchr(label_a, ':'))) {
00581 *label_b = '\0';
00582 label_b++;
00583 }
00584 if (pbx_checkcondition(expr))
00585 res = macro_exec(chan, label_a);
00586 else if (label_b)
00587 res = macro_exec(chan, label_b);
00588 } else
00589 ast_log(LOG_WARNING, "Invalid Syntax.\n");
00590
00591 return res;
00592 }
00593
00594 static int macro_exit_exec(struct ast_channel *chan, void *data)
00595 {
00596 return MACRO_EXIT_RESULT;
00597 }
00598
00599 static int unload_module(void)
00600 {
00601 int res;
00602
00603 res = ast_unregister_application(if_app);
00604 res |= ast_unregister_application(exit_app);
00605 res |= ast_unregister_application(app);
00606 res |= ast_unregister_application(exclusive_app);
00607
00608 return res;
00609 }
00610
00611 static int load_module(void)
00612 {
00613 int res;
00614
00615 res = ast_register_application_xml(exit_app, macro_exit_exec);
00616 res |= ast_register_application_xml(if_app, macroif_exec);
00617 res |= ast_register_application_xml(exclusive_app, macroexclusive_exec);
00618 res |= ast_register_application_xml(app, macro_exec);
00619
00620 return res;
00621 }
00622
00623 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Extension Macros");