Fix func_config list entry allocation
[asterisk/asterisk.git] / funcs / func_logic.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  * Portions Copyright (C) 2005, Anthony Minessale II
6  *
7  * See http://www.asterisk.org for more information about
8  * the Asterisk project. Please do not directly contact
9  * any of the maintainers of this project for assistance;
10  * the project provides a web site, mailing lists and IRC
11  * channels for your use.
12  *
13  * This program is free software, distributed under the terms of
14  * the GNU General Public License Version 2. See the LICENSE file
15  * at the top of the source tree.
16  */
17
18 /*! \file
19  * 
20  * \brief Conditional logic dialplan functions
21  * 
22  * \author Anthony Minessale II
23  *
24  * \ingroup functions
25  */
26
27 /*** MODULEINFO
28         <support_level>core</support_level>
29  ***/
30
31 #include "asterisk.h"
32
33 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
34
35 #include "asterisk/module.h"
36 #include "asterisk/channel.h"
37 #include "asterisk/pbx.h"
38 #include "asterisk/utils.h"
39 #include "asterisk/app.h"
40
41 /*** DOCUMENTATION
42         <function name="ISNULL" language="en_US">
43                 <synopsis>
44                         Check if a value is NULL.
45                 </synopsis>
46                 <syntax>
47                         <parameter name="data" required="true" />
48                 </syntax>
49                 <description>
50                         <para>Returns <literal>1</literal> if NULL or <literal>0</literal> otherwise.</para>
51                 </description>
52         </function>
53         <function name="SET" language="en_US">
54                 <synopsis>
55                         SET assigns a value to a channel variable.
56                 </synopsis>
57                 <syntax argsep="=">
58                         <parameter name="varname" required="true" />
59                         <parameter name="value" />
60                 </syntax>
61                 <description>
62                 </description>
63         </function>
64         <function name="EXISTS" language="en_US">
65                 <synopsis>
66                         Test the existence of a value.
67                 </synopsis>
68                 <syntax>
69                         <parameter name="data" required="true" />
70                 </syntax>
71                 <description>
72                         <para>Returns <literal>1</literal> if exists, <literal>0</literal> otherwise.</para>
73                 </description>
74         </function>
75         <function name="IF" language="en_US">
76                 <synopsis>
77                         Check for an expresion.
78                 </synopsis>
79                 <syntax argsep="?">
80                         <parameter name="expresion" required="true" />
81                         <parameter name="retvalue" argsep=":" required="true">
82                                 <argument name="true" />
83                                 <argument name="false" />
84                         </parameter>
85                 </syntax>
86                 <description>
87                         <para>Returns the data following <literal>?</literal> if true, else the data following <literal>:</literal></para>
88                 </description>  
89         </function>
90         <function name="IFTIME" language="en_US">
91                 <synopsis>
92                         Temporal Conditional.
93                 </synopsis>
94                 <syntax argsep="?">
95                         <parameter name="timespec" required="true" />
96                         <parameter name="retvalue" required="true" argsep=":">
97                                 <argument name="true" />
98                                 <argument name="false" />
99                         </parameter>
100                 </syntax>
101                 <description>
102                         <para>Returns the data following <literal>?</literal> if true, else the data following <literal>:</literal></para>
103                 </description>
104         </function>
105         <function name="IMPORT" language="en_US">
106                 <synopsis>
107                         Retrieve the value of a variable from another channel.
108                 </synopsis>
109                 <syntax>
110                         <parameter name="channel" required="true" />
111                         <parameter name="variable" required="true" />
112                 </syntax>
113                 <description>
114                 </description>
115         </function>
116  ***/
117
118 static int isnull(struct ast_channel *chan, const char *cmd, char *data,
119                   char *buf, size_t len)
120 {
121         strcpy(buf, data && *data ? "0" : "1");
122
123         return 0;
124 }
125
126 static int exists(struct ast_channel *chan, const char *cmd, char *data, char *buf,
127                   size_t len)
128 {
129         strcpy(buf, data && *data ? "1" : "0");
130
131         return 0;
132 }
133
134 static int iftime(struct ast_channel *chan, const char *cmd, char *data, char *buf,
135                   size_t len)
136 {
137         struct ast_timing timing;
138         char *expr;
139         char *iftrue;
140         char *iffalse;
141
142         data = ast_strip_quoted(data, "\"", "\"");
143         expr = strsep(&data, "?");
144         iftrue = strsep(&data, ":");
145         iffalse = data;
146
147         if (ast_strlen_zero(expr) || !(iftrue || iffalse)) {
148                 ast_log(LOG_WARNING,
149                                 "Syntax IFTIME(<timespec>?[<true>][:<false>])\n");
150                 return -1;
151         }
152
153         if (!ast_build_timing(&timing, expr)) {
154                 ast_log(LOG_WARNING, "Invalid Time Spec.\n");
155                 ast_destroy_timing(&timing);
156                 return -1;
157         }
158
159         if (iftrue)
160                 iftrue = ast_strip_quoted(iftrue, "\"", "\"");
161         if (iffalse)
162                 iffalse = ast_strip_quoted(iffalse, "\"", "\"");
163
164         ast_copy_string(buf, ast_check_timing(&timing) ? S_OR(iftrue, "") : S_OR(iffalse, ""), len);
165         ast_destroy_timing(&timing);
166
167         return 0;
168 }
169
170 static int acf_if(struct ast_channel *chan, const char *cmd, char *data, char *buf,
171                   size_t len)
172 {
173         AST_DECLARE_APP_ARGS(args1,
174                 AST_APP_ARG(expr);
175                 AST_APP_ARG(remainder);
176         );
177         AST_DECLARE_APP_ARGS(args2,
178                 AST_APP_ARG(iftrue);
179                 AST_APP_ARG(iffalse);
180         );
181         args2.iftrue = args2.iffalse = NULL; /* you have to set these, because if there is nothing after the '?',
182                                                                                         then args1.remainder will be NULL, not a pointer to a null string, and
183                                                                                         then any garbage in args2.iffalse will not be cleared, and you'll crash.
184                                                                                     -- and if you mod the ast_app_separate_args func instead, you'll really
185                                                                                         mess things up badly, because the rest of everything depends on null args
186                                                                                         for non-specified stuff. */
187         
188         AST_NONSTANDARD_APP_ARGS(args1, data, '?');
189         AST_NONSTANDARD_APP_ARGS(args2, args1.remainder, ':');
190
191         if (ast_strlen_zero(args1.expr) || !(args2.iftrue || args2.iffalse)) {
192                 ast_log(LOG_WARNING, "Syntax IF(<expr>?[<true>][:<false>])  (expr must be non-null, and either <true> or <false> must be non-null)\n");
193                 ast_log(LOG_WARNING, "      In this case, <expr>='%s', <true>='%s', and <false>='%s'\n", args1.expr, args2.iftrue, args2.iffalse);
194                 return -1;
195         }
196
197         args1.expr = ast_strip(args1.expr);
198         if (args2.iftrue)
199                 args2.iftrue = ast_strip(args2.iftrue);
200         if (args2.iffalse)
201                 args2.iffalse = ast_strip(args2.iffalse);
202
203         ast_copy_string(buf, pbx_checkcondition(args1.expr) ? (S_OR(args2.iftrue, "")) : (S_OR(args2.iffalse, "")), len);
204
205         return 0;
206 }
207
208 static int set(struct ast_channel *chan, const char *cmd, char *data, char *buf,
209                size_t len)
210 {
211         char *varname;
212         char *val;
213
214         varname = strsep(&data, "=");
215         val = data;
216
217         if (ast_strlen_zero(varname) || !val) {
218                 ast_log(LOG_WARNING, "Syntax SET(<varname>=[<value>])\n");
219                 return -1;
220         }
221
222         varname = ast_strip(varname);
223         val = ast_strip(val);
224         pbx_builtin_setvar_helper(chan, varname, val);
225         ast_copy_string(buf, val, len);
226
227         return 0;
228 }
229
230 static int set2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **str, ssize_t len)
231 {
232         if (len > -1) {
233                 ast_str_make_space(str, len == 0 ? strlen(data) : len);
234         }
235         return set(chan, cmd, data, ast_str_buffer(*str), ast_str_size(*str));
236 }
237
238 static int import_helper(struct ast_channel *chan, const char *cmd, char *data, char *buf, struct ast_str **str, ssize_t len)
239 {
240         AST_DECLARE_APP_ARGS(args,
241                 AST_APP_ARG(channel);
242                 AST_APP_ARG(varname);
243         );
244         AST_STANDARD_APP_ARGS(args, data);
245         if (buf) {
246                 *buf = '\0';
247         }
248
249         if (!ast_strlen_zero(args.varname)) {
250                 struct ast_channel *chan2;
251
252                 if ((chan2 = ast_channel_get_by_name(args.channel))) {
253                         char *s = ast_alloca(strlen(args.varname) + 4);
254                         sprintf(s, "${%s}", args.varname);
255                         ast_channel_lock(chan2);
256                         if (buf) {
257                                 pbx_substitute_variables_helper(chan2, s, buf, len);
258                         } else {
259                                 ast_str_substitute_variables(str, len, chan2, s);
260                         }
261                         ast_channel_unlock(chan2);
262                         chan2 = ast_channel_unref(chan2);
263                 }
264         }
265
266         return 0;
267 }
268
269 static int import_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
270 {
271         return import_helper(chan, cmd, data, buf, NULL, len);
272 }
273
274 static int import_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **str, ssize_t len)
275 {
276         return import_helper(chan, cmd, data, NULL, str, len);
277 }
278
279 static struct ast_custom_function isnull_function = {
280         .name = "ISNULL",
281         .read = isnull,
282         .read_max = 2,
283 };
284
285 static struct ast_custom_function set_function = {
286         .name = "SET",
287         .read = set,
288         .read2 = set2,
289 };
290
291 static struct ast_custom_function exists_function = {
292         .name = "EXISTS",
293         .read = exists,
294         .read_max = 2,
295 };
296
297 static struct ast_custom_function if_function = {
298         .name = "IF",
299         .read = acf_if,
300 };
301
302 static struct ast_custom_function if_time_function = {
303         .name = "IFTIME",
304         .read = iftime,
305 };
306
307 static struct ast_custom_function import_function = {
308         .name = "IMPORT",
309         .read = import_read,
310         .read2 = import_read2,
311 };
312
313 static int unload_module(void)
314 {
315         int res = 0;
316
317         res |= ast_custom_function_unregister(&isnull_function);
318         res |= ast_custom_function_unregister(&set_function);
319         res |= ast_custom_function_unregister(&exists_function);
320         res |= ast_custom_function_unregister(&if_function);
321         res |= ast_custom_function_unregister(&if_time_function);
322         res |= ast_custom_function_unregister(&import_function);
323
324         return res;
325 }
326
327 static int load_module(void)
328 {
329         int res = 0;
330
331         res |= ast_custom_function_register(&isnull_function);
332         res |= ast_custom_function_register(&set_function);
333         res |= ast_custom_function_register(&exists_function);
334         res |= ast_custom_function_register(&if_function);
335         res |= ast_custom_function_register(&if_time_function);
336         res |= ast_custom_function_register(&import_function);
337
338         return res;
339 }
340
341 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Logical dialplan functions");