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