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