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