mer feb 12 14:56:57 CET 2003
[asterisk/asterisk.git] / apps / app_macro.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Macro Implementation
5  * 
6  * Copyright (C) 2003, Digium
7  *
8  * Mark Spencer <markster@digium.com>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13
14 #include <asterisk/file.h>
15 #include <asterisk/logger.h>
16 #include <asterisk/channel.h>
17 #include <asterisk/pbx.h>
18 #include <asterisk/module.h>
19 #include <asterisk/options.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <string.h>
23 #include <stdlib.h>
24
25 #include <pthread.h>
26
27 #define MAX_ARGS 80
28
29 static char *tdesc = "Extension Macros";
30
31 static char *descrip =
32 "  Macro(macroname|arg1|arg2...): Executes a macro using the context\n"
33 "'macro-<macroname>', jumping to the 's' extension of that context and\n"
34 "executing each step, then returning when the steps end.  The calling\n"
35 "extension, context, and priority are stored in ${MACRO_EXTEN}, \n"
36 "${MACRO_CONTEXT} and ${MACRO_PRIORITY} respectively.  Arguments become\n"
37 "${ARG1}, ${ARG2}, etc in the macro context.  Macro returns -1 if\n"
38 "any step in the macro returns -1, and 0 otherwise.  If you Goto out\n"
39 "of the Macro context, the Macro will terminate and control will be return\n"
40 "at the location of the Goto.  Otherwise if ${MACRO_OFFSET} is set at\n"
41 "termination, Macro will attempt to continue at priority\n"
42 "MACRO_OFFSET + N + 1 if such a step exists, and N + 1 otherwise.\n";
43
44 static char *app = "Macro";
45
46 static char *synopsis = "Macro Implementation";
47
48 STANDARD_LOCAL_USER;
49
50 LOCAL_USER_DECL;
51
52 static int macro_exec(struct ast_channel *chan, void *data)
53 {
54   char tmp[256] = "";
55   char *cur, *rest;
56   char *macro;
57   char fullmacro[80];
58   char varname[80];
59   char *oldargs[MAX_ARGS + 1] = { NULL, };
60   int argc, x;
61   int res;
62   char oldexten[256]="";
63   int oldpriority;
64   char pc[80];
65   char oldcontext[256] = "";
66   char *offsets;
67   int offset;
68   
69   char *save_macro_exten;
70   char *save_macro_context;
71   char *save_macro_priority;
72   char *save_macro_offset;
73   
74   if (!data || !strlen(data)) {
75     ast_log(LOG_WARNING, "Invalid Macro incantation\n");
76     return 0;
77   }
78   strncpy(tmp, data, sizeof(tmp) - 1);
79   rest = tmp;
80   macro = strsep(&rest, "|");
81   if (!macro || !strlen(macro)) {
82         ast_log(LOG_WARNING, "Invalid macro name specified\n");
83         return 0;
84   }
85   snprintf(fullmacro, sizeof(fullmacro), "macro-%s", macro);
86   if (!ast_exists_extension(chan, fullmacro, "s", 1, chan->callerid)) {
87         if (!ast_context_find(fullmacro)) 
88                 ast_log(LOG_WARNING, "No such context '%s' for macro '%s'\n", fullmacro, macro);
89         else
90                 ast_log(LOG_WARNING, "Context '%s' for macro '%s' lacks 's' extension, priority 1\n", fullmacro, macro);
91         return 0;
92   }
93   /* Save old info */
94   oldpriority = chan->priority;
95   strncpy(oldexten, chan->exten, sizeof(oldexten) - 1);
96   strncpy(oldcontext, chan->context, sizeof(oldcontext) - 1);
97   argc = 1;
98   /* Save old macro variables */
99   save_macro_exten = pbx_builtin_getvar_helper(chan, "MACRO_EXTEN");
100   if (save_macro_exten) save_macro_exten = strdup(save_macro_exten);
101   pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", oldexten);
102
103   save_macro_context = pbx_builtin_getvar_helper(chan, "MACRO_CONTEXT");
104   if (save_macro_context) save_macro_context = strdup(save_macro_context);
105   pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", oldcontext);
106
107   save_macro_priority = pbx_builtin_getvar_helper(chan, "MACRO_PRIORITY");
108   if (save_macro_priority) save_macro_priority = strdup(save_macro_priority);
109   snprintf(pc, sizeof(pc), "%d", oldpriority);
110   pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", pc);
111   
112   save_macro_offset = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET");
113   if (save_macro_offset) save_macro_offset = strdup(save_macro_offset);
114   pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", NULL);
115
116   /* Setup environment for new run */
117   strcpy(chan->exten, "s");
118   strncpy(chan->context, fullmacro, sizeof(chan->context));
119   chan->priority = 1;
120
121   while((cur = strsep(&rest, "|")) && (argc < MAX_ARGS)) {
122         /* Save copy of old arguments if we're overwriting some, otherwise
123            let them pass through to the other macro */
124         oldargs[argc] = pbx_builtin_getvar_helper(chan, varname);
125         if (oldargs[argc])
126                 oldargs[argc] = strdup(oldargs[argc]);
127         snprintf(varname, sizeof(varname), "ARG%d", argc);
128         pbx_builtin_setvar_helper(chan, varname, cur);
129         argc++;
130   }
131   while(ast_exists_extension(chan, chan->context, chan->exten, chan->priority, chan->callerid)) {
132         if ((res = ast_spawn_extension(chan, chan->context, chan->exten, chan->priority, chan->callerid))) {
133                 /* Something bad happened, or a hangup has been requested. */
134                 if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F'))) {
135                         /* Just return result as to the previous application as if it had been dialed */
136                         ast_log(LOG_DEBUG, "Oooh, got something to jump out with ('%c')!\n", res);
137                         break;
138                 }
139                 switch(res) {
140                 case AST_PBX_KEEPALIVE:
141                         if (option_debug)
142                                 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);
143                         else if (option_verbose > 1)
144                                 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);
145                         goto out;
146                         break;
147                 default:
148                         if (option_debug)
149                                 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);
150                         else if (option_verbose > 1)
151                                 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);
152                         goto out;
153                 }
154         }
155         if (strcasecmp(chan->context, fullmacro)) {
156                 if (option_verbose > 1)
157                         ast_verbose(VERBOSE_PREFIX_2 "Channel '%s' jumping out of macro '%s'\n", chan->name, macro);
158                 break;
159         }
160         if (chan->_softhangup) {
161                 ast_log(LOG_DEBUG, "Extension %s, priority %d returned normally even though call was hung up\n",
162                         chan->exten, chan->priority);
163                 goto out;
164         }
165         chan->priority++;
166   }
167 out:
168   for (x=1;x<argc;x++) {
169         /* Restore old arguments and delete ours */
170         snprintf(varname, sizeof(varname), "ARG%d", x);
171         if (oldargs[x]) {
172                 pbx_builtin_setvar_helper(chan, varname, oldargs[x]);
173                 free(oldargs[x]);
174         } else {
175                 pbx_builtin_setvar_helper(chan, varname, NULL);
176         }
177   }
178
179   /* Restore macro variables */
180   pbx_builtin_setvar_helper(chan, "MACRO_EXTEN", save_macro_exten);
181   if (save_macro_exten) free(save_macro_exten);
182   pbx_builtin_setvar_helper(chan, "MACRO_CONTEXT", save_macro_context);
183   if (save_macro_context) free(save_macro_context);
184   pbx_builtin_setvar_helper(chan, "MACRO_PRIORITY", save_macro_priority);
185   if (save_macro_priority) free(save_macro_priority);
186
187   if (!strcasecmp(chan->context, fullmacro)) {
188         /* If we're leaving the macro normally, restore original information */
189         chan->priority = oldpriority;
190         strncpy(chan->exten, oldexten, sizeof(chan->exten) - 1);
191         strncpy(chan->context, oldcontext, sizeof(chan->context) - 1);
192         if ((offsets = pbx_builtin_getvar_helper(chan, "MACRO_OFFSET"))) {
193                 /* Handle macro offset if it's set by checking the availability of step n + offset + 1, otherwise continue
194                    normally if there is any problem */
195                 if (sscanf(offsets, "%d", &offset) == 1) {
196                         if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + offset + 1, chan->callerid)) {
197                                 chan->priority += offset;
198                         }
199                 }
200         }
201   }
202
203   pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", save_macro_offset);
204   if (save_macro_offset) free(save_macro_offset);
205   return res;
206 }
207
208 int unload_module(void)
209 {
210         STANDARD_HANGUP_LOCALUSERS;
211         return ast_unregister_application(app);
212 }
213
214 int load_module(void)
215 {
216         return ast_register_application(app, macro_exec, synopsis, descrip);
217 }
218
219 char *description(void)
220 {
221         return tdesc;
222 }
223
224 int usecount(void)
225 {
226         int res;
227         STANDARD_USECOUNT(res);
228         return res;
229 }
230
231 char *key()
232 {
233         return ASTERISK_GPL_KEY;
234 }