Fix a few things I missed to ensure zt_chan_conf structure is not modified in mkintf
[asterisk-bristuff.git] / apps / app_macro.c
blob7906ed4b2a107bf2c5d840a128163d9be3a53ae7
1 /*
2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
19 /*! \file
21 * \brief Dial plan macro Implementation
23 * \author Mark Spencer <markster@digium.com>
25 * \ingroup applications
28 #include "asterisk.h"
30 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <sys/types.h>
38 #include "asterisk/file.h"
39 #include "asterisk/logger.h"
40 #include "asterisk/channel.h"
41 #include "asterisk/pbx.h"
42 #include "asterisk/module.h"
43 #include "asterisk/options.h"
44 #include "asterisk/config.h"
45 #include "asterisk/utils.h"
46 #include "asterisk/lock.h"
48 #define MAX_ARGS 80
50 /* special result value used to force macro exit */
51 #define MACRO_EXIT_RESULT 1024
53 static char *descrip =
54 " Macro(macroname|arg1|arg2...): Executes a macro using the context\n"
55 "'macro-<macroname>', jumping to the 's' extension of that context and\n"
56 "executing each step, then returning when the steps end. \n"
57 "The calling extension, context, and priority are stored in ${MACRO_EXTEN}, \n"
58 "${MACRO_CONTEXT} and ${MACRO_PRIORITY} respectively. Arguments become\n"
59 "${ARG1}, ${ARG2}, etc in the macro context.\n"
60 "If you Goto out of the Macro context, the Macro will terminate and control\n"
61 "will be returned at the location of the Goto.\n"
62 "If ${MACRO_OFFSET} is set at termination, Macro will attempt to continue\n"
63 "at priority MACRO_OFFSET + N + 1 if such a step exists, and N + 1 otherwise.\n"
64 "Extensions: While a macro is being executed, it becomes the current context.\n"
65 " This means that if a hangup occurs, for instance, that the macro\n"
66 " will be searched for an 'h' extension, NOT the context from which\n"
67 " the macro was called. So, make sure to define all appropriate\n"
68 " extensions in your macro! (you can use 'catch' in AEL) \n"
69 "WARNING: Because of the way Macro is implemented (it executes the priorities\n"
70 " contained within it via sub-engine), and a fixed per-thread\n"
71 " memory stack allowance, macros are limited to 7 levels\n"
72 " of nesting (macro calling macro calling macro, etc.); It\n"
73 " may be possible that stack-intensive applications in deeply nested macros\n"
74 " could cause asterisk to crash earlier than this limit.\n";
76 static char *if_descrip =
77 " MacroIf(<expr>?macroname_a[|arg1][:macroname_b[|arg1]])\n"
78 "Executes macro defined in <macroname_a> if <expr> is true\n"
79 "(otherwise <macroname_b> if provided)\n"
80 "Arguments and return values as in application macro()\n";
82 static char *exclusive_descrip =
83 " MacroExclusive(macroname|arg1|arg2...):\n"
84 "Executes macro defined in the context 'macro-macroname'\n"
85 "Only one call at a time may run the macro.\n"
86 "(we'll wait if another call is busy executing in the Macro)\n"
87 "Arguments and return values as in application Macro()\n";
89 static char *exit_descrip =
90 " MacroExit():\n"
91 "Causes the currently running macro to exit as if it had\n"
92 "ended normally by running out of priorities to execute.\n"
93 "If used outside a macro, will likely cause unexpected\n"
94 "behavior.\n";
96 static char *app = "Macro";
97 static char *if_app = "MacroIf";
98 static char *exclusive_app = "MacroExclusive";
99 static char *exit_app = "MacroExit";
101 static char *synopsis = "Macro Implementation";
102 static char *if_synopsis = "Conditional Macro Implementation";
103 static char *exclusive_synopsis = "Exclusive Macro Implementation";
104 static char *exit_synopsis = "Exit From Macro";
107 static struct ast_exten *find_matching_priority(struct ast_context *c, const char *exten, int priority, const char *callerid)
109 struct ast_exten *e;
110 struct ast_include *i;
111 struct ast_context *c2;
113 for (e=ast_walk_context_extensions(c, NULL); e; e=ast_walk_context_extensions(c, e)) {
114 if (ast_extension_match(ast_get_extension_name(e), exten)) {
115 int needmatch = ast_get_extension_matchcid(e);
116 if ((needmatch && ast_extension_match(ast_get_extension_cidmatch(e), callerid)) ||
117 (!needmatch)) {
118 /* This is the matching extension we want */
119 struct ast_exten *p;
120 for (p=ast_walk_extension_priorities(e, NULL); p; p=ast_walk_extension_priorities(e, p)) {
121 if (priority != ast_get_extension_priority(p))
122 continue;
123 return p;
129 /* No match; run through includes */
130 for (i=ast_walk_context_includes(c, NULL); i; i=ast_walk_context_includes(c, i)) {
131 for (c2=ast_walk_contexts(NULL); c2; c2=ast_walk_contexts(c2)) {
132 if (!strcmp(ast_get_context_name(c2), ast_get_include_name(i))) {
133 e = find_matching_priority(c2, exten, priority, callerid);
134 if (e)
135 return e;
139 return NULL;
142 static int _macro_exec(struct ast_channel *chan, void *data, int exclusive)
144 const char *s;
145 char *tmp;
146 char *cur, *rest;
147 char *macro;
148 char fullmacro[80];
149 char varname[80];
150 char runningapp[80], runningdata[1024];
151 char *oldargs[MAX_ARGS + 1] = { NULL, };
152 int argc, x;
153 int res=0;
154 char oldexten[256]="";
155 int oldpriority, gosub_level = 0;
156 char pc[80], depthc[12];
157 char oldcontext[AST_MAX_CONTEXT] = "";
158 const char *inhangupc;
159 int offset, depth = 0, maxdepth = 7;
160 int setmacrocontext=0;
161 int autoloopflag, dead = 0, inhangup = 0;
163 char *save_macro_exten;
164 char *save_macro_context;
165 char *save_macro_priority;
166 char *save_macro_offset;
167 struct ast_module_user *u;
169 if (ast_strlen_zero(data)) {
170 ast_log(LOG_WARNING, "Macro() requires arguments. See \"show application macro\" for help.\n");
171 return -1;
174 u = ast_module_user_add(chan);
176 /* does the user want a deeper rabbit hole? */
177 s = pbx_builtin_getvar_helper(chan, "MACRO_RECURSION");
178 if (s)
179 sscanf(s, "%d", &maxdepth);
181 /* Count how many levels deep the rabbit hole goes */
182 s = pbx_builtin_getvar_helper(chan, "MACRO_DEPTH");
183 if (s)
184 sscanf(s, "%d", &depth);
185 /* Used for detecting whether to return when a Macro is called from another Macro after hangup */
186 if (strcmp(chan->exten, "h") == 0)
187 pbx_builtin_setvar_helper(chan, "MACRO_IN_HANGUP", "1");
188 inhangupc = pbx_builtin_getvar_helper(chan, "MACRO_IN_HANGUP");
189 if (!ast_strlen_zero(inhangupc))
190 sscanf(inhangupc, "%d", &inhangup);
192 if (depth >= maxdepth) {
193 ast_log(LOG_ERROR, "Macro(): possible infinite loop detected. Returning early.\n");
194 ast_module_user_remove(u);
195 return 0;
197 snprintf(depthc, sizeof(depthc), "%d", depth + 1);
198 pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
200 tmp = ast_strdupa(data);
201 rest = tmp;
202 macro = strsep(&rest, "|");
203 if (ast_strlen_zero(macro)) {
204 ast_log(LOG_WARNING, "Invalid macro name specified\n");
205 ast_module_user_remove(u);
206 return 0;
209 snprintf(fullmacro, sizeof(fullmacro), "macro-%s", macro);
210 if (!ast_exists_extension(chan, fullmacro, "s", 1, chan->cid.cid_num)) {
211 if (!ast_context_find(fullmacro))
212 ast_log(LOG_WARNING, "No such context '%s' for macro '%s'\n", fullmacro, macro);
213 else
214 ast_log(LOG_WARNING, "Context '%s' for macro '%s' lacks 's' extension, priority 1\n", fullmacro, macro);
215 ast_module_user_remove(u);
216 return 0;
219 /* If we are to run the macro exclusively, take the mutex */
220 if (exclusive) {
221 ast_log(LOG_DEBUG, "Locking macrolock for '%s'\n", fullmacro);
222 ast_autoservice_start(chan);
223 if (ast_context_lockmacro(fullmacro)) {
224 ast_log(LOG_WARNING, "Failed to lock macro '%s' as in-use\n", fullmacro);
225 ast_autoservice_stop(chan);
226 ast_module_user_remove(u);
228 return 0;
230 ast_autoservice_stop(chan);
233 /* Save old info */
234 oldpriority = chan->priority;
235 ast_copy_string(oldexten, chan->exten, sizeof(oldexten));
236 ast_copy_string(oldcontext, chan->context, sizeof(oldcontext));
237 if (ast_strlen_zero(chan->macrocontext)) {
238 ast_copy_string(chan->macrocontext, chan->context, sizeof(chan->macrocontext));
239 ast_copy_string(chan->macroexten, chan->exten, sizeof(chan->macroexten));
240 chan->macropriority = chan->priority;
241 setmacrocontext=1;
243 argc = 1;
244 /* Save old macro variables */
245 save_macro_exten = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_EXTEN"));
246 pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", oldexten);
248 save_macro_context = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_CONTEXT"));
249 pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", oldcontext);
251 save_macro_priority = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_PRIORITY"));
252 snprintf(pc, sizeof(pc), "%d", oldpriority);
253 pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", pc);
255 save_macro_offset = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"));
256 pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", NULL);
258 /* Setup environment for new run */
259 chan->exten[0] = 's';
260 chan->exten[1] = '\0';
261 ast_copy_string(chan->context, fullmacro, sizeof(chan->context));
262 chan->priority = 1;
264 while((cur = strsep(&rest, "|")) && (argc < MAX_ARGS)) {
265 const char *s;
266 /* Save copy of old arguments if we're overwriting some, otherwise
267 let them pass through to the other macro */
268 snprintf(varname, sizeof(varname), "ARG%d", argc);
269 s = pbx_builtin_getvar_helper(chan, varname);
270 if (s)
271 oldargs[argc] = ast_strdup(s);
272 pbx_builtin_setvar_helper(chan, varname, cur);
273 argc++;
275 autoloopflag = ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP);
276 ast_set_flag(chan, AST_FLAG_IN_AUTOLOOP);
277 while(ast_exists_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num)) {
278 struct ast_context *c;
279 struct ast_exten *e;
280 runningapp[0] = '\0';
281 runningdata[0] = '\0';
283 /* What application will execute? */
284 if (ast_rdlock_contexts()) {
285 ast_log(LOG_WARNING, "Failed to lock contexts list\n");
286 } else {
287 for (c = ast_walk_contexts(NULL), e = NULL; c; c = ast_walk_contexts(c)) {
288 if (!strcmp(ast_get_context_name(c), chan->context)) {
289 if (ast_lock_context(c)) {
290 ast_log(LOG_WARNING, "Unable to lock context?\n");
291 } else {
292 e = find_matching_priority(c, chan->exten, chan->priority, chan->cid.cid_num);
293 if (e) { /* This will only be undefined for pbx_realtime, which is majorly broken. */
294 ast_copy_string(runningapp, ast_get_extension_app(e), sizeof(runningapp));
295 ast_copy_string(runningdata, ast_get_extension_app_data(e), sizeof(runningdata));
297 ast_unlock_context(c);
299 break;
303 ast_unlock_contexts();
305 /* Reset the macro depth, if it was changed in the last iteration */
306 pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
308 if ((res = ast_spawn_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num))) {
309 /* Something bad happened, or a hangup has been requested. */
310 if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F')) ||
311 (res == '*') || (res == '#')) {
312 /* Just return result as to the previous application as if it had been dialed */
313 ast_log(LOG_DEBUG, "Oooh, got something to jump out with ('%c')!\n", res);
314 break;
316 switch(res) {
317 case MACRO_EXIT_RESULT:
318 res = 0;
319 goto out;
320 case AST_PBX_KEEPALIVE:
321 if (option_debug)
322 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);
323 else if (option_verbose > 1)
324 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);
325 goto out;
326 break;
327 default:
328 if (option_debug)
329 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);
330 else if (option_verbose > 1)
331 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);
332 dead = 1;
333 goto out;
337 ast_log(LOG_DEBUG, "Executed application: %s\n", runningapp);
339 if (!strcasecmp(runningapp, "GOSUB")) {
340 gosub_level++;
341 ast_log(LOG_DEBUG, "Incrementing gosub_level\n");
342 } else if (!strcasecmp(runningapp, "GOSUBIF")) {
343 char tmp2[1024] = "", *cond, *app, *app2 = tmp2;
344 pbx_substitute_variables_helper(chan, runningdata, tmp2, sizeof(tmp2) - 1);
345 cond = strsep(&app2, "?");
346 app = strsep(&app2, ":");
347 if (pbx_checkcondition(cond)) {
348 if (!ast_strlen_zero(app)) {
349 gosub_level++;
350 ast_log(LOG_DEBUG, "Incrementing gosub_level\n");
352 } else {
353 if (!ast_strlen_zero(app2)) {
354 gosub_level++;
355 ast_log(LOG_DEBUG, "Incrementing gosub_level\n");
358 } else if (!strcasecmp(runningapp, "RETURN")) {
359 gosub_level--;
360 ast_log(LOG_DEBUG, "Decrementing gosub_level\n");
361 } else if (!strcasecmp(runningapp, "STACKPOP")) {
362 gosub_level--;
363 ast_log(LOG_DEBUG, "Decrementing gosub_level\n");
364 } else if (!strncasecmp(runningapp, "EXEC", 4)) {
365 /* Must evaluate args to find actual app */
366 char tmp2[1024] = "", *tmp3 = NULL;
367 pbx_substitute_variables_helper(chan, runningdata, tmp2, sizeof(tmp2) - 1);
368 if (!strcasecmp(runningapp, "EXECIF")) {
369 tmp3 = strchr(tmp2, '|');
370 if (tmp3)
371 *tmp3++ = '\0';
372 if (!pbx_checkcondition(tmp2))
373 tmp3 = NULL;
374 } else
375 tmp3 = tmp2;
377 if (tmp3)
378 ast_log(LOG_DEBUG, "Last app: %s\n", tmp3);
380 if (tmp3 && !strncasecmp(tmp3, "GOSUB", 5)) {
381 gosub_level++;
382 ast_log(LOG_DEBUG, "Incrementing gosub_level\n");
383 } else if (tmp3 && !strncasecmp(tmp3, "RETURN", 6)) {
384 gosub_level--;
385 ast_log(LOG_DEBUG, "Decrementing gosub_level\n");
386 } else if (tmp3 && !strncasecmp(tmp3, "STACKPOP", 8)) {
387 gosub_level--;
388 ast_log(LOG_DEBUG, "Decrementing gosub_level\n");
392 if (gosub_level == 0 && strcasecmp(chan->context, fullmacro)) {
393 if (option_verbose > 1)
394 ast_verbose(VERBOSE_PREFIX_2 "Channel '%s' jumping out of macro '%s'\n", chan->name, macro);
395 break;
398 /* don't stop executing extensions when we're in "h" */
399 if (chan->_softhangup && !inhangup) {
400 ast_log(LOG_DEBUG, "Extension %s, macroexten %s, priority %d returned normally even though call was hung up\n",
401 chan->exten, chan->macroexten, chan->priority);
402 goto out;
404 chan->priority++;
406 out:
407 /* Reset the depth back to what it was when the routine was entered (like if we called Macro recursively) */
408 snprintf(depthc, sizeof(depthc), "%d", depth);
409 if (!dead) {
410 pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
411 ast_set2_flag(chan, autoloopflag, AST_FLAG_IN_AUTOLOOP);
414 for (x = 1; x < argc; x++) {
415 /* Restore old arguments and delete ours */
416 snprintf(varname, sizeof(varname), "ARG%d", x);
417 if (oldargs[x]) {
418 if (!dead)
419 pbx_builtin_setvar_helper(chan, varname, oldargs[x]);
420 free(oldargs[x]);
421 } else if (!dead) {
422 pbx_builtin_setvar_helper(chan, varname, NULL);
426 /* Restore macro variables */
427 if (!dead) {
428 pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", save_macro_exten);
429 pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", save_macro_context);
430 pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", save_macro_priority);
432 if (save_macro_exten)
433 free(save_macro_exten);
434 if (save_macro_context)
435 free(save_macro_context);
436 if (save_macro_priority)
437 free(save_macro_priority);
439 if (!dead && setmacrocontext) {
440 chan->macrocontext[0] = '\0';
441 chan->macroexten[0] = '\0';
442 chan->macropriority = 0;
445 if (!dead && !strcasecmp(chan->context, fullmacro)) {
446 /* If we're leaving the macro normally, restore original information */
447 chan->priority = oldpriority;
448 ast_copy_string(chan->context, oldcontext, sizeof(chan->context));
449 if (!(chan->_softhangup & AST_SOFTHANGUP_ASYNCGOTO)) {
450 /* Copy the extension, so long as we're not in softhangup, where we could be given an asyncgoto */
451 const char *offsets;
452 ast_copy_string(chan->exten, oldexten, sizeof(chan->exten));
453 if ((offsets = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"))) {
454 /* Handle macro offset if it's set by checking the availability of step n + offset + 1, otherwise continue
455 normally if there is any problem */
456 if (sscanf(offsets, "%d", &offset) == 1) {
457 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + offset + 1, chan->cid.cid_num)) {
458 chan->priority += offset;
465 if (!dead)
466 pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", save_macro_offset);
467 if (save_macro_offset)
468 free(save_macro_offset);
470 /* Unlock the macro */
471 if (exclusive) {
472 ast_log(LOG_DEBUG, "Unlocking macrolock for '%s'\n", fullmacro);
473 if (ast_context_unlockmacro(fullmacro)) {
474 ast_log(LOG_ERROR, "Failed to unlock macro '%s' - that isn't good\n", fullmacro);
475 res = 0;
479 ast_module_user_remove(u);
481 return res;
484 static int macro_exec(struct ast_channel *chan, void *data)
486 return _macro_exec(chan, data, 0);
489 static int macroexclusive_exec(struct ast_channel *chan, void *data)
491 return _macro_exec(chan, data, 1);
494 static int macroif_exec(struct ast_channel *chan, void *data)
496 char *expr = NULL, *label_a = NULL, *label_b = NULL;
497 int res = 0;
498 struct ast_module_user *u;
500 u = ast_module_user_add(chan);
502 if (!(expr = ast_strdupa(data))) {
503 ast_module_user_remove(u);
504 return -1;
507 if ((label_a = strchr(expr, '?'))) {
508 *label_a = '\0';
509 label_a++;
510 if ((label_b = strchr(label_a, ':'))) {
511 *label_b = '\0';
512 label_b++;
514 if (pbx_checkcondition(expr))
515 res = macro_exec(chan, label_a);
516 else if (label_b)
517 res = macro_exec(chan, label_b);
518 } else
519 ast_log(LOG_WARNING, "Invalid Syntax.\n");
521 ast_module_user_remove(u);
523 return res;
526 static int macro_exit_exec(struct ast_channel *chan, void *data)
528 return MACRO_EXIT_RESULT;
531 static int unload_module(void)
533 int res;
535 res = ast_unregister_application(if_app);
536 res |= ast_unregister_application(exit_app);
537 res |= ast_unregister_application(app);
538 res |= ast_unregister_application(exclusive_app);
540 ast_module_user_hangup_all();
542 return res;
545 static int load_module(void)
547 int res;
549 res = ast_register_application(exit_app, macro_exit_exec, exit_synopsis, exit_descrip);
550 res |= ast_register_application(if_app, macroif_exec, if_synopsis, if_descrip);
551 res |= ast_register_application(exclusive_app, macroexclusive_exec, exclusive_synopsis, exclusive_descrip);
552 res |= ast_register_application(app, macro_exec, synopsis, descrip);
554 return res;
557 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Extension Macros");