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