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