pbx: Create pbx_include.c for management of 'struct ast_include'.
[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_REGISTER_FILE()
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_context *c2;
194         int idx;
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 (idx = 0; idx < ast_context_includes_count(c); idx++) {
214                 const struct ast_include *i = ast_context_includes_get(c, idx);
215
216                 for (c2=ast_walk_contexts(NULL); c2; c2=ast_walk_contexts(c2)) {
217                         if (!strcmp(ast_get_context_name(c2), ast_get_include_name(i))) {
218                                 e = find_matching_priority(c2, exten, priority, callerid);
219                                 if (e)
220                                         return e;
221                         }
222                 }
223         }
224         return NULL;
225 }
226
227 static int _macro_exec(struct ast_channel *chan, const char *data, int exclusive)
228 {
229         const char *s;
230         char *tmp;
231         char *cur, *rest;
232         char *macro;
233         char fullmacro[80];
234         char varname[80];
235         char runningapp[80], runningdata[1024];
236         char *oldargs[MAX_ARGS + 1] = { NULL, };
237         int argc, x;
238         int res=0;
239         char oldexten[256]="";
240         int oldpriority, gosub_level = 0;
241         char pc[80], depthc[12];
242         char oldcontext[AST_MAX_CONTEXT] = "";
243         const char *inhangupc;
244         int offset, depth = 0, maxdepth = 7;
245         int setmacrocontext=0;
246         int autoloopflag, inhangup = 0;
247         struct ast_str *tmp_subst = NULL;
248   
249         char *save_macro_exten;
250         char *save_macro_context;
251         char *save_macro_priority;
252         char *save_macro_offset;
253         int save_in_subroutine;
254         struct ast_datastore *macro_store = ast_channel_datastore_find(chan, &macro_ds_info, NULL);
255
256         if (ast_strlen_zero(data)) {
257                 ast_log(LOG_WARNING, "Macro() requires arguments. See \"core show application macro\" for help.\n");
258                 return -1;
259         }
260
261         do {
262                 if (macro_store) {
263                         break;
264                 }
265                 if (!(macro_store = ast_datastore_alloc(&macro_ds_info, NULL))) {
266                         ast_log(LOG_WARNING, "Unable to allocate new datastore.\n");
267                         break;
268                 }
269                 /* Just the existence of this datastore is enough. */
270                 macro_store->inheritance = DATASTORE_INHERIT_FOREVER;
271                 ast_channel_datastore_add(chan, macro_store);
272         } while (0);
273
274         /* does the user want a deeper rabbit hole? */
275         ast_channel_lock(chan);
276         if ((s = pbx_builtin_getvar_helper(chan, "MACRO_RECURSION"))) {
277                 sscanf(s, "%30d", &maxdepth);
278         }
279         
280         /* Count how many levels deep the rabbit hole goes */
281         if ((s = pbx_builtin_getvar_helper(chan, "MACRO_DEPTH"))) {
282                 sscanf(s, "%30d", &depth);
283         }
284         
285         /* Used for detecting whether to return when a Macro is called from another Macro after hangup */
286         if (strcmp(ast_channel_exten(chan), "h") == 0)
287                 pbx_builtin_setvar_helper(chan, "MACRO_IN_HANGUP", "1");
288         
289         if ((inhangupc = pbx_builtin_getvar_helper(chan, "MACRO_IN_HANGUP"))) {
290                 sscanf(inhangupc, "%30d", &inhangup);
291         }
292         ast_channel_unlock(chan);
293
294         if (depth >= maxdepth) {
295                 ast_log(LOG_ERROR, "Macro():  possible infinite loop detected.  Returning early.\n");
296                 return 0;
297         }
298         snprintf(depthc, sizeof(depthc), "%d", depth + 1);
299
300         tmp = ast_strdupa(data);
301         rest = tmp;
302         macro = strsep(&rest, ",");
303         if (ast_strlen_zero(macro)) {
304                 ast_log(LOG_WARNING, "Invalid macro name specified\n");
305                 return 0;
306         }
307
308         snprintf(fullmacro, sizeof(fullmacro), "macro-%s", macro);
309         if (!ast_exists_extension(chan, fullmacro, "s", 1,
310                 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
311                 if (!ast_context_find(fullmacro)) 
312                         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));
313                 else
314                         ast_log(LOG_WARNING, "Context '%s' for macro '%s' lacks 's' extension, priority 1\n", fullmacro, macro);
315                 return 0;
316         }
317
318         /* If we are to run the macro exclusively, take the mutex */
319         if (exclusive) {
320                 ast_debug(1, "Locking macrolock for '%s'\n", fullmacro);
321                 ast_autoservice_start(chan);
322                 if (ast_context_lockmacro(fullmacro)) {
323                         ast_log(LOG_WARNING, "Failed to lock macro '%s' as in-use\n", fullmacro);
324                         ast_autoservice_stop(chan);
325                         return 0;
326                 }
327                 ast_autoservice_stop(chan);
328         }
329
330         if (!(tmp_subst = ast_str_create(16))) {
331                 return -1;
332         }
333
334         /* Save old info */
335         ast_channel_lock(chan);
336         oldpriority = ast_channel_priority(chan);
337         ast_copy_string(oldexten, ast_channel_exten(chan), sizeof(oldexten));
338         ast_copy_string(oldcontext, ast_channel_context(chan), sizeof(oldcontext));
339         if (ast_strlen_zero(ast_channel_macrocontext(chan))) {
340                 ast_channel_macrocontext_set(chan, ast_channel_context(chan));
341                 ast_channel_macroexten_set(chan, ast_channel_exten(chan));
342                 ast_channel_macropriority_set(chan, ast_channel_priority(chan));
343                 setmacrocontext=1;
344         }
345         argc = 1;
346         /* Save old macro variables */
347         save_macro_exten = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_EXTEN"));
348         pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", oldexten);
349
350         save_macro_context = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_CONTEXT"));
351         pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", oldcontext);
352
353         save_macro_priority = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_PRIORITY"));
354         snprintf(pc, sizeof(pc), "%d", oldpriority);
355         pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", pc);
356   
357         save_macro_offset = ast_strdup(pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"));
358         pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", NULL);
359
360         pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
361
362         save_in_subroutine = ast_test_flag(ast_channel_flags(chan), AST_FLAG_SUBROUTINE_EXEC);
363         ast_set_flag(ast_channel_flags(chan), AST_FLAG_SUBROUTINE_EXEC);
364
365         /* Setup environment for new run */
366         ast_channel_exten_set(chan, "s");
367         ast_channel_context_set(chan, fullmacro);
368         ast_channel_priority_set(chan, 1);
369
370         while((cur = strsep(&rest, ",")) && (argc < MAX_ARGS)) {
371                 const char *argp;
372                 /* Save copy of old arguments if we're overwriting some, otherwise
373                 let them pass through to the other macro */
374                 snprintf(varname, sizeof(varname), "ARG%d", argc);
375                 if ((argp = pbx_builtin_getvar_helper(chan, varname))) {
376                         oldargs[argc] = ast_strdup(argp);
377                 }
378                 pbx_builtin_setvar_helper(chan, varname, cur);
379                 argc++;
380         }
381         autoloopflag = ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
382         ast_set_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
383         ast_channel_unlock(chan);
384
385         while (ast_exists_extension(chan, ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan),
386                 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
387                 struct ast_context *c;
388                 struct ast_exten *e;
389                 int foundx;
390                 runningapp[0] = '\0';
391                 runningdata[0] = '\0';
392
393                 /* What application will execute? */
394                 if (ast_rdlock_contexts()) {
395                         ast_log(LOG_WARNING, "Failed to lock contexts list\n");
396                 } else {
397                         for (c = ast_walk_contexts(NULL), e = NULL; c; c = ast_walk_contexts(c)) {
398                                 if (!strcmp(ast_get_context_name(c), ast_channel_context(chan))) {
399                                         if (ast_rdlock_context(c)) {
400                                                 ast_log(LOG_WARNING, "Unable to lock context?\n");
401                                         } else {
402                                                 e = find_matching_priority(c, ast_channel_exten(chan), ast_channel_priority(chan),
403                                                         S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL));
404                                                 if (e) { /* This will only be undefined for pbx_realtime, which is majorly broken. */
405                                                         ast_copy_string(runningapp, ast_get_extension_app(e), sizeof(runningapp));
406                                                         ast_copy_string(runningdata, ast_get_extension_app_data(e), sizeof(runningdata));
407                                                 }
408                                                 ast_unlock_context(c);
409                                         }
410                                         break;
411                                 }
412                         }
413                 }
414                 ast_unlock_contexts();
415
416                 /* Reset the macro depth, if it was changed in the last iteration */
417                 pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
418
419                 res = ast_spawn_extension(chan, ast_channel_context(chan), ast_channel_exten(chan), ast_channel_priority(chan),
420                         S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
421                         &foundx, 1);
422                 if (res) {
423                         /* Something bad happened, or a hangup has been requested. */
424                         if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F')) ||
425                         (res == '*') || (res == '#')) {
426                                 /* Just return result as to the previous application as if it had been dialed */
427                                 ast_debug(1, "Oooh, got something to jump out with ('%c')!\n", res);
428                                 break;
429                         }
430                         switch(res) {
431                         case MACRO_EXIT_RESULT:
432                                 res = 0;
433                                 goto out;
434                         default:
435                                 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);
436                                 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);
437                                 goto out;
438                         }
439                 }
440
441                 ast_debug(1, "Executed application: %s\n", runningapp);
442
443                 if (!strcasecmp(runningapp, "GOSUB")) {
444                         gosub_level++;
445                         ast_debug(1, "Incrementing gosub_level\n");
446                 } else if (!strcasecmp(runningapp, "GOSUBIF")) {
447                         char *cond, *app_arg;
448                         char *app2;
449                         ast_str_substitute_variables(&tmp_subst, 0, chan, runningdata);
450                         app2 = ast_str_buffer(tmp_subst);
451                         cond = strsep(&app2, "?");
452                         app_arg = strsep(&app2, ":");
453                         if (pbx_checkcondition(cond)) {
454                                 if (!ast_strlen_zero(app_arg)) {
455                                         gosub_level++;
456                                         ast_debug(1, "Incrementing gosub_level\n");
457                                 }
458                         } else {
459                                 if (!ast_strlen_zero(app2)) {
460                                         gosub_level++;
461                                         ast_debug(1, "Incrementing gosub_level\n");
462                                 }
463                         }
464                 } else if (!strcasecmp(runningapp, "RETURN")) {
465                         gosub_level--;
466                         ast_debug(1, "Decrementing gosub_level\n");
467                 } else if (!strcasecmp(runningapp, "STACKPOP")) {
468                         gosub_level--;
469                         ast_debug(1, "Decrementing gosub_level\n");
470                 } else if (!strncasecmp(runningapp, "EXEC", 4)) {
471                         /* Must evaluate args to find actual app */
472                         char *tmp2, *tmp3 = NULL;
473                         ast_str_substitute_variables(&tmp_subst, 0, chan, runningdata);
474                         tmp2 = ast_str_buffer(tmp_subst);
475                         if (!strcasecmp(runningapp, "EXECIF")) {
476                                 if ((tmp3 = strchr(tmp2, '|'))) {
477                                         *tmp3++ = '\0';
478                                 }
479                                 if (!pbx_checkcondition(tmp2)) {
480                                         tmp3 = NULL;
481                                 }
482                         } else {
483                                 tmp3 = tmp2;
484                         }
485
486                         if (tmp3) {
487                                 ast_debug(1, "Last app: %s\n", tmp3);
488                         }
489
490                         if (tmp3 && !strncasecmp(tmp3, "GOSUB", 5)) {
491                                 gosub_level++;
492                                 ast_debug(1, "Incrementing gosub_level\n");
493                         } else if (tmp3 && !strncasecmp(tmp3, "RETURN", 6)) {
494                                 gosub_level--;
495                                 ast_debug(1, "Decrementing gosub_level\n");
496                         } else if (tmp3 && !strncasecmp(tmp3, "STACKPOP", 8)) {
497                                 gosub_level--;
498                                 ast_debug(1, "Decrementing gosub_level\n");
499                         }
500                 }
501
502                 if (gosub_level == 0 && strcasecmp(ast_channel_context(chan), fullmacro)) {
503                         ast_verb(2, "Channel '%s' jumping out of macro '%s'\n", ast_channel_name(chan), macro);
504                         break;
505                 }
506
507                 /* don't stop executing extensions when we're in "h" */
508                 if (ast_check_hangup(chan) && !inhangup) {
509                         ast_debug(1, "Extension %s, macroexten %s, priority %d returned normally even though call was hung up\n",
510                                 ast_channel_exten(chan),
511                                 ast_channel_macroexten(chan),
512                                 ast_channel_priority(chan));
513                         goto out;
514                 }
515                 ast_channel_priority_set(chan, ast_channel_priority(chan) + 1);
516         }
517         out:
518
519         /* Don't let the channel change now. */
520         ast_channel_lock(chan);
521
522         /* Reset the depth back to what it was when the routine was entered (like if we called Macro recursively) */
523         snprintf(depthc, sizeof(depthc), "%d", depth);
524         pbx_builtin_setvar_helper(chan, "MACRO_DEPTH", depthc);
525         ast_set2_flag(ast_channel_flags(chan), autoloopflag, AST_FLAG_IN_AUTOLOOP);
526         ast_set2_flag(ast_channel_flags(chan), save_in_subroutine, AST_FLAG_SUBROUTINE_EXEC);
527
528         for (x = 1; x < argc; x++) {
529                 /* Restore old arguments and delete ours */
530                 snprintf(varname, sizeof(varname), "ARG%d", x);
531                 pbx_builtin_setvar_helper(chan, varname, oldargs[x]);
532                 ast_free(oldargs[x]);
533         }
534
535         /* Restore macro variables */
536         pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", save_macro_exten);
537         pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", save_macro_context);
538         pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", save_macro_priority);
539         ast_free(save_macro_exten);
540         ast_free(save_macro_context);
541         ast_free(save_macro_priority);
542
543         if (setmacrocontext) {
544                 ast_channel_macrocontext_set(chan, "");
545                 ast_channel_macroexten_set(chan, "");
546                 ast_channel_macropriority_set(chan, 0);
547         }
548
549         if (!strcasecmp(ast_channel_context(chan), fullmacro)
550                 && !(ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO)) {
551                 const char *offsets;
552
553                 /* If we're leaving the macro normally, restore original information */
554                 ast_channel_priority_set(chan, oldpriority);
555                 ast_channel_context_set(chan, oldcontext);
556                 ast_channel_exten_set(chan, oldexten);
557                 if ((offsets = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"))) {
558                         /* Handle macro offset if it's set by checking the availability of step n + offset + 1, otherwise continue
559                         normally if there is any problem */
560                         if (sscanf(offsets, "%30d", &offset) == 1) {
561                                 if (ast_exists_extension(chan, ast_channel_context(chan), ast_channel_exten(chan),
562                                         ast_channel_priority(chan) + offset + 1,
563                                         S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
564                                         ast_channel_priority_set(chan, ast_channel_priority(chan) + offset);
565                                 }
566                         }
567                 }
568         }
569
570         pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", save_macro_offset);
571         ast_free(save_macro_offset);
572
573         /* Unlock the macro */
574         if (exclusive) {
575                 ast_debug(1, "Unlocking macrolock for '%s'\n", fullmacro);
576                 if (ast_context_unlockmacro(fullmacro)) {
577                         ast_log(LOG_ERROR, "Failed to unlock macro '%s' - that isn't good\n", fullmacro);
578                         res = 0;
579                 }
580         }
581         ast_channel_unlock(chan);
582         ast_free(tmp_subst);
583
584         return res;
585 }
586
587 static int macro_exec(struct ast_channel *chan, const char *data)
588 {
589         return _macro_exec(chan, data, 0);
590 }
591
592 static int macroexclusive_exec(struct ast_channel *chan, const char *data)
593 {
594         return _macro_exec(chan, data, 1);
595 }
596
597 static int macroif_exec(struct ast_channel *chan, const char *data) 
598 {
599         char *expr = NULL, *label_a = NULL, *label_b = NULL;
600         int res = 0;
601
602         expr = ast_strdupa(data);
603
604         if ((label_a = strchr(expr, '?'))) {
605                 *label_a = '\0';
606                 label_a++;
607                 if ((label_b = strchr(label_a, ':'))) {
608                         *label_b = '\0';
609                         label_b++;
610                 }
611                 if (pbx_checkcondition(expr))
612                         res = macro_exec(chan, label_a);
613                 else if (label_b) 
614                         res = macro_exec(chan, label_b);
615         } else
616                 ast_log(LOG_WARNING, "Invalid Syntax.\n");
617
618         return res;
619 }
620                         
621 static int macro_exit_exec(struct ast_channel *chan, const char *data)
622 {
623         return MACRO_EXIT_RESULT;
624 }
625
626 static int unload_module(void)
627 {
628         int res;
629
630         res = ast_unregister_application(if_app);
631         res |= ast_unregister_application(exit_app);
632         res |= ast_unregister_application(app);
633         res |= ast_unregister_application(exclusive_app);
634
635         return res;
636 }
637
638 static int load_module(void)
639 {
640         int res;
641
642         res = ast_register_application_xml(exit_app, macro_exit_exec);
643         res |= ast_register_application_xml(if_app, macroif_exec);
644         res |= ast_register_application_xml(exclusive_app, macroexclusive_exec);
645         res |= ast_register_application_xml(app, macro_exec);
646
647         return res;
648 }
649
650 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Extension Macros");