2 * Asterisk -- A telephony toolkit for Linux.
6 * Copyright (C) 2003 - 2005, Digium, Inc.
8 * Mark Spencer <markster@digium.com>
10 * This program is free software, distributed under the terms of
11 * the GNU General Public License
14 #include <sys/types.h>
21 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
23 #include "asterisk/file.h"
24 #include "asterisk/logger.h"
25 #include "asterisk/channel.h"
26 #include "asterisk/pbx.h"
27 #include "asterisk/module.h"
28 #include "asterisk/options.h"
29 #include "asterisk/config.h"
30 #include "asterisk/utils.h"
31 #include "asterisk/lock.h"
35 /* special result value used to force macro exit */
36 #define MACRO_EXIT_RESULT 1024
38 static char *tdesc = "Extension Macros";
40 static char *descrip =
41 " Macro(macroname|arg1|arg2...): Executes a macro using the context\n"
42 "'macro-<macroname>', jumping to the 's' extension of that context and\n"
43 "executing each step, then returning when the steps end. \n"
44 "The calling extension, context, and priority are stored in ${MACRO_EXTEN}, \n"
45 "${MACRO_CONTEXT} and ${MACRO_PRIORITY} respectively. Arguments become\n"
46 "${ARG1}, ${ARG2}, etc in the macro context.\n"
47 "If you Goto out of the Macro context, the Macro will terminate and control\n"
48 "will be returned at the location of the Goto.\n"
49 "Macro returns -1 if any step in the macro returns -1, and 0 otherwise.\n"
50 "If ${MACRO_OFFSET} is set at termination, Macro will attempt to continue\n"
51 "at priority MACRO_OFFSET + N + 1 if such a step exists, and N + 1 otherwise.\n";
53 static char *if_descrip =
54 " MacroIf(<expr>?macroname_a[|arg1][:macroname_b[|arg1]])\n"
55 "Executes macro defined in <macroname_a> if <expr> is true\n"
56 "(otherwise <macroname_b> if provided)\n"
57 "Arguments and return values as in application macro()\n";
59 static char *exit_descrip =
61 "Causes the currently running macro to exit as if it had\n"
62 "ended normally by running out of priorities to execute.\n"
63 "If used outside a macro, will likely cause unexpected\n"
66 static char *app = "Macro";
67 static char *if_app = "MacroIf";
68 static char *exit_app = "MacroExit";
70 static char *synopsis = "Macro Implementation";
71 static char *if_synopsis = "Conditional Macro Implementation";
72 static char *exit_synopsis = "Exit From Macro";
78 static int macro_exec(struct ast_channel *chan, void *data)
85 char *oldargs[MAX_ARGS + 1] = { NULL, };
88 char oldexten[256]="";
91 char oldcontext[AST_MAX_CONTEXT] = "";
94 int setmacrocontext=0;
97 char *save_macro_exten;
98 char *save_macro_context;
99 char *save_macro_priority;
100 char *save_macro_offset;
103 if (!data || ast_strlen_zero(data)) {
104 ast_log(LOG_WARNING, "Macro() requires arguments. See \"show application macro\" for help.\n");
108 tmp = ast_strdupa((char *) data);
110 macro = strsep(&rest, "|");
111 if (!macro || ast_strlen_zero(macro)) {
112 ast_log(LOG_WARNING, "Invalid macro name specified\n");
115 snprintf(fullmacro, sizeof(fullmacro), "macro-%s", macro);
116 if (!ast_exists_extension(chan, fullmacro, "s", 1, chan->cid.cid_num)) {
117 if (!ast_context_find(fullmacro))
118 ast_log(LOG_WARNING, "No such context '%s' for macro '%s'\n", fullmacro, macro);
120 ast_log(LOG_WARNING, "Context '%s' for macro '%s' lacks 's' extension, priority 1\n", fullmacro, macro);
126 oldpriority = chan->priority;
127 ast_copy_string(oldexten, chan->exten, sizeof(oldexten));
128 ast_copy_string(oldcontext, chan->context, sizeof(oldcontext));
129 if (ast_strlen_zero(chan->macrocontext)) {
130 ast_copy_string(chan->macrocontext, chan->context, sizeof(chan->macrocontext));
131 ast_copy_string(chan->macroexten, chan->exten, sizeof(chan->macroexten));
132 chan->macropriority = chan->priority;
136 /* Save old macro variables */
137 save_macro_exten = pbx_builtin_getvar_helper(chan, "MACRO_EXTEN");
138 if (save_macro_exten)
139 save_macro_exten = strdup(save_macro_exten);
140 pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", oldexten);
142 save_macro_context = pbx_builtin_getvar_helper(chan, "MACRO_CONTEXT");
143 if (save_macro_context)
144 save_macro_context = strdup(save_macro_context);
145 pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", oldcontext);
147 save_macro_priority = pbx_builtin_getvar_helper(chan, "MACRO_PRIORITY");
148 if (save_macro_priority)
149 save_macro_priority = strdup(save_macro_priority);
150 snprintf(pc, sizeof(pc), "%d", oldpriority);
151 pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", pc);
153 save_macro_offset = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET");
154 if (save_macro_offset)
155 save_macro_offset = strdup(save_macro_offset);
156 pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", NULL);
158 /* Setup environment for new run */
159 chan->exten[0] = 's';
160 chan->exten[1] = '\0';
161 ast_copy_string(chan->context, fullmacro, sizeof(chan->context));
164 while((cur = strsep(&rest, "|")) && (argc < MAX_ARGS)) {
165 /* Save copy of old arguments if we're overwriting some, otherwise
166 let them pass through to the other macro */
167 snprintf(varname, sizeof(varname), "ARG%d", argc);
168 oldargs[argc] = pbx_builtin_getvar_helper(chan, varname);
170 oldargs[argc] = strdup(oldargs[argc]);
171 pbx_builtin_setvar_helper(chan, varname, cur);
174 autoloopflag = ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP);
175 ast_set_flag(chan, AST_FLAG_IN_AUTOLOOP);
176 while(ast_exists_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num)) {
177 if ((res = ast_spawn_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num))) {
178 /* Something bad happened, or a hangup has been requested. */
179 if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F')) ||
180 (res == '*') || (res == '#')) {
181 /* Just return result as to the previous application as if it had been dialed */
182 ast_log(LOG_DEBUG, "Oooh, got something to jump out with ('%c')!\n", res);
186 case MACRO_EXIT_RESULT:
189 case AST_PBX_KEEPALIVE:
191 ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited KEEPALIVE in macro %s on '%s'\n", chan->context, chan->exten, chan->priority, macro, chan->name);
192 else if (option_verbose > 1)
193 ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited KEEPALIVE in macro '%s' on '%s'\n", chan->context, chan->exten, chan->priority, macro, chan->name);
198 ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited non-zero on '%s' in macro '%s'\n", chan->context, chan->exten, chan->priority, chan->name, macro);
199 else if (option_verbose > 1)
200 ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited non-zero on '%s' in macro '%s'\n", chan->context, chan->exten, chan->priority, chan->name, macro);
204 if (strcasecmp(chan->context, fullmacro)) {
205 if (option_verbose > 1)
206 ast_verbose(VERBOSE_PREFIX_2 "Channel '%s' jumping out of macro '%s'\n", chan->name, macro);
209 /* don't stop executing extensions when we're in "h" */
210 if (chan->_softhangup && strcasecmp(oldexten,"h")) {
211 ast_log(LOG_DEBUG, "Extension %s, priority %d returned normally even though call was hung up\n",
212 chan->exten, chan->priority);
218 ast_set2_flag(chan, autoloopflag, AST_FLAG_IN_AUTOLOOP);
219 for (x=1; x<argc; x++) {
220 /* Restore old arguments and delete ours */
221 snprintf(varname, sizeof(varname), "ARG%d", x);
223 pbx_builtin_setvar_helper(chan, varname, oldargs[x]);
226 pbx_builtin_setvar_helper(chan, varname, NULL);
230 /* Restore macro variables */
231 pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", save_macro_exten);
232 if (save_macro_exten)
233 free(save_macro_exten);
234 pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", save_macro_context);
235 if (save_macro_context)
236 free(save_macro_context);
237 pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", save_macro_priority);
238 if (save_macro_priority)
239 free(save_macro_priority);
240 if (setmacrocontext) {
241 chan->macrocontext[0] = '\0';
242 chan->macroexten[0] = '\0';
243 chan->macropriority = 0;
246 if (!strcasecmp(chan->context, fullmacro)) {
247 /* If we're leaving the macro normally, restore original information */
248 chan->priority = oldpriority;
249 ast_copy_string(chan->context, oldcontext, sizeof(chan->context));
250 if (!(chan->_softhangup & AST_SOFTHANGUP_ASYNCGOTO)) {
251 /* Copy the extension, so long as we're not in softhangup, where we could be given an asyncgoto */
252 ast_copy_string(chan->exten, oldexten, sizeof(chan->exten));
253 if ((offsets = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"))) {
254 /* Handle macro offset if it's set by checking the availability of step n + offset + 1, otherwise continue
255 normally if there is any problem */
256 if (sscanf(offsets, "%d", &offset) == 1) {
257 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + offset + 1, chan->cid.cid_num)) {
258 chan->priority += offset;
265 pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", save_macro_offset);
266 if (save_macro_offset)
267 free(save_macro_offset);
268 LOCAL_USER_REMOVE(u);
272 static int macroif_exec(struct ast_channel *chan, void *data)
274 char *expr = NULL, *label_a = NULL, *label_b = NULL;
277 if((expr = ast_strdupa((char *) data))) {
278 if ((label_a = strchr(expr, '?'))) {
281 if ((label_b = strchr(label_a, ':'))) {
286 macro_exec(chan, label_a);
288 macro_exec(chan, label_b);
291 ast_log(LOG_WARNING, "Invalid Syntax.\n");
293 ast_log(LOG_ERROR, "Out of Memory!\n");
297 static int macro_exit_exec(struct ast_channel *chan, void *data)
299 return MACRO_EXIT_RESULT;
302 int unload_module(void)
304 STANDARD_HANGUP_LOCALUSERS;
305 ast_unregister_application(if_app);
306 ast_unregister_application(exit_app);
307 return ast_unregister_application(app);
310 int load_module(void)
312 ast_register_application(exit_app, macro_exit_exec, exit_synopsis, exit_descrip);
313 ast_register_application(if_app, macroif_exec, if_synopsis, if_descrip);
314 return ast_register_application(app, macro_exec, synopsis, descrip);
317 char *description(void)
325 STANDARD_USECOUNT(res);
331 return ASTERISK_GPL_KEY;