make While() able to find the matching EndWhile() for when the condition is zero...
[asterisk/asterisk.git] / apps / app_while.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * While Loop and ExecIf Implementations
5  * 
6  * Copyright 2004 - 2005, Anthony Minessale <anthmct@yahoo.com>
7  *
8  * Anthony Minessale <anthmct@yahoo.com>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <string.h>
17
18 #include "asterisk.h"
19
20 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
21
22 #include "asterisk/file.h"
23 #include "asterisk/logger.h"
24 #include "asterisk/channel.h"
25 #include "asterisk/utils.h"
26 #include "asterisk/config.h"
27 #include "asterisk/pbx.h"
28 #include "asterisk/module.h"
29 #include "asterisk/lock.h"
30 #include "asterisk/options.h"
31
32 #define ALL_DONE(u,ret) {LOCAL_USER_REMOVE(u); return ret;}
33
34
35 static char *exec_app = "ExecIf";
36 static char *exec_desc = 
37 "Usage:  ExecIF (<expr>|<app>|<data>)\n"
38 "If <expr> is true, execute and return the result of <app>(<data>).\n"
39 "If <expr> is true, but <app> is not found, then the application\n"
40 "will return a non-zero value.";
41 static char *exec_synopsis = "Conditional exec";
42
43 static char *start_app = "While";
44 static char *start_desc = 
45 "Usage:  While(<expr>)\n"
46 "Start a While Loop.  Execution will return to this point when\n"
47 "EndWhile is called until expr is no longer true.\n";
48
49 static char *start_synopsis = "Start A While Loop";
50
51
52 static char *stop_app = "EndWhile";
53 static char *stop_desc = 
54 "Usage:  EndWhile()\n"
55 "Return to the previous called While\n\n";
56
57 static char *stop_synopsis = "End A While Loop";
58
59 static char *tdesc = "While Loops and Conditional Execution";
60
61
62
63 STANDARD_LOCAL_USER;
64
65 LOCAL_USER_DECL;
66
67 static int execif_exec(struct ast_channel *chan, void *data) {
68         int res=0;
69         struct localuser *u;
70         char *myapp = NULL;
71         char *mydata = NULL;
72         char *expr = NULL;
73         struct ast_app *app = NULL;
74
75         LOCAL_USER_ADD(u);
76         expr = ast_strdupa((char *) data);
77         if ((myapp = strchr(expr,'|'))) {
78                 *myapp = '\0';
79                 myapp++;
80                 if ((mydata = strchr(myapp,'|'))) {
81                         *mydata = '\0';
82                         mydata++;
83                 } else
84                         mydata = "";
85
86                 if (ast_true(expr)) { 
87                         if ((app = pbx_findapp(myapp))) {
88                                 res = pbx_exec(chan, app, mydata, 1);
89                         } else {
90                                 ast_log(LOG_WARNING, "Count not find application! (%s)\n", myapp);
91                                 res = -1;
92                         }
93                 }
94         } else {
95                 ast_log(LOG_ERROR,"Invalid Syntax.\n");
96                 res = -1;
97         }
98                 
99         ALL_DONE(u,res);
100 }
101
102 #define VAR_SIZE 64
103
104
105 static char *get_index(struct ast_channel *chan, const char *prefix, int index) {
106         char varname[VAR_SIZE];
107
108         snprintf(varname, VAR_SIZE, "%s_%d", prefix, index);
109         return pbx_builtin_getvar_helper(chan, varname);
110 }
111
112 static struct ast_exten *find_matching_priority(struct ast_context *c, const char *exten, int priority, const char *callerid)
113 {
114         struct ast_exten *e;
115         struct ast_include *i;
116         struct ast_context *c2;
117
118         for (e=ast_walk_context_extensions(c, NULL); e; e=ast_walk_context_extensions(c, e)) {
119                 if (ast_extension_match(ast_get_extension_name(e), exten)) {
120                         int needmatch = ast_get_extension_matchcid(e);
121                         if ((needmatch && ast_extension_match(ast_get_extension_cidmatch(e), callerid)) ||
122                                 (!needmatch)) {
123                                 /* This is the matching extension we want */
124                                 struct ast_exten *p;
125                                 for (p=ast_walk_extension_priorities(e, NULL); p; p=ast_walk_extension_priorities(e, p)) {
126                                         if (priority != ast_get_extension_priority(p))
127                                                 continue;
128                                         return p;
129                                 }
130                         }
131                 }
132         }
133
134         /* No match; run through includes */
135         for (i=ast_walk_context_includes(c, NULL); i; i=ast_walk_context_includes(c, i)) {
136                 for (c2=ast_walk_contexts(NULL); c2; c2=ast_walk_contexts(c2)) {
137                         if (!strcmp(ast_get_context_name(c2), ast_get_include_name(i))) {
138                                 e = find_matching_priority(c2, exten, priority, callerid);
139                                 if (e)
140                                         return e;
141                         }
142                 }
143         }
144         return NULL;
145 }
146
147 static int find_matching_endwhile(struct ast_channel *chan)
148 {
149         struct ast_context *c;
150         int res=-1;
151
152         if (ast_lock_contexts()) {
153                 ast_log(LOG_ERROR, "Failed to lock contexts list\n");
154                 return -1;
155         }
156
157         for (c=ast_walk_contexts(NULL); c; c=ast_walk_contexts(c)) {
158                 struct ast_exten *e;
159
160                 if (!ast_lock_context(c)) {
161                         if (!strcmp(ast_get_context_name(c), chan->context)) {
162                                 /* This is the matching context we want */
163                                 int cur_priority = chan->priority + 1, level=1;
164
165                                 for (e = find_matching_priority(c, chan->exten, cur_priority, chan->cid.cid_num); e; e = find_matching_priority(c, chan->exten, ++cur_priority, chan->cid.cid_num)) {
166                                         if (!strcasecmp(ast_get_extension_app(e), "WHILE")) {
167                                                 level++;
168                                         } else if (!strcasecmp(ast_get_extension_app(e), "ENDWHILE")) {
169                                                 level--;
170                                         }
171
172                                         if (level == 0) {
173                                                 res = cur_priority;
174                                                 break;
175                                         }
176                                 }
177                         }
178                         ast_unlock_context(c);
179                         if (res > 0) {
180                                 break;
181                         }
182                 }
183         }
184         ast_unlock_contexts();
185         return res;
186 }
187
188 static int _while_exec(struct ast_channel *chan, void *data, int end)
189 {
190         int res=0;
191         struct localuser *u;
192         char *while_pri = NULL;
193         char *goto_str = NULL, *my_name = NULL;
194         char *condition = NULL, *label = NULL;
195         char varname[VAR_SIZE], end_varname[VAR_SIZE];
196         const char *prefix = "WHILE";
197         size_t size=0;
198         int used_index_i = -1, x=0;
199         char used_index[VAR_SIZE] = "0", new_index[VAR_SIZE] = "0";
200         
201         if (!chan) {
202                 /* huh ? */
203                 return -1;
204         }
205
206         LOCAL_USER_ADD(u);
207
208         /* dont want run away loops if the chan isn't even up
209            this is up for debate since it slows things down a tad ......
210         */
211         if (ast_waitfordigit(chan,1) < 0)
212                 ALL_DONE(u,-1);
213
214
215         for (x=0;;x++) {
216                 if (get_index(chan, prefix, x)) {
217                         used_index_i = x;
218                 } else 
219                         break;
220         }
221         
222         snprintf(used_index, VAR_SIZE, "%d", used_index_i);
223         snprintf(new_index, VAR_SIZE, "%d", used_index_i + 1);
224         
225         if (!end) {
226                 condition = ast_strdupa((char *) data);
227         }
228
229         size = strlen(chan->context) + strlen(chan->exten) + 32;
230         my_name = alloca(size);
231         memset(my_name, 0, size);
232         snprintf(my_name, size, "%s_%s_%d", chan->context, chan->exten, chan->priority);
233         
234         if (!label || ast_strlen_zero(label)) {
235                 if (end) 
236                         label = used_index;
237                 else if (!(label = pbx_builtin_getvar_helper(chan, my_name))) {
238                         label = new_index;
239                         pbx_builtin_setvar_helper(chan, my_name, label);
240                 }
241                 
242         }
243         
244         snprintf(varname, VAR_SIZE, "%s_%s", prefix, label);
245         while_pri = pbx_builtin_getvar_helper(chan, varname);
246         
247         if ((while_pri = pbx_builtin_getvar_helper(chan, varname)) && !end) {
248                 snprintf(end_varname,VAR_SIZE,"END_%s",varname);
249         }
250         
251
252         if (!end && !ast_true(condition)) {
253                 /* Condition Met (clean up helper vars) */
254                 pbx_builtin_setvar_helper(chan, varname, NULL);
255                 pbx_builtin_setvar_helper(chan, my_name, NULL);
256         snprintf(end_varname,VAR_SIZE,"END_%s",varname);
257                 if ((goto_str=pbx_builtin_getvar_helper(chan, end_varname))) {
258                         pbx_builtin_setvar_helper(chan, end_varname, NULL);
259                         ast_parseable_goto(chan, goto_str);
260                 } else {
261                         int pri = find_matching_endwhile(chan);
262                         if (pri > 0) {
263                                 if (option_verbose > 2)
264                                         ast_verbose(VERBOSE_PREFIX_3 "Jumping to priority %d\n", pri);
265                                 chan->priority = pri;
266                         } else {
267                                 ast_log(LOG_WARNING, "Couldn't find matching EndWhile? (While at %s@%s priority %d)\n", chan->context, chan->exten, chan->priority);
268                         }
269                 }
270                 ALL_DONE(u,res);
271         }
272
273         if (!end && !while_pri) {
274                 size = strlen(chan->context) + strlen(chan->exten) + 32;
275                 goto_str = alloca(size);
276                 memset(goto_str, 0, size);
277                 snprintf(goto_str, size, "%s|%s|%d", chan->context, chan->exten, chan->priority);
278                 pbx_builtin_setvar_helper(chan, varname, goto_str);
279         } 
280
281         else if (end && while_pri) {
282                 /* END of loop */
283                 snprintf(end_varname, VAR_SIZE, "END_%s", varname);
284                 if (! pbx_builtin_getvar_helper(chan, end_varname)) {
285                         size = strlen(chan->context) + strlen(chan->exten) + 32;
286                         goto_str = alloca(size);
287                         memset(goto_str, 0, size);
288                         snprintf(goto_str, size, "%s|%s|%d", chan->context, chan->exten, chan->priority+1);
289                         pbx_builtin_setvar_helper(chan, end_varname, goto_str);
290                 }
291                 ast_parseable_goto(chan, while_pri);
292         }
293         
294
295
296
297         ALL_DONE(u, res);
298 }
299
300 static int while_start_exec(struct ast_channel *chan, void *data) {
301         return _while_exec(chan, data, 0);
302 }
303
304 static int while_end_exec(struct ast_channel *chan, void *data) {
305         return _while_exec(chan, data, 1);
306 }
307
308
309 int unload_module(void)
310 {
311         STANDARD_HANGUP_LOCALUSERS;
312         ast_unregister_application(start_app);
313         ast_unregister_application(exec_app);
314         return ast_unregister_application(stop_app);
315 }
316
317 int load_module(void)
318 {
319         ast_register_application(start_app, while_start_exec, start_synopsis, start_desc);
320         ast_register_application(exec_app, execif_exec, exec_synopsis, exec_desc);
321         return ast_register_application(stop_app, while_end_exec, stop_synopsis, stop_desc);
322 }
323
324 char *description(void)
325 {
326         return tdesc;
327 }
328
329 int usecount(void)
330 {
331         int res;
332         STANDARD_USECOUNT(res);
333         return res;
334 }
335
336 char *key()
337 {
338         return ASTERISK_GPL_KEY;
339 }
340