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: 338084 $")
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 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(chan->exten, "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(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
00308 if (!ast_context_find(fullmacro))
00309 ast_log(LOG_WARNING, "No such context '%s' for macro '%s'\n", fullmacro, macro);
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 = chan->priority;
00333 ast_copy_string(oldexten, chan->exten, sizeof(oldexten));
00334 ast_copy_string(oldcontext, chan->context, sizeof(oldcontext));
00335 if (ast_strlen_zero(chan->macrocontext)) {
00336 ast_copy_string(chan->macrocontext, chan->context, sizeof(chan->macrocontext));
00337 ast_copy_string(chan->macroexten, chan->exten, sizeof(chan->macroexten));
00338 chan->macropriority = chan->priority;
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 chan->exten[0] = 's';
00360 chan->exten[1] = '\0';
00361 ast_copy_string(chan->context, fullmacro, sizeof(chan->context));
00362 chan->priority = 1;
00363
00364 ast_channel_lock(chan);
00365 while((cur = strsep(&rest, ",")) && (argc < MAX_ARGS)) {
00366 const char *argp;
00367
00368
00369 snprintf(varname, sizeof(varname), "ARG%d", argc);
00370 if ((argp = pbx_builtin_getvar_helper(chan, varname))) {
00371 oldargs[argc] = ast_strdup(argp);
00372 }
00373 pbx_builtin_setvar_helper(chan, varname, cur);
00374 argc++;
00375 }
00376 ast_channel_unlock(chan);
00377 autoloopflag = ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP);
00378 ast_set_flag(chan, AST_FLAG_IN_AUTOLOOP);
00379 while (ast_exists_extension(chan, chan->context, chan->exten, chan->priority,
00380 S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
00381 struct ast_context *c;
00382 struct ast_exten *e;
00383 int foundx;
00384 runningapp[0] = '\0';
00385 runningdata[0] = '\0';
00386
00387
00388 if (ast_rdlock_contexts()) {
00389 ast_log(LOG_WARNING, "Failed to lock contexts list\n");
00390 } else {
00391 for (c = ast_walk_contexts(NULL), e = NULL; c; c = ast_walk_contexts(c)) {
00392 if (!strcmp(ast_get_context_name(c), chan->context)) {
00393 if (ast_rdlock_context(c)) {
00394 ast_log(LOG_WARNING, "Unable to lock context?\n");
00395 } else {
00396 e = find_matching_priority(c, chan->exten, chan->priority,
00397 S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL));
00398 if (e) {
00399 ast_copy_string(runningapp, ast_get_extension_app(e), sizeof(runningapp));
00400 ast_copy_string(runningdata, ast_get_extension_app_data(e), sizeof(runningdata));
00401 }
00402 ast_unlock_context(c);
00403 }
00404 break;
00405 }
00406 }
00407 }
00408 ast_unlock_contexts();
00409
00410
00411 pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
00412
00413 res = ast_spawn_extension(chan, chan->context, chan->exten, chan->priority,
00414 S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
00415 &foundx, 1);
00416 if (res) {
00417
00418 if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F')) ||
00419 (res == '*') || (res == '#')) {
00420
00421 ast_debug(1, "Oooh, got something to jump out with ('%c')!\n", res);
00422 break;
00423 }
00424 switch(res) {
00425 case MACRO_EXIT_RESULT:
00426 res = 0;
00427 goto out;
00428 default:
00429 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);
00430 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);
00431 goto out;
00432 }
00433 }
00434
00435 ast_debug(1, "Executed application: %s\n", runningapp);
00436
00437 if (!strcasecmp(runningapp, "GOSUB")) {
00438 gosub_level++;
00439 ast_debug(1, "Incrementing gosub_level\n");
00440 } else if (!strcasecmp(runningapp, "GOSUBIF")) {
00441 char *cond, *app_arg;
00442 char *app2;
00443 ast_str_substitute_variables(&tmp_subst, 0, chan, runningdata);
00444 app2 = ast_str_buffer(tmp_subst);
00445 cond = strsep(&app2, "?");
00446 app_arg = strsep(&app2, ":");
00447 if (pbx_checkcondition(cond)) {
00448 if (!ast_strlen_zero(app_arg)) {
00449 gosub_level++;
00450 ast_debug(1, "Incrementing gosub_level\n");
00451 }
00452 } else {
00453 if (!ast_strlen_zero(app2)) {
00454 gosub_level++;
00455 ast_debug(1, "Incrementing gosub_level\n");
00456 }
00457 }
00458 } else if (!strcasecmp(runningapp, "RETURN")) {
00459 gosub_level--;
00460 ast_debug(1, "Decrementing gosub_level\n");
00461 } else if (!strcasecmp(runningapp, "STACKPOP")) {
00462 gosub_level--;
00463 ast_debug(1, "Decrementing gosub_level\n");
00464 } else if (!strncasecmp(runningapp, "EXEC", 4)) {
00465
00466 char *tmp2, *tmp3 = NULL;
00467 ast_str_substitute_variables(&tmp_subst, 0, chan, runningdata);
00468 tmp2 = ast_str_buffer(tmp_subst);
00469 if (!strcasecmp(runningapp, "EXECIF")) {
00470 if ((tmp3 = strchr(tmp2, '|'))) {
00471 *tmp3++ = '\0';
00472 }
00473 if (!pbx_checkcondition(tmp2)) {
00474 tmp3 = NULL;
00475 }
00476 } else {
00477 tmp3 = tmp2;
00478 }
00479
00480 if (tmp3) {
00481 ast_debug(1, "Last app: %s\n", tmp3);
00482 }
00483
00484 if (tmp3 && !strncasecmp(tmp3, "GOSUB", 5)) {
00485 gosub_level++;
00486 ast_debug(1, "Incrementing gosub_level\n");
00487 } else if (tmp3 && !strncasecmp(tmp3, "RETURN", 6)) {
00488 gosub_level--;
00489 ast_debug(1, "Decrementing gosub_level\n");
00490 } else if (tmp3 && !strncasecmp(tmp3, "STACKPOP", 8)) {
00491 gosub_level--;
00492 ast_debug(1, "Decrementing gosub_level\n");
00493 }
00494 }
00495
00496 if (gosub_level == 0 && strcasecmp(chan->context, fullmacro)) {
00497 ast_verb(2, "Channel '%s' jumping out of macro '%s'\n", chan->name, macro);
00498 break;
00499 }
00500
00501
00502 if (ast_check_hangup(chan) && !inhangup) {
00503 ast_debug(1, "Extension %s, macroexten %s, priority %d returned normally even though call was hung up\n", chan->exten, chan->macroexten, chan->priority);
00504 goto out;
00505 }
00506 chan->priority++;
00507 }
00508 out:
00509
00510
00511 ast_channel_lock(chan);
00512
00513
00514 snprintf(depthc, sizeof(depthc), "%d", depth);
00515 pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
00516 ast_set2_flag(chan, autoloopflag, AST_FLAG_IN_AUTOLOOP);
00517
00518 for (x = 1; x < argc; x++) {
00519
00520 snprintf(varname, sizeof(varname), "ARG%d", x);
00521 if (oldargs[x]) {
00522 pbx_builtin_setvar_helper(chan, varname, oldargs[x]);
00523 ast_free(oldargs[x]);
00524 } else {
00525 pbx_builtin_setvar_helper(chan, varname, NULL);
00526 }
00527 }
00528
00529
00530 pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", save_macro_exten);
00531 pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", save_macro_context);
00532 pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", save_macro_priority);
00533 if (save_macro_exten)
00534 ast_free(save_macro_exten);
00535 if (save_macro_context)
00536 ast_free(save_macro_context);
00537 if (save_macro_priority)
00538 ast_free(save_macro_priority);
00539
00540 if (setmacrocontext) {
00541 chan->macrocontext[0] = '\0';
00542 chan->macroexten[0] = '\0';
00543 chan->macropriority = 0;
00544 }
00545
00546 if (!strcasecmp(chan->context, fullmacro)) {
00547 const char *offsets;
00548
00549
00550 chan->priority = oldpriority;
00551 ast_copy_string(chan->context, oldcontext, sizeof(chan->context));
00552 ast_copy_string(chan->exten, oldexten, sizeof(chan->exten));
00553 if ((offsets = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"))) {
00554
00555
00556 if (sscanf(offsets, "%30d", &offset) == 1) {
00557 if (ast_exists_extension(chan, chan->context, chan->exten,
00558 chan->priority + offset + 1,
00559 S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
00560 chan->priority += offset;
00561 }
00562 }
00563 }
00564 }
00565
00566 pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", save_macro_offset);
00567 if (save_macro_offset)
00568 ast_free(save_macro_offset);
00569
00570
00571 if (exclusive) {
00572 ast_debug(1, "Unlocking macrolock for '%s'\n", fullmacro);
00573 if (ast_context_unlockmacro(fullmacro)) {
00574 ast_log(LOG_ERROR, "Failed to unlock macro '%s' - that isn't good\n", fullmacro);
00575 res = 0;
00576 }
00577 }
00578 ast_channel_unlock(chan);
00579 ast_free(tmp_subst);
00580
00581 return res;
00582 }
00583
00584 static int macro_exec(struct ast_channel *chan, const char *data)
00585 {
00586 return _macro_exec(chan, data, 0);
00587 }
00588
00589 static int macroexclusive_exec(struct ast_channel *chan, const char *data)
00590 {
00591 return _macro_exec(chan, data, 1);
00592 }
00593
00594 static int macroif_exec(struct ast_channel *chan, const char *data)
00595 {
00596 char *expr = NULL, *label_a = NULL, *label_b = NULL;
00597 int res = 0;
00598
00599 if (!(expr = ast_strdupa(data)))
00600 return -1;
00601
00602 if ((label_a = strchr(expr, '?'))) {
00603 *label_a = '\0';
00604 label_a++;
00605 if ((label_b = strchr(label_a, ':'))) {
00606 *label_b = '\0';
00607 label_b++;
00608 }
00609 if (pbx_checkcondition(expr))
00610 res = macro_exec(chan, label_a);
00611 else if (label_b)
00612 res = macro_exec(chan, label_b);
00613 } else
00614 ast_log(LOG_WARNING, "Invalid Syntax.\n");
00615
00616 return res;
00617 }
00618
00619 static int macro_exit_exec(struct ast_channel *chan, const char *data)
00620 {
00621 return MACRO_EXIT_RESULT;
00622 }
00623
00624 static int unload_module(void)
00625 {
00626 int res;
00627
00628 res = ast_unregister_application(if_app);
00629 res |= ast_unregister_application(exit_app);
00630 res |= ast_unregister_application(app);
00631 res |= ast_unregister_application(exclusive_app);
00632
00633 return res;
00634 }
00635
00636 static int load_module(void)
00637 {
00638 int res;
00639
00640 res = ast_register_application_xml(exit_app, macro_exit_exec);
00641 res |= ast_register_application_xml(if_app, macroif_exec);
00642 res |= ast_register_application_xml(exclusive_app, macroexclusive_exec);
00643 res |= ast_register_application_xml(app, macro_exec);
00644
00645 return res;
00646 }
00647
00648 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Extension Macros");