It is no longer required for each module that deals with a channel to call ast_module...
[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 Implementation
22  *
23  * \author Anthony Minessale <anthmct@yahoo.com>
24  * 
25  * \ingroup applications
26  */
27
28 #include "asterisk.h"
29
30 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
31
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <unistd.h>
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) {ast_module_user_remove(u); return ret;}
48
49
50 static char *start_app = "While";
51 static char *start_desc = 
52 "Usage:  While(<expr>)\n"
53 "Start a While Loop.  Execution will return to this point when\n"
54 "EndWhile is called until expr is no longer true.\n";
55
56 static char *start_synopsis = "Start a while loop";
57
58
59 static char *stop_app = "EndWhile";
60 static char *stop_desc = 
61 "Usage:  EndWhile()\n"
62 "Return to the previous called While\n";
63
64 static char *stop_synopsis = "End a while loop";
65
66 static char *exit_app = "ExitWhile";
67 static char *exit_desc =
68 "Usage:  ExitWhile()\n"
69 "Exits a While loop, whether or not the conditional has been satisfied.\n";
70 static char *exit_synopsis = "End a While loop";
71
72 static char *continue_app = "ContinueWhile";
73 static char *continue_desc =
74 "Usage:  ContinueWhile()\n"
75 "Returns to the top of the while loop and re-evaluates the conditional.\n";
76 static char *continue_synopsis = "Restart a While loop";
77
78 #define VAR_SIZE 64
79
80
81 static const char *get_index(struct ast_channel *chan, const char *prefix, int index) {
82         char varname[VAR_SIZE];
83
84         snprintf(varname, VAR_SIZE, "%s_%d", prefix, index);
85         return pbx_builtin_getvar_helper(chan, varname);
86 }
87
88 static struct ast_exten *find_matching_priority(struct ast_context *c, const char *exten, int priority, const char *callerid)
89 {
90         struct ast_exten *e;
91         struct ast_include *i;
92         struct ast_context *c2;
93
94         for (e=ast_walk_context_extensions(c, NULL); e; e=ast_walk_context_extensions(c, e)) {
95                 if (ast_extension_match(ast_get_extension_name(e), exten)) {
96                         int needmatch = ast_get_extension_matchcid(e);
97                         if ((needmatch && ast_extension_match(ast_get_extension_cidmatch(e), callerid)) ||
98                                 (!needmatch)) {
99                                 /* This is the matching extension we want */
100                                 struct ast_exten *p;
101                                 for (p=ast_walk_extension_priorities(e, NULL); p; p=ast_walk_extension_priorities(e, p)) {
102                                         if (priority != ast_get_extension_priority(p))
103                                                 continue;
104                                         return p;
105                                 }
106                         }
107                 }
108         }
109
110         /* No match; run through includes */
111         for (i=ast_walk_context_includes(c, NULL); i; i=ast_walk_context_includes(c, i)) {
112                 for (c2=ast_walk_contexts(NULL); c2; c2=ast_walk_contexts(c2)) {
113                         if (!strcmp(ast_get_context_name(c2), ast_get_include_name(i))) {
114                                 e = find_matching_priority(c2, exten, priority, callerid);
115                                 if (e)
116                                         return e;
117                         }
118                 }
119         }
120         return NULL;
121 }
122
123 static int find_matching_endwhile(struct ast_channel *chan)
124 {
125         struct ast_context *c;
126         int res=-1;
127
128         if (ast_rdlock_contexts()) {
129                 ast_log(LOG_ERROR, "Failed to lock contexts list\n");
130                 return -1;
131         }
132
133         for (c=ast_walk_contexts(NULL); c; c=ast_walk_contexts(c)) {
134                 struct ast_exten *e;
135
136                 if (!ast_rdlock_context(c)) {
137                         if (!strcmp(ast_get_context_name(c), chan->context)) {
138                                 /* This is the matching context we want */
139                                 int cur_priority = chan->priority + 1, level=1;
140
141                                 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)) {
142                                         if (!strcasecmp(ast_get_extension_app(e), "WHILE")) {
143                                                 level++;
144                                         } else if (!strcasecmp(ast_get_extension_app(e), "ENDWHILE")) {
145                                                 level--;
146                                         }
147
148                                         if (level == 0) {
149                                                 res = cur_priority;
150                                                 break;
151                                         }
152                                 }
153                         }
154                         ast_unlock_context(c);
155                         if (res > 0) {
156                                 break;
157                         }
158                 }
159         }
160         ast_unlock_contexts();
161         return res;
162 }
163
164 static int _while_exec(struct ast_channel *chan, void *data, int end)
165 {
166         int res=0;
167         struct ast_module_user *u;
168         const char *while_pri = NULL;
169         char *my_name = NULL;
170         const char *condition = NULL, *label = NULL;
171         char varname[VAR_SIZE], end_varname[VAR_SIZE];
172         const char *prefix = "WHILE";
173         size_t size=0;
174         int used_index_i = -1, x=0;
175         char used_index[VAR_SIZE] = "0", new_index[VAR_SIZE] = "0";
176
177         if (!chan) {
178                 /* huh ? */
179                 return -1;
180         }
181
182         u = ast_module_user_add(chan);
183
184         /* dont want run away loops if the chan isn't even up
185            this is up for debate since it slows things down a tad ......
186         */
187         if (ast_waitfordigit(chan,1) < 0)
188                 ALL_DONE(u,-1);
189
190
191         for (x=0;;x++) {
192                 if (get_index(chan, prefix, x)) {
193                         used_index_i = x;
194                 } else 
195                         break;
196         }
197         
198         snprintf(used_index, VAR_SIZE, "%d", used_index_i);
199         snprintf(new_index, VAR_SIZE, "%d", used_index_i + 1);
200         
201         if (!end)
202                 condition = ast_strdupa(data);
203
204         size = strlen(chan->context) + strlen(chan->exten) + 32;
205         my_name = alloca(size);
206         memset(my_name, 0, size);
207         snprintf(my_name, size, "%s_%s_%d", chan->context, chan->exten, chan->priority);
208         
209         if (ast_strlen_zero(label)) {
210                 if (end) 
211                         label = used_index;
212                 else if (!(label = pbx_builtin_getvar_helper(chan, my_name))) {
213                         label = new_index;
214                         pbx_builtin_setvar_helper(chan, my_name, label);
215                 }
216                 
217         }
218         
219         snprintf(varname, VAR_SIZE, "%s_%s", prefix, label);
220         while_pri = pbx_builtin_getvar_helper(chan, varname);
221         
222         if ((while_pri = pbx_builtin_getvar_helper(chan, varname)) && !end) {
223                 snprintf(end_varname,VAR_SIZE,"END_%s",varname);
224         }
225         
226
227         if ((!end && !pbx_checkcondition(condition)) || (end == 2)) {
228                 /* Condition Met (clean up helper vars) */
229                 const char *goto_str;
230                 pbx_builtin_setvar_helper(chan, varname, NULL);
231                 pbx_builtin_setvar_helper(chan, my_name, NULL);
232                 snprintf(end_varname,VAR_SIZE,"END_%s",varname);
233                 if ((goto_str=pbx_builtin_getvar_helper(chan, end_varname))) {
234                         ast_parseable_goto(chan, goto_str);
235                         pbx_builtin_setvar_helper(chan, end_varname, NULL);
236                 } else {
237                         int pri = find_matching_endwhile(chan);
238                         if (pri > 0) {
239                                 if (option_verbose > 2)
240                                         ast_verbose(VERBOSE_PREFIX_3 "Jumping to priority %d\n", pri);
241                                 chan->priority = pri;
242                         } else {
243                                 ast_log(LOG_WARNING, "Couldn't find matching EndWhile? (While at %s@%s priority %d)\n", chan->context, chan->exten, chan->priority);
244                         }
245                 }
246                 ALL_DONE(u,res);
247         }
248
249         if (!end && !while_pri) {
250                 char *goto_str;
251                 size = strlen(chan->context) + strlen(chan->exten) + 32;
252                 goto_str = alloca(size);
253                 memset(goto_str, 0, size);
254                 snprintf(goto_str, size, "%s|%s|%d", chan->context, chan->exten, chan->priority);
255                 pbx_builtin_setvar_helper(chan, varname, goto_str);
256         } 
257
258         else if (end && while_pri) {
259                 /* END of loop */
260                 snprintf(end_varname, VAR_SIZE, "END_%s", varname);
261                 if (! pbx_builtin_getvar_helper(chan, end_varname)) {
262                         char *goto_str;
263                         size = strlen(chan->context) + strlen(chan->exten) + 32;
264                         goto_str = alloca(size);
265                         memset(goto_str, 0, size);
266                         snprintf(goto_str, size, "%s|%s|%d", chan->context, chan->exten, chan->priority+1);
267                         pbx_builtin_setvar_helper(chan, end_varname, goto_str);
268                 }
269                 ast_parseable_goto(chan, while_pri);
270         }
271         
272
273
274
275         ALL_DONE(u, res);
276 }
277
278 static int while_start_exec(struct ast_channel *chan, void *data) {
279         return _while_exec(chan, data, 0);
280 }
281
282 static int while_end_exec(struct ast_channel *chan, void *data) {
283         return _while_exec(chan, data, 1);
284 }
285
286 static int while_exit_exec(struct ast_channel *chan, void *data) {
287         return _while_exec(chan, data, 2);
288 }
289
290 static int while_continue_exec(struct ast_channel *chan, void *data)
291 {
292         int x;
293         const char *prefix = "WHILE", *while_pri=NULL;
294
295         for (x = 0; ; x++) {
296                 const char *tmp = get_index(chan, prefix, x);
297                 if (tmp)
298                         while_pri = tmp;
299                 else
300                         break;
301         }
302
303         if (while_pri)
304                 ast_parseable_goto(chan, while_pri);
305
306         return 0;
307 }
308
309 static int unload_module(void)
310 {
311         int res;
312         
313         res = ast_unregister_application(start_app);
314         res |= ast_unregister_application(stop_app);
315         res |= ast_unregister_application(exit_app);
316         res |= ast_unregister_application(continue_app);
317
318         return res;
319 }
320
321 static int load_module(void)
322 {
323         int res;
324
325         res = ast_register_application(start_app, while_start_exec, start_synopsis, start_desc);
326         res |= ast_register_application(stop_app, while_end_exec, stop_synopsis, stop_desc);
327         res |= ast_register_application(exit_app, while_exit_exec, exit_synopsis, exit_desc);
328         res |= ast_register_application(continue_app, while_continue_exec, continue_synopsis, continue_desc);
329
330         return res;
331 }
332
333 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "While Loops and Conditional Execution");