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