Merged revisions 166093 via svnmerge from
[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 "asterisk/file.h"
33 #include "asterisk/channel.h"
34 #include "asterisk/pbx.h"
35 #include "asterisk/module.h"
36 #include "asterisk/config.h"
37 #include "asterisk/utils.h"
38 #include "asterisk/lock.h"
39
40 /*** DOCUMENTATION
41         <application name="Macro" language="en_US">
42                 <synopsis>
43                         Macro Implementation.
44                 </synopsis>
45                 <syntax>
46                         <parameter name="name" required="true">
47                                 <para>The name of the macro</para>
48                         </parameter>
49                         <parameter name="args">
50                                 <argument name="arg1" required="true" />
51                                 <argument name="arg2" multiple="true" />
52                         </parameter>
53                 </syntax>
54                 <description>
55                         <para>Executes a macro using the context macro-<replaceable>name</replaceable>,
56                         jumping to the <literal>s</literal> extension of that context and executing each step,
57                         then returning when the steps end.</para>
58                         <para>The calling extension, context, and priority are stored in <variable>MACRO_EXTEN</variable>,
59                         <variable>MACRO_CONTEXT</variable> and <variable>MACRO_PRIORITY</variable> respectively. Arguments
60                         become <variable>ARG1</variable>, <variable>ARG2</variable>, etc in the macro context.</para>
61                         <para>If you Goto out of the Macro context, the Macro will terminate and control will be returned
62                         at the location of the Goto.</para>
63                         <para>If <variable>MACRO_OFFSET</variable> is set at termination, Macro will attempt to continue
64                         at priority MACRO_OFFSET + N + 1 if such a step exists, and N + 1 otherwise.</para>
65                         <para>Extensions: While a macro is being executed, it becomes the current context. This means that if
66                         a hangup occurs, for instance, that the macro will be searched for an <literal>h</literal> extension,
67                         NOT the context from which the macro was called. So, make sure to define all appropriate extensions
68                         in your macro! (Note: AEL does not use macros)</para>
69                         <warning><para>Because of the way Macro is implemented (it executes the priorities contained within
70                         it via sub-engine), and a fixed per-thread memory stack allowance, macros are limited to 7 levels
71                         of nesting (macro calling macro calling macro, etc.); It may be possible that stack-intensive
72                         applications in deeply nested macros could cause asterisk to crash earlier than this limit.
73                         It is advised that if you need to deeply nest macro calls, that you use the Gosub application
74                         (now allows arguments like a Macro) with explict Return() calls instead.</para></warning>
75                 </description>
76                 <see-also>
77                         <ref type="application">MacroExit</ref>
78                         <ref type="application">Goto</ref>
79                         <ref type="application">Gosub</ref>
80                 </see-also>
81         </application>
82         <application name="MacroIf" language="en_US">
83                 <synopsis>
84                         Conditional Macro implementation.
85                 </synopsis>
86                 <syntax argsep="?">
87                         <parameter name="expr" required="true" />
88                         <parameter name="destination" required="true" argsep=":">
89                                 <argument name="macroiftrue" required="true">
90                                         <argument name="macroiftrue" required="true" />
91                                         <argument name="arg1" multiple="true" />
92                                 </argument>
93                                 <argument name="macroiffalse">
94                                         <argument name="macroiffalse" required="true" />
95                                         <argument name="arg1" multiple="true" />
96                                 </argument>
97                         </parameter>
98                 </syntax>
99                 <description>
100                         <para>Executes macro defined in <replaceable>macroiftrue</replaceable> if
101                         <replaceable>expr</replaceable> is true (otherwise <replaceable>macroiffalse</replaceable>
102                         if provided)</para>
103                         <para>Arguments and return values as in application Macro()</para>
104                 </description>
105                 <see-also>
106                         <ref type="application">GotoIf</ref>
107                         <ref type="application">GosubIf</ref>
108                         <ref type="function">IF</ref>
109                 </see-also>
110         </application>
111         <application name="MacroExclusive" language="en_US">
112                 <synopsis>
113                         Exclusive Macro Implementation.
114                 </synopsis>
115                 <syntax>
116                         <parameter name="name" required="true">
117                                 <para>The name of the macro</para>
118                         </parameter>
119                         <parameter name="arg1" />
120                         <parameter name="arg2" multiple="true" />
121                 </syntax>
122                 <description>
123                         <para>Executes macro defined in the context macro-<replaceable>name</replaceable>.
124                         Only one call at a time may run the macro. (we'll wait if another call is busy
125                         executing in the Macro)</para>
126                         <para>Arguments and return values as in application Macro()</para>
127                 </description>
128                 <see-also>
129                         <ref type="application">Macro</ref>
130                 </see-also>
131         </application>
132         <application name="MacroExit" language="en_US">
133                 <synopsis>
134                         Exit from Macro.
135                 </synopsis>
136                 <syntax />
137                 <description>
138                         <para>Causes the currently running macro to exit as if it had
139                         ended normally by running out of priorities to execute.
140                         If used outside a macro, will likely cause unexpected behavior.</para>
141                 </description>
142                 <see-also>
143                         <ref type="application">Macro</ref>
144                 </see-also>
145         </application>
146  ***/
147
148 #define MAX_ARGS 80
149
150 /* special result value used to force macro exit */
151 #define MACRO_EXIT_RESULT 1024
152
153 static char *app = "Macro";
154 static char *if_app = "MacroIf";
155 static char *exclusive_app = "MacroExclusive";
156 static char *exit_app = "MacroExit";
157
158 static void macro_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan);
159
160 struct ast_datastore_info macro_ds_info = {
161         .type = "MACRO",
162         .chan_fixup = macro_fixup,
163 };
164
165 static void macro_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
166 {
167         int i;
168         char varname[10];
169         pbx_builtin_setvar_helper(new_chan, "MACRO_DEPTH", "0");
170         pbx_builtin_setvar_helper(new_chan, "MACRO_CONTEXT", NULL);
171         pbx_builtin_setvar_helper(new_chan, "MACRO_EXTEN", NULL);
172         pbx_builtin_setvar_helper(new_chan, "MACRO_PRIORITY", NULL);
173         pbx_builtin_setvar_helper(new_chan, "MACRO_OFFSET", NULL);
174         for (i = 1; i < 100; i++) {
175                 snprintf(varname, sizeof(varname), "ARG%d", i);
176                 while (pbx_builtin_getvar_helper(new_chan, varname)) {
177                         /* Kill all levels of arguments */
178                         pbx_builtin_setvar_helper(new_chan, varname, NULL);
179                 }
180         }
181 }
182
183 static struct ast_exten *find_matching_priority(struct ast_context *c, const char *exten, int priority, const char *callerid)
184 {
185         struct ast_exten *e;
186         struct ast_include *i;
187         struct ast_context *c2;
188
189         for (e=ast_walk_context_extensions(c, NULL); e; e=ast_walk_context_extensions(c, e)) {
190                 if (ast_extension_match(ast_get_extension_name(e), exten)) {
191                         int needmatch = ast_get_extension_matchcid(e);
192                         if ((needmatch && ast_extension_match(ast_get_extension_cidmatch(e), callerid)) ||
193                                 (!needmatch)) {
194                                 /* This is the matching extension we want */
195                                 struct ast_exten *p;
196                                 for (p=ast_walk_extension_priorities(e, NULL); p; p=ast_walk_extension_priorities(e, p)) {
197                                         if (priority != ast_get_extension_priority(p))
198                                                 continue;
199                                         return p;
200                                 }
201                         }
202                 }
203         }
204
205         /* No match; run through includes */
206         for (i=ast_walk_context_includes(c, NULL); i; i=ast_walk_context_includes(c, i)) {
207                 for (c2=ast_walk_contexts(NULL); c2; c2=ast_walk_contexts(c2)) {
208                         if (!strcmp(ast_get_context_name(c2), ast_get_include_name(i))) {
209                                 e = find_matching_priority(c2, exten, priority, callerid);
210                                 if (e)
211                                         return e;
212                         }
213                 }
214         }
215         return NULL;
216 }
217
218 static int _macro_exec(struct ast_channel *chan, void *data, int exclusive)
219 {
220         const char *s;
221         char *tmp;
222         char *cur, *rest;
223         char *macro;
224         char fullmacro[80];
225         char varname[80];
226         char runningapp[80], runningdata[1024];
227         char *oldargs[MAX_ARGS + 1] = { NULL, };
228         int argc, x;
229         int res=0;
230         char oldexten[256]="";
231         int oldpriority, gosub_level = 0;
232         char pc[80], depthc[12];
233         char oldcontext[AST_MAX_CONTEXT] = "";
234         const char *inhangupc;
235         int offset, depth = 0, maxdepth = 7;
236         int setmacrocontext=0;
237         int autoloopflag, inhangup = 0;
238   
239         char *save_macro_exten;
240         char *save_macro_context;
241         char *save_macro_priority;
242         char *save_macro_offset;
243         struct ast_datastore *macro_store = ast_channel_datastore_find(chan, &macro_ds_info, NULL);
244  
245         if (ast_strlen_zero(data)) {
246                 ast_log(LOG_WARNING, "Macro() requires arguments. See \"core show application macro\" for help.\n");
247                 return -1;
248         }
249
250         do {
251                 if (macro_store) {
252                         break;
253                 }
254                 if (!(macro_store = ast_datastore_alloc(&macro_ds_info, NULL))) {
255                         ast_log(LOG_WARNING, "Unable to allocate new datastore.\n");
256                         break;
257                 }
258                 /* Just the existence of this datastore is enough. */
259                 macro_store->inheritance = DATASTORE_INHERIT_FOREVER;
260                 ast_channel_datastore_add(chan, macro_store);
261         } while (0);
262
263         /* does the user want a deeper rabbit hole? */
264         ast_channel_lock(chan);
265         if ((s = pbx_builtin_getvar_helper(chan, "MACRO_RECURSION"))) {
266                 sscanf(s, "%d", &maxdepth);
267         }
268         
269         /* Count how many levels deep the rabbit hole goes */
270         if ((s = pbx_builtin_getvar_helper(chan, "MACRO_DEPTH"))) {
271                 sscanf(s, "%d", &depth);
272         }
273         
274         /* Used for detecting whether to return when a Macro is called from another Macro after hangup */
275         if (strcmp(chan->exten, "h") == 0)
276                 pbx_builtin_setvar_helper(chan, "MACRO_IN_HANGUP", "1");
277         
278         if ((inhangupc = pbx_builtin_getvar_helper(chan, "MACRO_IN_HANGUP"))) {
279                 sscanf(inhangupc, "%d", &inhangup);
280         }
281         ast_channel_unlock(chan);
282
283         if (depth >= maxdepth) {
284                 ast_log(LOG_ERROR, "Macro():  possible infinite loop detected.  Returning early.\n");
285                 return 0;
286         }
287         snprintf(depthc, sizeof(depthc), "%d", depth + 1);
288         pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
289
290         tmp = ast_strdupa(data);
291         rest = tmp;
292         macro = strsep(&rest, ",");
293         if (ast_strlen_zero(macro)) {
294                 ast_log(LOG_WARNING, "Invalid macro name specified\n");
295                 return 0;
296         }
297
298         snprintf(fullmacro, sizeof(fullmacro), "macro-%s", macro);
299         if (!ast_exists_extension(chan, fullmacro, "s", 1, chan->cid.cid_num)) {
300                 if (!ast_context_find(fullmacro)) 
301                         ast_log(LOG_WARNING, "No such context '%s' for macro '%s'\n", fullmacro, macro);
302                 else
303                         ast_log(LOG_WARNING, "Context '%s' for macro '%s' lacks 's' extension, priority 1\n", fullmacro, macro);
304                 return 0;
305         }
306
307         /* If we are to run the macro exclusively, take the mutex */
308         if (exclusive) {
309                 ast_debug(1, "Locking macrolock for '%s'\n", fullmacro);
310                 ast_autoservice_start(chan);
311                 if (ast_context_lockmacro(fullmacro)) {
312                         ast_log(LOG_WARNING, "Failed to lock macro '%s' as in-use\n", fullmacro);
313                         ast_autoservice_stop(chan);
314                         return 0;
315                 }
316                 ast_autoservice_stop(chan);
317         }
318         
319         /* Save old info */
320         oldpriority = chan->priority;
321         ast_copy_string(oldexten, chan->exten, sizeof(oldexten));
322         ast_copy_string(oldcontext, chan->context, sizeof(oldcontext));
323         if (ast_strlen_zero(chan->macrocontext)) {
324                 ast_copy_string(chan->macrocontext, chan->context, sizeof(chan->macrocontext));
325                 ast_copy_string(chan->macroexten, chan->exten, sizeof(chan->macroexten));
326                 chan->macropriority = chan->priority;
327                 setmacrocontext=1;
328         }
329         argc = 1;
330         /* Save old macro variables */
331         save_macro_exten = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_EXTEN"));
332         pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", oldexten);
333
334         save_macro_context = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_CONTEXT"));
335         pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", oldcontext);
336
337         save_macro_priority = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_PRIORITY"));
338         snprintf(pc, sizeof(pc), "%d", oldpriority);
339         pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", pc);
340   
341         save_macro_offset = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"));
342         pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", NULL);
343
344         /* Setup environment for new run */
345         chan->exten[0] = 's';
346         chan->exten[1] = '\0';
347         ast_copy_string(chan->context, fullmacro, sizeof(chan->context));
348         chan->priority = 1;
349
350         ast_channel_lock(chan);
351         while((cur = strsep(&rest, ",")) && (argc < MAX_ARGS)) {
352                 const char *argp;
353                 /* Save copy of old arguments if we're overwriting some, otherwise
354                 let them pass through to the other macro */
355                 snprintf(varname, sizeof(varname), "ARG%d", argc);
356                 if ((argp = pbx_builtin_getvar_helper(chan, varname))) {
357                         oldargs[argc] = ast_strdup(argp);
358                 }
359                 pbx_builtin_setvar_helper(chan, varname, cur);
360                 argc++;
361         }
362         ast_channel_unlock(chan);
363         autoloopflag = ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP);
364         ast_set_flag(chan, AST_FLAG_IN_AUTOLOOP);
365         while(ast_exists_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num)) {
366                 struct ast_context *c;
367                 struct ast_exten *e;
368                 int foundx;
369                 runningapp[0] = '\0';
370                 runningdata[0] = '\0';
371
372                 /* What application will execute? */
373                 if (ast_rdlock_contexts()) {
374                         ast_log(LOG_WARNING, "Failed to lock contexts list\n");
375                 } else {
376                         for (c = ast_walk_contexts(NULL), e = NULL; c; c = ast_walk_contexts(c)) {
377                                 if (!strcmp(ast_get_context_name(c), chan->context)) {
378                                         if (ast_rdlock_context(c)) {
379                                                 ast_log(LOG_WARNING, "Unable to lock context?\n");
380                                         } else {
381                                                 e = find_matching_priority(c, chan->exten, chan->priority, chan->cid.cid_num);
382                                                 if (e) { /* This will only be undefined for pbx_realtime, which is majorly broken. */
383                                                         ast_copy_string(runningapp, ast_get_extension_app(e), sizeof(runningapp));
384                                                         ast_copy_string(runningdata, ast_get_extension_app_data(e), sizeof(runningdata));
385                                                 }
386                                                 ast_unlock_context(c);
387                                         }
388                                         break;
389                                 }
390                         }
391                 }
392                 ast_unlock_contexts();
393
394                 /* Reset the macro depth, if it was changed in the last iteration */
395                 pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
396
397                 if ((res = ast_spawn_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num, &foundx,1))) {
398                         /* Something bad happened, or a hangup has been requested. */
399                         if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F')) ||
400                         (res == '*') || (res == '#')) {
401                                 /* Just return result as to the previous application as if it had been dialed */
402                                 ast_debug(1, "Oooh, got something to jump out with ('%c')!\n", res);
403                                 break;
404                         }
405                         switch(res) {
406                         case MACRO_EXIT_RESULT:
407                                 res = 0;
408                                 goto out;
409                         default:
410                                 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);
411                                 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);
412                                 goto out;
413                         }
414                 }
415
416                 ast_debug(1, "Executed application: %s\n", runningapp);
417
418                 if (!strcasecmp(runningapp, "GOSUB")) {
419                         gosub_level++;
420                         ast_debug(1, "Incrementing gosub_level\n");
421                 } else if (!strcasecmp(runningapp, "GOSUBIF")) {
422                         char tmp2[1024], *cond, *app_arg, *app2 = tmp2;
423                         pbx_substitute_variables_helper(chan, runningdata, tmp2, sizeof(tmp2) - 1);
424                         cond = strsep(&app2, "?");
425                         app_arg = strsep(&app2, ":");
426                         if (pbx_checkcondition(cond)) {
427                                 if (!ast_strlen_zero(app_arg)) {
428                                         gosub_level++;
429                                         ast_debug(1, "Incrementing gosub_level\n");
430                                 }
431                         } else {
432                                 if (!ast_strlen_zero(app2)) {
433                                         gosub_level++;
434                                         ast_debug(1, "Incrementing gosub_level\n");
435                                 }
436                         }
437                 } else if (!strcasecmp(runningapp, "RETURN")) {
438                         gosub_level--;
439                         ast_debug(1, "Decrementing gosub_level\n");
440                 } else if (!strcasecmp(runningapp, "STACKPOP")) {
441                         gosub_level--;
442                         ast_debug(1, "Decrementing gosub_level\n");
443                 } else if (!strncasecmp(runningapp, "EXEC", 4)) {
444                         /* Must evaluate args to find actual app */
445                         char tmp2[1024], *tmp3 = NULL;
446                         pbx_substitute_variables_helper(chan, runningdata, tmp2, sizeof(tmp2) - 1);
447                         if (!strcasecmp(runningapp, "EXECIF")) {
448                                 tmp3 = strchr(tmp2, '|');
449                                 if (tmp3)
450                                         *tmp3++ = '\0';
451                                 if (!pbx_checkcondition(tmp2))
452                                         tmp3 = NULL;
453                         } else
454                                 tmp3 = tmp2;
455
456                         if (tmp3)
457                                 ast_debug(1, "Last app: %s\n", tmp3);
458
459                         if (tmp3 && !strncasecmp(tmp3, "GOSUB", 5)) {
460                                 gosub_level++;
461                                 ast_debug(1, "Incrementing gosub_level\n");
462                         } else if (tmp3 && !strncasecmp(tmp3, "RETURN", 6)) {
463                                 gosub_level--;
464                                 ast_debug(1, "Decrementing gosub_level\n");
465                         } else if (tmp3 && !strncasecmp(tmp3, "STACKPOP", 8)) {
466                                 gosub_level--;
467                                 ast_debug(1, "Decrementing gosub_level\n");
468                         }
469                 }
470
471                 if (gosub_level == 0 && strcasecmp(chan->context, fullmacro)) {
472                         ast_verb(2, "Channel '%s' jumping out of macro '%s'\n", chan->name, macro);
473                         break;
474                 }
475
476                 /* don't stop executing extensions when we're in "h" */
477                 if (ast_check_hangup(chan) && !inhangup) {
478                         ast_debug(1, "Extension %s, macroexten %s, priority %d returned normally even though call was hung up\n", chan->exten, chan->macroexten, chan->priority);
479                         goto out;
480                 }
481                 chan->priority++;
482         }
483         out:
484
485         /* Don't let the channel change now. */
486         ast_channel_lock(chan);
487
488         /* Reset the depth back to what it was when the routine was entered (like if we called Macro recursively) */
489         snprintf(depthc, sizeof(depthc), "%d", depth);
490         pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
491         ast_set2_flag(chan, autoloopflag, AST_FLAG_IN_AUTOLOOP);
492
493         for (x = 1; x < argc; x++) {
494                 /* Restore old arguments and delete ours */
495                 snprintf(varname, sizeof(varname), "ARG%d", x);
496                 if (oldargs[x]) {
497                         pbx_builtin_setvar_helper(chan, varname, oldargs[x]);
498                         ast_free(oldargs[x]);
499                 } else {
500                         pbx_builtin_setvar_helper(chan, varname, NULL);
501                 }
502         }
503
504         /* Restore macro variables */
505         pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", save_macro_exten);
506         pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", save_macro_context);
507         pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", save_macro_priority);
508         if (save_macro_exten)
509                 ast_free(save_macro_exten);
510         if (save_macro_context)
511                 ast_free(save_macro_context);
512         if (save_macro_priority)
513                 ast_free(save_macro_priority);
514
515         if (setmacrocontext) {
516                 chan->macrocontext[0] = '\0';
517                 chan->macroexten[0] = '\0';
518                 chan->macropriority = 0;
519         }
520
521         if (!strcasecmp(chan->context, fullmacro)) {
522                 /* If we're leaving the macro normally, restore original information */
523                 chan->priority = oldpriority;
524                 ast_copy_string(chan->context, oldcontext, sizeof(chan->context));
525                 if (!(ast_check_hangup(chan) & AST_SOFTHANGUP_ASYNCGOTO)) {
526                         /* Copy the extension, so long as we're not in softhangup, where we could be given an asyncgoto */
527                         const char *offsets;
528                         ast_copy_string(chan->exten, oldexten, sizeof(chan->exten));
529                         if ((offsets = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"))) {
530                                 /* Handle macro offset if it's set by checking the availability of step n + offset + 1, otherwise continue
531                                 normally if there is any problem */
532                                 if (sscanf(offsets, "%d", &offset) == 1) {
533                                         if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + offset + 1, chan->cid.cid_num)) {
534                                                 chan->priority += offset;
535                                         }
536                                 }
537                         }
538                 }
539         }
540
541         pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", save_macro_offset);
542         if (save_macro_offset)
543                 ast_free(save_macro_offset);
544
545         /* Unlock the macro */
546         if (exclusive) {
547                 ast_debug(1, "Unlocking macrolock for '%s'\n", fullmacro);
548                 if (ast_context_unlockmacro(fullmacro)) {
549                         ast_log(LOG_ERROR, "Failed to unlock macro '%s' - that isn't good\n", fullmacro);
550                         res = 0;
551                 }
552         }
553         ast_channel_unlock(chan);
554
555         return res;
556 }
557
558 static int macro_exec(struct ast_channel *chan, void *data)
559 {
560         return _macro_exec(chan, data, 0);
561 }
562
563 static int macroexclusive_exec(struct ast_channel *chan, void *data)
564 {
565         return _macro_exec(chan, data, 1);
566 }
567
568 static int macroif_exec(struct ast_channel *chan, void *data) 
569 {
570         char *expr = NULL, *label_a = NULL, *label_b = NULL;
571         int res = 0;
572
573         if (!(expr = ast_strdupa(data)))
574                 return -1;
575
576         if ((label_a = strchr(expr, '?'))) {
577                 *label_a = '\0';
578                 label_a++;
579                 if ((label_b = strchr(label_a, ':'))) {
580                         *label_b = '\0';
581                         label_b++;
582                 }
583                 if (pbx_checkcondition(expr))
584                         res = macro_exec(chan, label_a);
585                 else if (label_b) 
586                         res = macro_exec(chan, label_b);
587         } else
588                 ast_log(LOG_WARNING, "Invalid Syntax.\n");
589
590         return res;
591 }
592                         
593 static int macro_exit_exec(struct ast_channel *chan, void *data)
594 {
595         return MACRO_EXIT_RESULT;
596 }
597
598 static int unload_module(void)
599 {
600         int res;
601
602         res = ast_unregister_application(if_app);
603         res |= ast_unregister_application(exit_app);
604         res |= ast_unregister_application(app);
605         res |= ast_unregister_application(exclusive_app);
606
607         return res;
608 }
609
610 static int load_module(void)
611 {
612         int res;
613
614         res = ast_register_application_xml(exit_app, macro_exit_exec);
615         res |= ast_register_application_xml(if_app, macroif_exec);
616         res |= ast_register_application_xml(exclusive_app, macroexclusive_exec);
617         res |= ast_register_application_xml(app, macro_exec);
618
619         return res;
620 }
621
622 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Extension Macros");