on this pass, only remove duplicate log messages
[asterisk/asterisk.git] / funcs / func_strings.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2005, Digium, Inc.
5  * Portions Copyright (C) 2005, Tilghman Lesher.  All rights reserved.
6  * Portions Copyright (C) 2005, Anthony Minessale II
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 String manipulation dialplan functions
22  *
23  * \author Tilghman Lesher
24  * \author Anothony Minessale II 
25  */
26
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <sys/types.h>
31 #include <regex.h>
32
33 #include "asterisk.h"
34
35 /* ASTERISK_FILE_VERSION(__FILE__, "$Revision$") */
36
37 #include "asterisk/channel.h"
38 #include "asterisk/pbx.h"
39 #include "asterisk/logger.h"
40 #include "asterisk/utils.h"
41 #include "asterisk/app.h"
42 #include "asterisk/localtime.h"
43
44 static char *function_fieldqty(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
45 {
46         char *varval, workspace[4096];
47         int fieldcount = 0;
48         char *parse;
49         AST_DECLARE_APP_ARGS(args,
50                 AST_APP_ARG(varname);
51                 AST_APP_ARG(delim);
52         );
53
54         if (!(parse = ast_strdupa(data))) {
55                 ast_copy_string(buf, "0", len);
56                 return buf;
57         }
58
59         AST_STANDARD_APP_ARGS(args, parse);
60         if (args.delim) {
61                 pbx_retrieve_variable(chan, args.varname, &varval, workspace, sizeof(workspace), NULL);
62                 while (strsep(&varval, args.delim))
63                         fieldcount++;
64         } else {
65                 fieldcount = 1;
66         }
67         snprintf(buf, len, "%d", fieldcount);
68
69         return buf;
70 }
71
72 #ifndef BUILTIN_FUNC
73 static
74 #endif
75 struct ast_custom_function fieldqty_function = {
76         .name = "FIELDQTY",
77         .synopsis = "Count the fields, with an arbitrary delimiter",
78         .syntax = "FIELDQTY(<varname>,<delim>)",
79         .read = function_fieldqty,
80 };
81
82 static char *builtin_function_filter(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
83 {
84         char *parse;
85         AST_DECLARE_APP_ARGS(args,
86                 AST_APP_ARG(allowed);
87                 AST_APP_ARG(string);
88         );
89         char *outbuf=buf;
90
91         if (!(parse = ast_strdupa(data)))
92                 return "";
93
94         AST_STANDARD_APP_ARGS(args, parse);
95
96         if (!args.string ) {
97                 ast_log(LOG_ERROR, "Usage: FILTER(<allowed-chars>,<string>)\n");
98                 return "";
99         }
100
101         for ( ; *(args.string) && (buf + len - 1 > outbuf); (args.string)++) {
102                 if (strchr(args.allowed, *(args.string))) {
103                         *outbuf = *(args.string);
104                         outbuf++;
105                 }
106         }
107         *outbuf = '\0';
108         
109         return buf;
110 }
111
112 #ifndef BUILTIN_FUNC
113 static
114 #endif
115 struct ast_custom_function filter_function = {
116         .name = "FILTER",
117         .synopsis = "Filter the string to include only the allowed characters",
118         .syntax = "FILTER(<allowed-chars>,<string>)",
119         .read = builtin_function_filter,
120 };
121
122 static char *builtin_function_regex(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len) 
123 {
124         char *parse;
125         AST_DECLARE_APP_ARGS(args,
126                 AST_APP_ARG(null);
127                 AST_APP_ARG(reg);
128                 AST_APP_ARG(str);
129         );
130                                 
131
132         char errstr[256] = "";
133         int errcode;
134         regex_t regexbuf;
135
136         ast_copy_string(buf, "0", len);
137         
138         if (!(parse = ast_strdupa(data)))
139                 return buf;
140
141         AST_NONSTANDARD_APP_ARGS(args, parse, '"');
142
143         ast_log(LOG_DEBUG, "FUNCTION REGEX (%s)(%s)\n", args.reg, args.str);
144
145         if ((errcode = regcomp(&regexbuf, args.reg, REG_EXTENDED | REG_NOSUB))) {
146                 regerror(errcode, &regexbuf, errstr, sizeof(errstr));
147                 ast_log(LOG_WARNING, "Malformed input %s(%s): %s\n", cmd, data, errstr);
148         } else {
149                 if (!regexec(&regexbuf, args.str, 0, NULL, 0))
150                         ast_copy_string(buf, "1", len); 
151         }
152         regfree(&regexbuf);
153
154         return buf;
155 }
156
157 #ifndef BUILTIN_FUNC
158 static
159 #endif
160 struct ast_custom_function regex_function = {
161         .name = "REGEX",
162         .synopsis = "Regular Expression: Returns 1 if data matches regular expression.",
163         .syntax = "REGEX(\"<regular expression>\" <data>)",
164         .read = builtin_function_regex,
165 };
166
167 static void builtin_function_array(struct ast_channel *chan, char *cmd, char *data, const char *value)
168 {
169         AST_DECLARE_APP_ARGS(arg1,
170                 AST_APP_ARG(var)[100];
171         );
172         AST_DECLARE_APP_ARGS(arg2,
173                 AST_APP_ARG(val)[100];
174         );
175         char *var, *value2;
176         int i;
177
178         var = ast_strdupa(data);
179         value2 = ast_strdupa(value);
180         if (!var || !value2)
181                 return;
182
183         /* The functions this will generally be used with are SORT and ODBC_*, which
184          * both return comma-delimited lists.  However, if somebody uses literal lists,
185          * their commas will be translated to vertical bars by the load, and I don't
186          * want them to be surprised by the result.  Hence, we prefer commas as the
187          * delimiter, but we'll fall back to vertical bars if commas aren't found.
188          */
189         ast_log(LOG_DEBUG, "array (%s=%s)\n", var, value2);
190         if (strchr(var, ',')) {
191                 AST_NONSTANDARD_APP_ARGS(arg1, var, ',');
192         } else {
193                 AST_STANDARD_APP_ARGS(arg1, var);
194         }
195
196         if (strchr(value2, ',')) {
197                 AST_NONSTANDARD_APP_ARGS(arg2, value2, ',');
198         } else {
199                 AST_STANDARD_APP_ARGS(arg2, value2);
200         }
201
202         for (i = 0; i < arg1.argc; i++) {
203                 ast_log(LOG_DEBUG, "array set value (%s=%s)\n", arg1.var[i], arg2.val[i]);
204                 if (i < arg2.argc) {
205                         pbx_builtin_setvar_helper(chan, arg1.var[i], arg2.val[i]);
206                 } else {
207                         /* We could unset the variable, by passing a NULL, but due to
208                          * pushvar semantics, that could create some undesired behavior. */
209                         pbx_builtin_setvar_helper(chan, arg1.var[i], "");
210                 }
211         }
212 }
213
214 #ifndef BUILTIN_FUNC
215 static
216 #endif
217 struct ast_custom_function array_function = {
218         .name = "ARRAY",
219         .synopsis = "Allows setting multiple variables at once",
220         .syntax = "ARRAY(var1[,var2[...][,varN]])",
221         .write = builtin_function_array,
222         .desc =
223 "The comma-separated list passed as a value to which the function is set will\n"
224 "be interpreted as a set of values to which the comma-separated list of\n"
225 "variable names in the argument should be set.\n"
226 "Hence, Set(ARRAY(var1,var2)=1,2) will set var1 to 1 and var2 to 2\n"
227 "Note: remember to either backslash your commas in extensions.conf or quote the\n"
228 "entire argument, since Set can take multiple arguments itself.\n",
229 };
230
231 static char *builtin_function_len(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len) 
232 {
233         int length = 0;
234         if (data) {
235                 length = strlen(data);
236         }
237         snprintf(buf, len, "%d", length);
238         return buf;
239 }
240
241 #ifndef BUILTIN_FUNC
242 static
243 #endif
244 struct ast_custom_function len_function = {
245         .name = "LEN",
246         .synopsis = "Returns the length of the argument given",
247         .syntax = "LEN(<string>)",
248         .read = builtin_function_len,
249 };
250
251 static char *acf_strftime(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len) 
252 {
253         char *parse;
254         AST_DECLARE_APP_ARGS(args,
255                 AST_APP_ARG(epoch);
256                 AST_APP_ARG(timezone);
257                 AST_APP_ARG(format);
258         );
259         long epochi;
260         struct tm time;
261
262         buf[0] = '\0';
263
264         if (ast_strlen_zero(data)) {
265                 ast_log(LOG_ERROR, "Asterisk function STRFTIME() requires an argument.\n");
266                 return buf;
267         }
268         
269         if (!(parse = ast_strdupa(data)))
270                 return buf;
271         
272         AST_STANDARD_APP_ARGS(args, parse);
273
274         if (ast_strlen_zero(args.epoch) || !sscanf(args.epoch, "%ld", &epochi)) {
275                 struct timeval tv = ast_tvnow();
276                 epochi = tv.tv_sec;
277         }
278
279         ast_localtime(&epochi, &time, args.timezone);
280
281         if (!strftime(buf, len, args.format?args.format:"%c", &time)) {
282                 ast_log(LOG_WARNING, "C function strftime() output nothing?!!\n");
283         }
284         buf[len - 1] = '\0';
285
286         return buf;
287 }
288
289 #ifndef BUILTIN_FUNC
290 static
291 #endif
292 struct ast_custom_function strftime_function = {
293         .name = "STRFTIME",
294         .synopsis = "Returns the current date/time in a specified format.",
295         .syntax = "STRFTIME([<epoch>][,[timezone][,format]])",
296         .read = acf_strftime,
297 };
298
299 static char *function_eval(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len) 
300 {
301         memset(buf, 0, len);
302
303         if (ast_strlen_zero(data)) {
304                 ast_log(LOG_WARNING, "EVAL requires an argument: EVAL(<string>)\n");
305                 return buf;
306         }
307
308         pbx_substitute_variables_helper(chan, data, buf, len - 1);
309
310         return buf;
311 }
312
313 #ifndef BUILTIN_FUNC
314 static
315 #endif
316 struct ast_custom_function eval_function = {
317         .name = "EVAL",
318         .synopsis = "Evaluate stored variables.",
319         .syntax = "EVAL(<variable>)",
320         .desc = "Using EVAL basically causes a string to be evaluated twice.\n"
321                 "When a variable or expression is in the dialplan, it will be\n"
322                 "evaluated at runtime. However, if the result of the evaluation\n"
323                 "is in fact a variable or expression, using EVAL will have it\n"
324                 "evaluated a second time. For example, if the variable ${MYVAR}\n"
325                 "contains \"${OTHERVAR}\", then the result of putting ${EVAL(${MYVAR})}\n"
326                 "in the dialplan will be the contents of the variable, OTHERVAR.\n"
327                 "Normally, by just putting ${MYVAR} in the dialplan, you would be\n"
328                 "left with \"${OTHERVAR}\".\n", 
329         .read = function_eval,
330 };
331