Use the same delimited character as the FILTER function in FIELDQTY and CUT.
[asterisk/asterisk.git] / apps / app_macro.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
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.
13  *
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.
17  */
18
19 /*! \file
20  *
21  * \brief Dial plan macro Implementation
22  *
23  * \author Mark Spencer <markster@digium.com>
24  * 
25  * \ingroup applications
26  */
27
28 #include "asterisk.h"
29
30 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
31
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <sys/types.h>
37
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"
47
48 #define MAX_ARGS 80
49
50 /* special result value used to force macro exit */
51 #define MACRO_EXIT_RESULT 1024
52
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! (Note: AEL does not use macros)\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. It is advised that\n"
75 "         if you need to deeply nest macro calls, that you use the Gosub application\n"
76 "         (now allows arguments like a Macro) with explict Return() calls instead.\n";
77
78 static char *if_descrip =
79 "  MacroIf(<expr>?macroname_a[,arg1][:macroname_b[,arg1]])\n"
80 "Executes macro defined in <macroname_a> if <expr> is true\n"
81 "(otherwise <macroname_b> if provided)\n"
82 "Arguments and return values as in application macro()\n";
83
84 static char *exclusive_descrip =
85 "  MacroExclusive(macroname,arg1,arg2...):\n"
86 "Executes macro defined in the context 'macro-macroname'\n"
87 "Only one call at a time may run the macro.\n"
88 "(we'll wait if another call is busy executing in the Macro)\n"
89 "Arguments and return values as in application Macro()\n";
90
91 static char *exit_descrip =
92 "  MacroExit():\n"
93 "Causes the currently running macro to exit as if it had\n"
94 "ended normally by running out of priorities to execute.\n"
95 "If used outside a macro, will likely cause unexpected\n"
96 "behavior.\n";
97
98 static char *app = "Macro";
99 static char *if_app = "MacroIf";
100 static char *exclusive_app = "MacroExclusive";
101 static char *exit_app = "MacroExit";
102
103 static char *synopsis = "Macro Implementation";
104 static char *if_synopsis = "Conditional Macro Implementation";
105 static char *exclusive_synopsis = "Exclusive Macro Implementation";
106 static char *exit_synopsis = "Exit From Macro";
107
108
109 static struct ast_exten *find_matching_priority(struct ast_context *c, const char *exten, int priority, const char *callerid)
110 {
111         struct ast_exten *e;
112         struct ast_include *i;
113         struct ast_context *c2;
114
115         for (e=ast_walk_context_extensions(c, NULL); e; e=ast_walk_context_extensions(c, e)) {
116                 if (ast_extension_match(ast_get_extension_name(e), exten)) {
117                         int needmatch = ast_get_extension_matchcid(e);
118                         if ((needmatch && ast_extension_match(ast_get_extension_cidmatch(e), callerid)) ||
119                                 (!needmatch)) {
120                                 /* This is the matching extension we want */
121                                 struct ast_exten *p;
122                                 for (p=ast_walk_extension_priorities(e, NULL); p; p=ast_walk_extension_priorities(e, p)) {
123                                         if (priority != ast_get_extension_priority(p))
124                                                 continue;
125                                         return p;
126                                 }
127                         }
128                 }
129         }
130
131         /* No match; run through includes */
132         for (i=ast_walk_context_includes(c, NULL); i; i=ast_walk_context_includes(c, i)) {
133                 for (c2=ast_walk_contexts(NULL); c2; c2=ast_walk_contexts(c2)) {
134                         if (!strcmp(ast_get_context_name(c2), ast_get_include_name(i))) {
135                                 e = find_matching_priority(c2, exten, priority, callerid);
136                                 if (e)
137                                         return e;
138                         }
139                 }
140         }
141         return NULL;
142 }
143
144 static int _macro_exec(struct ast_channel *chan, void *data, int exclusive)
145 {
146         const char *s;
147         char *tmp;
148         char *cur, *rest;
149         char *macro;
150         char fullmacro[80];
151         char varname[80];
152         char runningapp[80], runningdata[1024];
153         char *oldargs[MAX_ARGS + 1] = { NULL, };
154         int argc, x;
155         int res=0;
156         char oldexten[256]="";
157         int oldpriority, gosub_level = 0;
158         char pc[80], depthc[12];
159         char oldcontext[AST_MAX_CONTEXT] = "";
160         const char *inhangupc;
161         int offset, depth = 0, maxdepth = 7;
162         int setmacrocontext=0;
163         int autoloopflag, dead = 0, inhangup = 0;
164   
165         char *save_macro_exten;
166         char *save_macro_context;
167         char *save_macro_priority;
168         char *save_macro_offset;
169  
170         if (ast_strlen_zero(data)) {
171                 ast_log(LOG_WARNING, "Macro() requires arguments. See \"core show application macro\" for help.\n");
172                 return -1;
173         }
174
175         /* does the user want a deeper rabbit hole? */
176         s = pbx_builtin_getvar_helper(chan, "MACRO_RECURSION");
177         if (s)
178                 sscanf(s, "%d", &maxdepth);
179
180         /* Count how many levels deep the rabbit hole goes */
181         s = pbx_builtin_getvar_helper(chan, "MACRO_DEPTH");
182         if (s)
183                 sscanf(s, "%d", &depth);
184         /* Used for detecting whether to return when a Macro is called from another Macro after hangup */
185         if (strcmp(chan->exten, "h") == 0)
186                 pbx_builtin_setvar_helper(chan, "MACRO_IN_HANGUP", "1");
187         inhangupc = pbx_builtin_getvar_helper(chan, "MACRO_IN_HANGUP");
188         if (!ast_strlen_zero(inhangupc))
189                 sscanf(inhangupc, "%d", &inhangup);
190
191         if (depth >= maxdepth) {
192                 ast_log(LOG_ERROR, "Macro():  possible infinite loop detected.  Returning early.\n");
193                 return 0;
194         }
195         snprintf(depthc, sizeof(depthc), "%d", depth + 1);
196         pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
197
198         tmp = ast_strdupa(data);
199         rest = tmp;
200         macro = strsep(&rest, ",");
201         if (ast_strlen_zero(macro)) {
202                 ast_log(LOG_WARNING, "Invalid macro name specified\n");
203                 return 0;
204         }
205
206         snprintf(fullmacro, sizeof(fullmacro), "macro-%s", macro);
207         if (!ast_exists_extension(chan, fullmacro, "s", 1, chan->cid.cid_num)) {
208                 if (!ast_context_find(fullmacro)) 
209                         ast_log(LOG_WARNING, "No such context '%s' for macro '%s'\n", fullmacro, macro);
210                 else
211                         ast_log(LOG_WARNING, "Context '%s' for macro '%s' lacks 's' extension, priority 1\n", fullmacro, macro);
212                 return 0;
213         }
214
215         /* If we are to run the macro exclusively, take the mutex */
216         if (exclusive) {
217                 ast_debug(1, "Locking macrolock for '%s'\n", fullmacro);
218                 ast_autoservice_start(chan);
219                 if (ast_context_lockmacro(fullmacro)) {
220                         ast_log(LOG_WARNING, "Failed to lock macro '%s' as in-use\n", fullmacro);
221                         ast_autoservice_stop(chan);
222                         return 0;
223                 }
224                 ast_autoservice_stop(chan);
225         }
226         
227         /* Save old info */
228         oldpriority = chan->priority;
229         ast_copy_string(oldexten, chan->exten, sizeof(oldexten));
230         ast_copy_string(oldcontext, chan->context, sizeof(oldcontext));
231         if (ast_strlen_zero(chan->macrocontext)) {
232                 ast_copy_string(chan->macrocontext, chan->context, sizeof(chan->macrocontext));
233                 ast_copy_string(chan->macroexten, chan->exten, sizeof(chan->macroexten));
234                 chan->macropriority = chan->priority;
235                 setmacrocontext=1;
236         }
237         argc = 1;
238         /* Save old macro variables */
239         save_macro_exten = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_EXTEN"));
240         pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", oldexten);
241
242         save_macro_context = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_CONTEXT"));
243         pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", oldcontext);
244
245         save_macro_priority = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_PRIORITY"));
246         snprintf(pc, sizeof(pc), "%d", oldpriority);
247         pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", pc);
248   
249         save_macro_offset = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"));
250         pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", NULL);
251
252         /* Setup environment for new run */
253         chan->exten[0] = 's';
254         chan->exten[1] = '\0';
255         ast_copy_string(chan->context, fullmacro, sizeof(chan->context));
256         chan->priority = 1;
257
258         while((cur = strsep(&rest, ",")) && (argc < MAX_ARGS)) {
259                 const char *s;
260                 /* Save copy of old arguments if we're overwriting some, otherwise
261                 let them pass through to the other macro */
262                 snprintf(varname, sizeof(varname), "ARG%d", argc);
263                 s = pbx_builtin_getvar_helper(chan, varname);
264                 if (s)
265                         oldargs[argc] = ast_strdup(s);
266                 pbx_builtin_setvar_helper(chan, varname, cur);
267                 argc++;
268         }
269         autoloopflag = ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP);
270         ast_set_flag(chan, AST_FLAG_IN_AUTOLOOP);
271         while(ast_exists_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num)) {
272                 struct ast_context *c;
273                 struct ast_exten *e;
274                 runningapp[0] = '\0';
275                 runningdata[0] = '\0';
276
277                 /* What application will execute? */
278                 if (ast_rdlock_contexts()) {
279                         ast_log(LOG_WARNING, "Failed to lock contexts list\n");
280                 } else {
281                         for (c = ast_walk_contexts(NULL), e = NULL; c; c = ast_walk_contexts(c)) {
282                                 if (!strcmp(ast_get_context_name(c), chan->context)) {
283                                         if (ast_rdlock_context(c)) {
284                                                 ast_log(LOG_WARNING, "Unable to lock context?\n");
285                                         } else {
286                                                 e = find_matching_priority(c, chan->exten, chan->priority, chan->cid.cid_num);
287                                                 if (e) { /* This will only be undefined for pbx_realtime, which is majorly broken. */
288                                                         ast_copy_string(runningapp, ast_get_extension_app(e), sizeof(runningapp));
289                                                         ast_copy_string(runningdata, ast_get_extension_app_data(e), sizeof(runningdata));
290                                                 }
291                                                 ast_unlock_context(c);
292                                         }
293                                         break;
294                                 }
295                         }
296                 }
297                 ast_unlock_contexts();
298
299                 /* Reset the macro depth, if it was changed in the last iteration */
300                 pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
301
302                 if ((res = ast_spawn_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num))) {
303                         /* Something bad happened, or a hangup has been requested. */
304                         if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F')) ||
305                         (res == '*') || (res == '#')) {
306                                 /* Just return result as to the previous application as if it had been dialed */
307                                 ast_debug(1, "Oooh, got something to jump out with ('%c')!\n", res);
308                                 break;
309                         }
310                         switch(res) {
311                         case MACRO_EXIT_RESULT:
312                                 res = 0;
313                                 goto out;
314                         case AST_PBX_KEEPALIVE:
315                                 ast_debug(2, "Spawn extension (%s,%s,%d) exited KEEPALIVE in macro %s on '%s'\n", chan->context, chan->exten, chan->priority, macro, chan->name);
316                                 ast_verb(2, "Spawn extension (%s, %s, %d) exited KEEPALIVE in macro '%s' on '%s'\n", chan->context, chan->exten, chan->priority, macro, chan->name);
317                                 goto out;
318                                 break;
319                         default:
320                                 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);
321                                 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);
322                                 dead = 1;
323                                 goto out;
324                         }
325                 }
326
327                 ast_debug(1, "Executed application: %s\n", runningapp);
328
329                 if (!strcasecmp(runningapp, "GOSUB")) {
330                         gosub_level++;
331                         ast_debug(1, "Incrementing gosub_level\n");
332                 } else if (!strcasecmp(runningapp, "GOSUBIF")) {
333                         char tmp2[1024] = "", *cond, *app, *app2 = tmp2;
334                         pbx_substitute_variables_helper(chan, runningdata, tmp2, sizeof(tmp2) - 1);
335                         cond = strsep(&app2, "?");
336                         app = strsep(&app2, ":");
337                         if (pbx_checkcondition(cond)) {
338                                 if (!ast_strlen_zero(app)) {
339                                         gosub_level++;
340                                         ast_debug(1, "Incrementing gosub_level\n");
341                                 }
342                         } else {
343                                 if (!ast_strlen_zero(app2)) {
344                                         gosub_level++;
345                                         ast_debug(1, "Incrementing gosub_level\n");
346                                 }
347                         }
348                 } else if (!strcasecmp(runningapp, "RETURN")) {
349                         gosub_level--;
350                         ast_debug(1, "Decrementing gosub_level\n");
351                 } else if (!strcasecmp(runningapp, "STACKPOP")) {
352                         gosub_level--;
353                         ast_debug(1, "Decrementing gosub_level\n");
354                 } else if (!strncasecmp(runningapp, "EXEC", 4)) {
355                         /* Must evaluate args to find actual app */
356                         char tmp2[1024] = "", *tmp3 = NULL;
357                         pbx_substitute_variables_helper(chan, runningdata, tmp2, sizeof(tmp2) - 1);
358                         if (!strcasecmp(runningapp, "EXECIF")) {
359                                 tmp3 = strchr(tmp2, '|');
360                                 if (tmp3)
361                                         *tmp3++ = '\0';
362                                 if (!pbx_checkcondition(tmp2))
363                                         tmp3 = NULL;
364                         } else
365                                 tmp3 = tmp2;
366
367                         if (tmp3)
368                                 ast_debug(1, "Last app: %s\n", tmp3);
369
370                         if (tmp3 && !strncasecmp(tmp3, "GOSUB", 5)) {
371                                 gosub_level++;
372                                 ast_debug(1, "Incrementing gosub_level\n");
373                         } else if (tmp3 && !strncasecmp(tmp3, "RETURN", 6)) {
374                                 gosub_level--;
375                                 ast_debug(1, "Decrementing gosub_level\n");
376                         } else if (tmp3 && !strncasecmp(tmp3, "STACKPOP", 8)) {
377                                 gosub_level--;
378                                 ast_debug(1, "Decrementing gosub_level\n");
379                         }
380                 }
381
382                 if (gosub_level == 0 && strcasecmp(chan->context, fullmacro)) {
383                         ast_verb(2, "Channel '%s' jumping out of macro '%s'\n", chan->name, macro);
384                         break;
385                 }
386
387                 /* don't stop executing extensions when we're in "h" */
388                 if (ast_check_hangup(chan) && !inhangup) {
389                         ast_debug(1, "Extension %s, macroexten %s, priority %d returned normally even though call was hung up\n", chan->exten, chan->macroexten, chan->priority);
390                         goto out;
391                 }
392                 chan->priority++;
393         }
394         out:
395         /* Reset the depth back to what it was when the routine was entered (like if we called Macro recursively) */
396         snprintf(depthc, sizeof(depthc), "%d", depth);
397         if (!dead) {
398                 pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
399                 ast_set2_flag(chan, autoloopflag, AST_FLAG_IN_AUTOLOOP);
400         }
401
402         for (x = 1; x < argc; x++) {
403                 /* Restore old arguments and delete ours */
404                 snprintf(varname, sizeof(varname), "ARG%d", x);
405                 if (oldargs[x]) {
406                         if (!dead)
407                                 pbx_builtin_setvar_helper(chan, varname, oldargs[x]);
408                         ast_free(oldargs[x]);
409                 } else if (!dead) {
410                         pbx_builtin_setvar_helper(chan, varname, NULL);
411                 }
412         }
413
414         /* Restore macro variables */
415         if (!dead) {
416                 pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", save_macro_exten);
417                 pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", save_macro_context);
418                 pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", save_macro_priority);
419         }
420         if (save_macro_exten)
421                 ast_free(save_macro_exten);
422         if (save_macro_context)
423                 ast_free(save_macro_context);
424         if (save_macro_priority)
425                 ast_free(save_macro_priority);
426
427         if (!dead && setmacrocontext) {
428                 chan->macrocontext[0] = '\0';
429                 chan->macroexten[0] = '\0';
430                 chan->macropriority = 0;
431         }
432
433         if (!dead && !strcasecmp(chan->context, fullmacro)) {
434                 /* If we're leaving the macro normally, restore original information */
435                 chan->priority = oldpriority;
436                 ast_copy_string(chan->context, oldcontext, sizeof(chan->context));
437                 if (!(ast_check_hangup(chan) & AST_SOFTHANGUP_ASYNCGOTO)) {
438                         /* Copy the extension, so long as we're not in softhangup, where we could be given an asyncgoto */
439                         const char *offsets;
440                         ast_copy_string(chan->exten, oldexten, sizeof(chan->exten));
441                         if ((offsets = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"))) {
442                                 /* Handle macro offset if it's set by checking the availability of step n + offset + 1, otherwise continue
443                                 normally if there is any problem */
444                                 if (sscanf(offsets, "%d", &offset) == 1) {
445                                         if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + offset + 1, chan->cid.cid_num)) {
446                                                 chan->priority += offset;
447                                         }
448                                 }
449                         }
450                 }
451         }
452
453         if (!dead)
454                 pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", save_macro_offset);
455         if (save_macro_offset)
456                 ast_free(save_macro_offset);
457
458         /* Unlock the macro */
459         if (exclusive) {
460                 ast_debug(1, "Unlocking macrolock for '%s'\n", fullmacro);
461                 if (ast_context_unlockmacro(fullmacro)) {
462                         ast_log(LOG_ERROR, "Failed to unlock macro '%s' - that isn't good\n", fullmacro);
463                         res = 0;
464                 }
465         }
466
467         return res;
468 }
469
470 static int macro_exec(struct ast_channel *chan, void *data)
471 {
472         return _macro_exec(chan, data, 0);
473 }
474
475 static int macroexclusive_exec(struct ast_channel *chan, void *data)
476 {
477         return _macro_exec(chan, data, 1);
478 }
479
480 static int macroif_exec(struct ast_channel *chan, void *data) 
481 {
482         char *expr = NULL, *label_a = NULL, *label_b = NULL;
483         int res = 0;
484
485         if (!(expr = ast_strdupa(data)))
486                 return -1;
487
488         if ((label_a = strchr(expr, '?'))) {
489                 *label_a = '\0';
490                 label_a++;
491                 if ((label_b = strchr(label_a, ':'))) {
492                         *label_b = '\0';
493                         label_b++;
494                 }
495                 if (pbx_checkcondition(expr))
496                         res = macro_exec(chan, label_a);
497                 else if (label_b) 
498                         res = macro_exec(chan, label_b);
499         } else
500                 ast_log(LOG_WARNING, "Invalid Syntax.\n");
501
502         return res;
503 }
504                         
505 static int macro_exit_exec(struct ast_channel *chan, void *data)
506 {
507         return MACRO_EXIT_RESULT;
508 }
509
510 static int unload_module(void)
511 {
512         int res;
513
514         res = ast_unregister_application(if_app);
515         res |= ast_unregister_application(exit_app);
516         res |= ast_unregister_application(app);
517         res |= ast_unregister_application(exclusive_app);
518
519         return res;
520 }
521
522 static int load_module(void)
523 {
524         int res;
525
526         res = ast_register_application(exit_app, macro_exit_exec, exit_synopsis, exit_descrip);
527         res |= ast_register_application(if_app, macroif_exec, if_synopsis, if_descrip);
528         res |= ast_register_application(exclusive_app, macroexclusive_exec, exclusive_synopsis, exclusive_descrip);
529         res |= ast_register_application(app, macro_exec, synopsis, descrip);
530
531         return res;
532 }
533
534 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Extension Macros");