Applications no longer need to call ast_module_user_add and ast_module_user_remove...
[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 \"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                                 if (option_verbose > 1)
317                                         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);
318                                 goto out;
319                                 break;
320                         default:
321                                 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);
322                                 if (option_verbose > 1)
323                                         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);
324                                 dead = 1;
325                                 goto out;
326                         }
327                 }
328
329                 ast_debug(1, "Executed application: %s\n", runningapp);
330
331                 if (!strcasecmp(runningapp, "GOSUB")) {
332                         gosub_level++;
333                         ast_debug(1, "Incrementing gosub_level\n");
334                 } else if (!strcasecmp(runningapp, "GOSUBIF")) {
335                         char tmp2[1024] = "", *cond, *app, *app2 = tmp2;
336                         pbx_substitute_variables_helper(chan, runningdata, tmp2, sizeof(tmp2) - 1);
337                         cond = strsep(&app2, "?");
338                         app = strsep(&app2, ":");
339                         if (pbx_checkcondition(cond)) {
340                                 if (!ast_strlen_zero(app)) {
341                                         gosub_level++;
342                                         ast_debug(1, "Incrementing gosub_level\n");
343                                 }
344                         } else {
345                                 if (!ast_strlen_zero(app2)) {
346                                         gosub_level++;
347                                         ast_debug(1, "Incrementing gosub_level\n");
348                                 }
349                         }
350                 } else if (!strcasecmp(runningapp, "RETURN")) {
351                         gosub_level--;
352                         ast_debug(1, "Decrementing gosub_level\n");
353                 } else if (!strcasecmp(runningapp, "STACKPOP")) {
354                         gosub_level--;
355                         ast_debug(1, "Decrementing gosub_level\n");
356                 } else if (!strncasecmp(runningapp, "EXEC", 4)) {
357                         /* Must evaluate args to find actual app */
358                         char tmp2[1024] = "", *tmp3 = NULL;
359                         pbx_substitute_variables_helper(chan, runningdata, tmp2, sizeof(tmp2) - 1);
360                         if (!strcasecmp(runningapp, "EXECIF")) {
361                                 tmp3 = strchr(tmp2, '|');
362                                 if (tmp3)
363                                         *tmp3++ = '\0';
364                                 if (!pbx_checkcondition(tmp2))
365                                         tmp3 = NULL;
366                         } else
367                                 tmp3 = tmp2;
368
369                         if (tmp3)
370                                 ast_debug(1, "Last app: %s\n", tmp3);
371
372                         if (tmp3 && !strncasecmp(tmp3, "GOSUB", 5)) {
373                                 gosub_level++;
374                                 ast_debug(1, "Incrementing gosub_level\n");
375                         } else if (tmp3 && !strncasecmp(tmp3, "RETURN", 6)) {
376                                 gosub_level--;
377                                 ast_debug(1, "Decrementing gosub_level\n");
378                         } else if (tmp3 && !strncasecmp(tmp3, "STACKPOP", 8)) {
379                                 gosub_level--;
380                                 ast_debug(1, "Decrementing gosub_level\n");
381                         }
382                 }
383
384                 if (gosub_level == 0 && strcasecmp(chan->context, fullmacro)) {
385                         if (option_verbose > 1)
386                                 ast_verbose(VERBOSE_PREFIX_2 "Channel '%s' jumping out of macro '%s'\n", chan->name, macro);
387                         break;
388                 }
389
390                 /* don't stop executing extensions when we're in "h" */
391                 if (chan->_softhangup && !inhangup) {
392                         ast_debug(1, "Extension %s, macroexten %s, priority %d returned normally even though call was hung up\n", chan->exten, chan->macroexten, chan->priority);
393                         goto out;
394                 }
395                 chan->priority++;
396         }
397         out:
398         /* Reset the depth back to what it was when the routine was entered (like if we called Macro recursively) */
399         snprintf(depthc, sizeof(depthc), "%d", depth);
400         if (!dead) {
401                 pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
402                 ast_set2_flag(chan, autoloopflag, AST_FLAG_IN_AUTOLOOP);
403         }
404
405         for (x = 1; x < argc; x++) {
406                 /* Restore old arguments and delete ours */
407                 snprintf(varname, sizeof(varname), "ARG%d", x);
408                 if (oldargs[x]) {
409                         if (!dead)
410                                 pbx_builtin_setvar_helper(chan, varname, oldargs[x]);
411                         ast_free(oldargs[x]);
412                 } else if (!dead) {
413                         pbx_builtin_setvar_helper(chan, varname, NULL);
414                 }
415         }
416
417         /* Restore macro variables */
418         if (!dead) {
419                 pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", save_macro_exten);
420                 pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", save_macro_context);
421                 pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", save_macro_priority);
422         }
423         if (save_macro_exten)
424                 ast_free(save_macro_exten);
425         if (save_macro_context)
426                 ast_free(save_macro_context);
427         if (save_macro_priority)
428                 ast_free(save_macro_priority);
429
430         if (!dead && setmacrocontext) {
431                 chan->macrocontext[0] = '\0';
432                 chan->macroexten[0] = '\0';
433                 chan->macropriority = 0;
434         }
435
436         if (!dead && !strcasecmp(chan->context, fullmacro)) {
437                 /* If we're leaving the macro normally, restore original information */
438                 chan->priority = oldpriority;
439                 ast_copy_string(chan->context, oldcontext, sizeof(chan->context));
440                 if (!(chan->_softhangup & AST_SOFTHANGUP_ASYNCGOTO)) {
441                         /* Copy the extension, so long as we're not in softhangup, where we could be given an asyncgoto */
442                         const char *offsets;
443                         ast_copy_string(chan->exten, oldexten, sizeof(chan->exten));
444                         if ((offsets = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"))) {
445                                 /* Handle macro offset if it's set by checking the availability of step n + offset + 1, otherwise continue
446                                 normally if there is any problem */
447                                 if (sscanf(offsets, "%d", &offset) == 1) {
448                                         if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + offset + 1, chan->cid.cid_num)) {
449                                                 chan->priority += offset;
450                                         }
451                                 }
452                         }
453                 }
454         }
455
456         if (!dead)
457                 pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", save_macro_offset);
458         if (save_macro_offset)
459                 ast_free(save_macro_offset);
460
461         /* Unlock the macro */
462         if (exclusive) {
463                 ast_debug(1, "Unlocking macrolock for '%s'\n", fullmacro);
464                 if (ast_context_unlockmacro(fullmacro)) {
465                         ast_log(LOG_ERROR, "Failed to unlock macro '%s' - that isn't good\n", fullmacro);
466                         res = 0;
467                 }
468         }
469
470         return res;
471 }
472
473 static int macro_exec(struct ast_channel *chan, void *data)
474 {
475         return _macro_exec(chan, data, 0);
476 }
477
478 static int macroexclusive_exec(struct ast_channel *chan, void *data)
479 {
480         return _macro_exec(chan, data, 1);
481 }
482
483 static int macroif_exec(struct ast_channel *chan, void *data) 
484 {
485         char *expr = NULL, *label_a = NULL, *label_b = NULL;
486         int res = 0;
487
488         if (!(expr = ast_strdupa(data)))
489                 return -1;
490
491         if ((label_a = strchr(expr, '?'))) {
492                 *label_a = '\0';
493                 label_a++;
494                 if ((label_b = strchr(label_a, ':'))) {
495                         *label_b = '\0';
496                         label_b++;
497                 }
498                 if (pbx_checkcondition(expr))
499                         macro_exec(chan, label_a);
500                 else if (label_b) 
501                         macro_exec(chan, label_b);
502         } else
503                 ast_log(LOG_WARNING, "Invalid Syntax.\n");
504
505         return res;
506 }
507                         
508 static int macro_exit_exec(struct ast_channel *chan, void *data)
509 {
510         return MACRO_EXIT_RESULT;
511 }
512
513 static int unload_module(void)
514 {
515         int res;
516
517         res = ast_unregister_application(if_app);
518         res |= ast_unregister_application(exit_app);
519         res |= ast_unregister_application(app);
520         res |= ast_unregister_application(exclusive_app);
521
522         return res;
523 }
524
525 static int load_module(void)
526 {
527         int res;
528
529         res = ast_register_application(exit_app, macro_exit_exec, exit_synopsis, exit_descrip);
530         res |= ast_register_application(if_app, macroif_exec, if_synopsis, if_descrip);
531         res |= ast_register_application(exclusive_app, macroexclusive_exec, exclusive_synopsis, exclusive_descrip);
532         res |= ast_register_application(app, macro_exec, synopsis, descrip);
533
534         return res;
535 }
536
537 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Extension Macros");