2 * Asterisk -- A telephony toolkit for Linux.
6 * Copyright (C) 2003, Digium
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>
15 #include <asterisk/file.h>
16 #include <asterisk/logger.h>
17 #include <asterisk/channel.h>
18 #include <asterisk/pbx.h>
19 #include <asterisk/module.h>
20 #include <asterisk/options.h>
21 #include <asterisk/utils.h>
31 static char *tdesc = "Extension Macros";
33 static char *descrip =
34 " Macro(macroname|arg1|arg2...): Executes a macro using the context\n"
35 "'macro-<macroname>', jumping to the 's' extension of that context and\n"
36 "executing each step, then returning when the steps end. The calling\n"
37 "extension, context, and priority are stored in ${MACRO_EXTEN}, \n"
38 "${MACRO_CONTEXT} and ${MACRO_PRIORITY} respectively. Arguments become\n"
39 "${ARG1}, ${ARG2}, etc in the macro context. Macro returns -1 if\n"
40 "any step in the macro returns -1, and 0 otherwise. If you Goto out\n"
41 "of the Macro context, the Macro will terminate and control will be return\n"
42 "at the location of the Goto. Otherwise if ${MACRO_OFFSET} is set at\n"
43 "termination, Macro will attempt to continue at priority\n"
44 "MACRO_OFFSET + N + 1 if such a step exists, and N + 1 otherwise.\n";
46 static char *app = "Macro";
48 static char *synopsis = "Macro Implementation";
54 static int macro_exec(struct ast_channel *chan, void *data)
61 char *oldargs[MAX_ARGS + 1] = { NULL, };
64 char oldexten[256]="";
67 char oldcontext[256] = "";
70 int setmacrocontext=0;
72 char *save_macro_exten;
73 char *save_macro_context;
74 char *save_macro_priority;
75 char *save_macro_offset;
77 if (!data || ast_strlen_zero(data)) {
78 ast_log(LOG_WARNING, "Invalid Macro incantation\n");
81 strncpy(tmp, data, sizeof(tmp) - 1);
83 macro = strsep(&rest, "|");
84 if (!macro || ast_strlen_zero(macro)) {
85 ast_log(LOG_WARNING, "Invalid macro name specified\n");
88 snprintf(fullmacro, sizeof(fullmacro), "macro-%s", macro);
89 if (!ast_exists_extension(chan, fullmacro, "s", 1, chan->callerid)) {
90 if (!ast_context_find(fullmacro))
91 ast_log(LOG_WARNING, "No such context '%s' for macro '%s'\n", fullmacro, macro);
93 ast_log(LOG_WARNING, "Context '%s' for macro '%s' lacks 's' extension, priority 1\n", fullmacro, macro);
97 oldpriority = chan->priority;
98 strncpy(oldexten, chan->exten, sizeof(oldexten) - 1);
99 strncpy(oldcontext, chan->context, sizeof(oldcontext) - 1);
100 if (ast_strlen_zero(chan->macrocontext)) {
101 strncpy(chan->macrocontext, chan->context, sizeof(chan->macrocontext) - 1);
102 strncpy(chan->macroexten, chan->exten, sizeof(chan->macroexten) - 1);
103 chan->macropriority = chan->priority;
107 /* Save old macro variables */
108 save_macro_exten = pbx_builtin_getvar_helper(chan, "MACRO_EXTEN");
109 if (save_macro_exten) save_macro_exten = strdup(save_macro_exten);
110 pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", oldexten);
112 save_macro_context = pbx_builtin_getvar_helper(chan, "MACRO_CONTEXT");
113 if (save_macro_context) save_macro_context = strdup(save_macro_context);
114 pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", oldcontext);
116 save_macro_priority = pbx_builtin_getvar_helper(chan, "MACRO_PRIORITY");
117 if (save_macro_priority) save_macro_priority = strdup(save_macro_priority);
118 snprintf(pc, sizeof(pc), "%d", oldpriority);
119 pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", pc);
121 save_macro_offset = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET");
122 if (save_macro_offset) save_macro_offset = strdup(save_macro_offset);
123 pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", NULL);
125 /* Setup environment for new run */
126 strcpy(chan->exten, "s");
127 strncpy(chan->context, fullmacro, sizeof(chan->context));
130 while((cur = strsep(&rest, "|")) && (argc < MAX_ARGS)) {
131 /* Save copy of old arguments if we're overwriting some, otherwise
132 let them pass through to the other macro */
133 snprintf(varname, sizeof(varname), "ARG%d", argc);
134 oldargs[argc] = pbx_builtin_getvar_helper(chan, varname);
136 oldargs[argc] = strdup(oldargs[argc]);
137 pbx_builtin_setvar_helper(chan, varname, cur);
140 while(ast_exists_extension(chan, chan->context, chan->exten, chan->priority, chan->callerid)) {
141 if ((res = ast_spawn_extension(chan, chan->context, chan->exten, chan->priority, chan->callerid))) {
142 /* Something bad happened, or a hangup has been requested. */
143 if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F'))) {
144 /* Just return result as to the previous application as if it had been dialed */
145 ast_log(LOG_DEBUG, "Oooh, got something to jump out with ('%c')!\n", res);
149 case AST_PBX_KEEPALIVE:
151 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);
152 else if (option_verbose > 1)
153 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);
158 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);
159 else if (option_verbose > 1)
160 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);
164 if (strcasecmp(chan->context, fullmacro)) {
165 if (option_verbose > 1)
166 ast_verbose(VERBOSE_PREFIX_2 "Channel '%s' jumping out of macro '%s'\n", chan->name, macro);
169 /* don't stop executing extensions when we're in "h" */
170 if (chan->_softhangup && strcasecmp(oldexten,"h")) {
171 ast_log(LOG_DEBUG, "Extension %s, priority %d returned normally even though call was hung up\n",
172 chan->exten, chan->priority);
178 for (x=1;x<argc;x++) {
179 /* Restore old arguments and delete ours */
180 snprintf(varname, sizeof(varname), "ARG%d", x);
182 pbx_builtin_setvar_helper(chan, varname, oldargs[x]);
185 pbx_builtin_setvar_helper(chan, varname, NULL);
189 /* Restore macro variables */
190 pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", save_macro_exten);
191 if (save_macro_exten) free(save_macro_exten);
192 pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", save_macro_context);
193 if (save_macro_context) free(save_macro_context);
194 pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", save_macro_priority);
195 if (save_macro_priority) free(save_macro_priority);
196 if (setmacrocontext) {
197 strcpy(chan->macrocontext, "");
198 strcpy(chan->macroexten, "");
199 chan->macropriority = 0;
202 if (!strcasecmp(chan->context, fullmacro)) {
203 /* If we're leaving the macro normally, restore original information */
204 chan->priority = oldpriority;
205 strncpy(chan->context, oldcontext, sizeof(chan->context) - 1);
206 if (!(chan->_softhangup & AST_SOFTHANGUP_ASYNCGOTO)) {
207 /* Copy the extension, so long as we're not in softhangup, where we could be given an asyncgoto */
208 strncpy(chan->exten, oldexten, sizeof(chan->exten) - 1);
209 if ((offsets = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"))) {
210 /* Handle macro offset if it's set by checking the availability of step n + offset + 1, otherwise continue
211 normally if there is any problem */
212 if (sscanf(offsets, "%d", &offset) == 1) {
213 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + offset + 1, chan->callerid)) {
214 chan->priority += offset;
221 pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", save_macro_offset);
222 if (save_macro_offset) free(save_macro_offset);
226 int unload_module(void)
228 STANDARD_HANGUP_LOCALUSERS;
229 return ast_unregister_application(app);
232 int load_module(void)
234 return ast_register_application(app, macro_exec, synopsis, descrip);
237 char *description(void)
245 STANDARD_USECOUNT(res);
251 return ASTERISK_GPL_KEY;