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