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