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