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