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