bug #8076 check option_debug before printing to debug channel.
[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
65 static char *if_descrip =
66 "  MacroIf(<expr>?macroname_a[|arg1][:macroname_b[|arg1]])\n"
67 "Executes macro defined in <macroname_a> if <expr> is true\n"
68 "(otherwise <macroname_b> if provided)\n"
69 "Arguments and return values as in application macro()\n";
70
71 static char *exclusive_descrip =
72 "  MacroExclusive(macroname|arg1|arg2...):\n"
73 "Executes macro defined in the context 'macro-macroname'\n"
74 "Only one call at a time may run the macro.\n"
75 "(we'll wait if another call is busy executing in the Macro)\n"
76 "Arguments and return values as in application Macro()\n";
77
78 static char *exit_descrip =
79 "  MacroExit():\n"
80 "Causes the currently running macro to exit as if it had\n"
81 "ended normally by running out of priorities to execute.\n"
82 "If used outside a macro, will likely cause unexpected\n"
83 "behavior.\n";
84
85 static char *app = "Macro";
86 static char *if_app = "MacroIf";
87 static char *exclusive_app = "MacroExclusive";
88 static char *exit_app = "MacroExit";
89
90 static char *synopsis = "Macro Implementation";
91 static char *if_synopsis = "Conditional Macro Implementation";
92 static char *exclusive_synopsis = "Exclusive Macro Implementation";
93 static char *exit_synopsis = "Exit From Macro";
94
95
96 static int _macro_exec(struct ast_channel *chan, void *data, int exclusive)
97 {
98         const char *s;
99
100         char *tmp;
101         char *cur, *rest;
102         char *macro;
103         char fullmacro[80];
104         char varname[80];
105         char *oldargs[MAX_ARGS + 1] = { NULL, };
106         int argc, x;
107         int res=0;
108         char oldexten[256]="";
109         int oldpriority;
110         char pc[80], depthc[12];
111         char oldcontext[AST_MAX_CONTEXT] = "";
112         int offset, depth = 0, maxdepth = 7;
113         int setmacrocontext=0;
114         int autoloopflag, dead = 0;
115   
116         char *save_macro_exten;
117         char *save_macro_context;
118         char *save_macro_priority;
119         char *save_macro_offset;
120         struct ast_module_user *u;
121  
122         if (ast_strlen_zero(data)) {
123                 ast_log(LOG_WARNING, "Macro() requires arguments. See \"show application macro\" for help.\n");
124                 return -1;
125         }
126
127         u = ast_module_user_add(chan);
128
129         /* does the user want a deeper rabbit hole? */
130         s = pbx_builtin_getvar_helper(chan, "MACRO_RECURSION");
131         if (s)
132                 sscanf(s, "%d", &maxdepth);
133
134         /* Count how many levels deep the rabbit hole goes */
135         s = pbx_builtin_getvar_helper(chan, "MACRO_DEPTH");
136         if (s)
137                 sscanf(s, "%d", &depth);
138         if (depth >= maxdepth) {
139                 ast_log(LOG_ERROR, "Macro():  possible infinite loop detected.  Returning early.\n");
140                 ast_module_user_remove(u);
141                 return 0;
142         }
143         snprintf(depthc, sizeof(depthc), "%d", depth + 1);
144         pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
145
146         tmp = ast_strdupa(data);
147         rest = tmp;
148         macro = strsep(&rest, "|");
149         if (ast_strlen_zero(macro)) {
150                 ast_log(LOG_WARNING, "Invalid macro name specified\n");
151                 ast_module_user_remove(u);
152                 return 0;
153         }
154
155         snprintf(fullmacro, sizeof(fullmacro), "macro-%s", macro);
156         if (!ast_exists_extension(chan, fullmacro, "s", 1, chan->cid.cid_num)) {
157                 if (!ast_context_find(fullmacro)) 
158                         ast_log(LOG_WARNING, "No such context '%s' for macro '%s'\n", fullmacro, macro);
159                 else
160                         ast_log(LOG_WARNING, "Context '%s' for macro '%s' lacks 's' extension, priority 1\n", fullmacro, macro);
161                 ast_module_user_remove(u);
162                 return 0;
163         }
164
165         /* If we are to run the macro exclusively, take the mutex */
166         if (exclusive) {
167                 if (option_debug)
168                         ast_log(LOG_DEBUG, "Locking macrolock for '%s'\n", fullmacro);
169                 ast_autoservice_start(chan);
170                 if (ast_context_lockmacro(fullmacro)) {
171                         ast_log(LOG_WARNING, "Failed to lock macro '%s' as in-use\n", fullmacro);
172                         ast_autoservice_stop(chan);
173                         ast_module_user_remove(u);
174
175                         return 0;
176                 }
177                 ast_autoservice_stop(chan);
178         }
179         
180         /* Save old info */
181         oldpriority = chan->priority;
182         ast_copy_string(oldexten, chan->exten, sizeof(oldexten));
183         ast_copy_string(oldcontext, chan->context, sizeof(oldcontext));
184         if (ast_strlen_zero(chan->macrocontext)) {
185                 ast_copy_string(chan->macrocontext, chan->context, sizeof(chan->macrocontext));
186                 ast_copy_string(chan->macroexten, chan->exten, sizeof(chan->macroexten));
187                 chan->macropriority = chan->priority;
188                 setmacrocontext=1;
189         }
190         argc = 1;
191         /* Save old macro variables */
192         save_macro_exten = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_EXTEN"));
193         pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", oldexten);
194
195         save_macro_context = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_CONTEXT"));
196         pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", oldcontext);
197
198         save_macro_priority = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_PRIORITY"));
199         snprintf(pc, sizeof(pc), "%d", oldpriority);
200         pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", pc);
201   
202         save_macro_offset = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"));
203         pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", NULL);
204
205         /* Setup environment for new run */
206         chan->exten[0] = 's';
207         chan->exten[1] = '\0';
208         ast_copy_string(chan->context, fullmacro, sizeof(chan->context));
209         chan->priority = 1;
210
211         while((cur = strsep(&rest, "|")) && (argc < MAX_ARGS)) {
212                 const char *s;
213                 /* Save copy of old arguments if we're overwriting some, otherwise
214                 let them pass through to the other macro */
215                 snprintf(varname, sizeof(varname), "ARG%d", argc);
216                 s = pbx_builtin_getvar_helper(chan, varname);
217                 if (s)
218                         oldargs[argc] = ast_strdup(s);
219                 pbx_builtin_setvar_helper(chan, varname, cur);
220                 argc++;
221         }
222         autoloopflag = ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP);
223         ast_set_flag(chan, AST_FLAG_IN_AUTOLOOP);
224         while(ast_exists_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num)) {
225                 /* Reset the macro depth, if it was changed in the last iteration */
226                 pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
227                 if ((res = ast_spawn_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num))) {
228                         /* Something bad happened, or a hangup has been requested. */
229                         if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F')) ||
230                         (res == '*') || (res == '#')) {
231                                 /* Just return result as to the previous application as if it had been dialed */
232                                 if (option_debug)
233                                         ast_log(LOG_DEBUG, "Oooh, got something to jump out with ('%c')!\n", res);
234                                 break;
235                         }
236                         switch(res) {
237                         case MACRO_EXIT_RESULT:
238                                 res = 0;
239                                 goto out;
240                         case AST_PBX_KEEPALIVE:
241                                 if (option_debug)
242                                         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);
243                                 else if (option_verbose > 1)
244                                         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);
245                                 goto out;
246                                 break;
247                         default:
248                                 if (option_debug)
249                                         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);
250                                 else if (option_verbose > 1)
251                                         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);
252                                 dead = 1;
253                                 goto out;
254                         }
255                 }
256                 if (strcasecmp(chan->context, fullmacro)) {
257                         if (option_verbose > 1)
258                                 ast_verbose(VERBOSE_PREFIX_2 "Channel '%s' jumping out of macro '%s'\n", chan->name, macro);
259                         break;
260                 }
261                 /* don't stop executing extensions when we're in "h" */
262                 if (chan->_softhangup && strcasecmp(oldexten,"h") && strcasecmp(chan->macroexten,"h")) {
263                         if (option_debug)
264                                 ast_log(LOG_DEBUG, "Extension %s, macroexten %s, priority %d returned normally even though call was hung up\n",
265                                         chan->exten, chan->macroexten, chan->priority);
266                         goto out;
267                 }
268                 chan->priority++;
269         }
270         out:
271         /* Reset the depth back to what it was when the routine was entered (like if we called Macro recursively) */
272         snprintf(depthc, sizeof(depthc), "%d", depth);
273         if (!dead) {
274                 pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
275                 ast_set2_flag(chan, autoloopflag, AST_FLAG_IN_AUTOLOOP);
276         }
277
278         for (x = 1; x < argc; x++) {
279                 /* Restore old arguments and delete ours */
280                 snprintf(varname, sizeof(varname), "ARG%d", x);
281                 if (oldargs[x]) {
282                         if (!dead)
283                                 pbx_builtin_setvar_helper(chan, varname, oldargs[x]);
284                         free(oldargs[x]);
285                 } else if (!dead) {
286                         pbx_builtin_setvar_helper(chan, varname, NULL);
287                 }
288         }
289
290         /* Restore macro variables */
291         if (!dead) {
292                 pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", save_macro_exten);
293                 pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", save_macro_context);
294                 pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", save_macro_priority);
295         }
296         if (save_macro_exten)
297                 free(save_macro_exten);
298         if (save_macro_context)
299                 free(save_macro_context);
300         if (save_macro_priority)
301                 free(save_macro_priority);
302
303         if (!dead && setmacrocontext) {
304                 chan->macrocontext[0] = '\0';
305                 chan->macroexten[0] = '\0';
306                 chan->macropriority = 0;
307         }
308
309         if (!dead && !strcasecmp(chan->context, fullmacro)) {
310                 /* If we're leaving the macro normally, restore original information */
311                 chan->priority = oldpriority;
312                 ast_copy_string(chan->context, oldcontext, sizeof(chan->context));
313                 if (!(chan->_softhangup & AST_SOFTHANGUP_ASYNCGOTO)) {
314                         /* Copy the extension, so long as we're not in softhangup, where we could be given an asyncgoto */
315                         const char *offsets;
316                         ast_copy_string(chan->exten, oldexten, sizeof(chan->exten));
317                         if ((offsets = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"))) {
318                                 /* Handle macro offset if it's set by checking the availability of step n + offset + 1, otherwise continue
319                                 normally if there is any problem */
320                                 if (sscanf(offsets, "%d", &offset) == 1) {
321                                         if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + offset + 1, chan->cid.cid_num)) {
322                                                 chan->priority += offset;
323                                         }
324                                 }
325                         }
326                 }
327         }
328
329         if (!dead)
330                 pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", save_macro_offset);
331         if (save_macro_offset)
332                 free(save_macro_offset);
333
334         /* Unlock the macro */
335         if (exclusive) {
336                 if (option_debug)
337                         ast_log(LOG_DEBUG, "Unlocking macrolock for '%s'\n", fullmacro);
338                 if (ast_context_unlockmacro(fullmacro)) {
339                         ast_log(LOG_ERROR, "Failed to unlock macro '%s' - that isn't good\n", fullmacro);
340                         res = 0;
341                 }
342         }
343         
344         ast_module_user_remove(u);
345
346         return res;
347 }
348
349 static int macro_exec(struct ast_channel *chan, void *data)
350 {
351         return _macro_exec(chan, data, 0);
352 }
353
354 static int macroexclusive_exec(struct ast_channel *chan, void *data)
355 {
356         return _macro_exec(chan, data, 1);
357 }
358
359 static int macroif_exec(struct ast_channel *chan, void *data) 
360 {
361         char *expr = NULL, *label_a = NULL, *label_b = NULL;
362         int res = 0;
363         struct ast_module_user *u;
364
365         u = ast_module_user_add(chan);
366
367         if (!(expr = ast_strdupa(data))) {
368                 ast_module_user_remove(u);
369                 return -1;
370         }
371
372         if ((label_a = strchr(expr, '?'))) {
373                 *label_a = '\0';
374                 label_a++;
375                 if ((label_b = strchr(label_a, ':'))) {
376                         *label_b = '\0';
377                         label_b++;
378                 }
379                 if (pbx_checkcondition(expr))
380                         macro_exec(chan, label_a);
381                 else if (label_b) 
382                         macro_exec(chan, label_b);
383         } else
384                 ast_log(LOG_WARNING, "Invalid Syntax.\n");
385
386         ast_module_user_remove(u);
387
388         return res;
389 }
390                         
391 static int macro_exit_exec(struct ast_channel *chan, void *data)
392 {
393         return MACRO_EXIT_RESULT;
394 }
395
396 static int unload_module(void)
397 {
398         int res;
399
400         res = ast_unregister_application(if_app);
401         res |= ast_unregister_application(exit_app);
402         res |= ast_unregister_application(app);
403         res |= ast_unregister_application(exclusive_app);
404
405         ast_module_user_hangup_all();
406
407         return res;
408 }
409
410 static int load_module(void)
411 {
412         int res;
413
414         res = ast_register_application(exit_app, macro_exit_exec, exit_synopsis, exit_descrip);
415         res |= ast_register_application(if_app, macroif_exec, if_synopsis, if_descrip);
416         res |= ast_register_application(exclusive_app, macroexclusive_exec, exclusive_synopsis, exclusive_descrip);
417         res |= ast_register_application(app, macro_exec, synopsis, descrip);
418
419         return res;
420 }
421
422 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Extension Macros");