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