644b9043eb63b23414323122c7b5fbf0d1d38d26
[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/module.h"
38 #include "asterisk/channel.h"
39 #include "asterisk/pbx.h"
40 #include "asterisk/logger.h"
41 #include "asterisk/utils.h"
42 #include "asterisk/app.h"
43 #include "asterisk/localtime.h"
44
45 static char *function_fieldqty(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
46 {
47         char *varval, workspace[4096];
48         int fieldcount = 0;
49         char *parse;
50         AST_DECLARE_APP_ARGS(args,
51                 AST_APP_ARG(varname);
52                 AST_APP_ARG(delim);
53         );
54
55         if (!(parse = ast_strdupa(data))) {
56                 ast_copy_string(buf, "0", len);
57                 return buf;
58         }
59
60         AST_STANDARD_APP_ARGS(args, parse);
61         if (args.delim) {
62                 pbx_retrieve_variable(chan, args.varname, &varval, workspace, sizeof(workspace), NULL);
63                 while (strsep(&varval, args.delim))
64                         fieldcount++;
65         } else {
66                 fieldcount = 1;
67         }
68         snprintf(buf, len, "%d", fieldcount);
69
70         return buf;
71 }
72
73 static struct ast_custom_function fieldqty_function = {
74         .name = "FIELDQTY",
75         .synopsis = "Count the fields, with an arbitrary delimiter",
76         .syntax = "FIELDQTY(<varname>,<delim>)",
77         .read = function_fieldqty,
78 };
79
80 static char *filter(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
81 {
82         char *parse;
83         AST_DECLARE_APP_ARGS(args,
84                 AST_APP_ARG(allowed);
85                 AST_APP_ARG(string);
86         );
87         char *outbuf=buf;
88
89         if (!(parse = ast_strdupa(data)))
90                 return "";
91
92         AST_STANDARD_APP_ARGS(args, parse);
93
94         if (!args.string ) {
95                 ast_log(LOG_ERROR, "Usage: FILTER(<allowed-chars>,<string>)\n");
96                 return "";
97         }
98
99         for ( ; *(args.string) && (buf + len - 1 > outbuf); (args.string)++) {
100                 if (strchr(args.allowed, *(args.string))) {
101                         *outbuf = *(args.string);
102                         outbuf++;
103                 }
104         }
105         *outbuf = '\0';
106         
107         return buf;
108 }
109
110 static struct ast_custom_function filter_function = {
111         .name = "FILTER",
112         .synopsis = "Filter the string to include only the allowed characters",
113         .syntax = "FILTER(<allowed-chars>,<string>)",
114         .read = filter,
115 };
116
117 static char *regex(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len) 
118 {
119         char *parse;
120         AST_DECLARE_APP_ARGS(args,
121                 AST_APP_ARG(null);
122                 AST_APP_ARG(reg);
123                 AST_APP_ARG(str);
124         );
125                                 
126
127         char errstr[256] = "";
128         int errcode;
129         regex_t regexbuf;
130
131         ast_copy_string(buf, "0", len);
132         
133         if (!(parse = ast_strdupa(data)))
134                 return buf;
135
136         AST_NONSTANDARD_APP_ARGS(args, parse, '"');
137
138         ast_log(LOG_DEBUG, "FUNCTION REGEX (%s)(%s)\n", args.reg, args.str);
139
140         if ((errcode = regcomp(&regexbuf, args.reg, REG_EXTENDED | REG_NOSUB))) {
141                 regerror(errcode, &regexbuf, errstr, sizeof(errstr));
142                 ast_log(LOG_WARNING, "Malformed input %s(%s): %s\n", cmd, data, errstr);
143         } else {
144                 if (!regexec(&regexbuf, args.str, 0, NULL, 0))
145                         ast_copy_string(buf, "1", len); 
146         }
147         regfree(&regexbuf);
148
149         return buf;
150 }
151
152 static struct ast_custom_function regex_function = {
153         .name = "REGEX",
154         .synopsis = "Regular Expression: Returns 1 if data matches regular expression.",
155         .syntax = "REGEX(\"<regular expression>\" <data>)",
156         .read = regex,
157 };
158
159 static void array(struct ast_channel *chan, char *cmd, char *data, const char *value)
160 {
161         AST_DECLARE_APP_ARGS(arg1,
162                 AST_APP_ARG(var)[100];
163         );
164         AST_DECLARE_APP_ARGS(arg2,
165                 AST_APP_ARG(val)[100];
166         );
167         char *var, *value2;
168         int i;
169
170         var = ast_strdupa(data);
171         value2 = ast_strdupa(value);
172         if (!var || !value2)
173                 return;
174
175         /* The functions this will generally be used with are SORT and ODBC_*, which
176          * both return comma-delimited lists.  However, if somebody uses literal lists,
177          * their commas will be translated to vertical bars by the load, and I don't
178          * want them to be surprised by the result.  Hence, we prefer commas as the
179          * delimiter, but we'll fall back to vertical bars if commas aren't found.
180          */
181         ast_log(LOG_DEBUG, "array (%s=%s)\n", var, value2);
182         if (strchr(var, ',')) {
183                 AST_NONSTANDARD_APP_ARGS(arg1, var, ',');
184         } else {
185                 AST_STANDARD_APP_ARGS(arg1, var);
186         }
187
188         if (strchr(value2, ',')) {
189                 AST_NONSTANDARD_APP_ARGS(arg2, value2, ',');
190         } else {
191                 AST_STANDARD_APP_ARGS(arg2, value2);
192         }
193
194         for (i = 0; i < arg1.argc; i++) {
195                 ast_log(LOG_DEBUG, "array set value (%s=%s)\n", arg1.var[i], arg2.val[i]);
196                 if (i < arg2.argc) {
197                         pbx_builtin_setvar_helper(chan, arg1.var[i], arg2.val[i]);
198                 } else {
199                         /* We could unset the variable, by passing a NULL, but due to
200                          * pushvar semantics, that could create some undesired behavior. */
201                         pbx_builtin_setvar_helper(chan, arg1.var[i], "");
202                 }
203         }
204 }
205
206 static struct ast_custom_function array_function = {
207         .name = "ARRAY",
208         .synopsis = "Allows setting multiple variables at once",
209         .syntax = "ARRAY(var1[,var2[...][,varN]])",
210         .write = array,
211         .desc =
212 "The comma-separated list passed as a value to which the function is set will\n"
213 "be interpreted as a set of values to which the comma-separated list of\n"
214 "variable names in the argument should be set.\n"
215 "Hence, Set(ARRAY(var1,var2)=1,2) will set var1 to 1 and var2 to 2\n"
216 "Note: remember to either backslash your commas in extensions.conf or quote the\n"
217 "entire argument, since Set can take multiple arguments itself.\n",
218 };
219
220 static char *len(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len) 
221 {
222         int length = 0;
223         if (data) {
224                 length = strlen(data);
225         }
226         snprintf(buf, len, "%d", length);
227         return buf;
228 }
229
230 static struct ast_custom_function len_function = {
231         .name = "LEN",
232         .synopsis = "Returns the length of the argument given",
233         .syntax = "LEN(<string>)",
234         .read = len,
235 };
236
237 static char *acf_strftime(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len) 
238 {
239         char *parse;
240         AST_DECLARE_APP_ARGS(args,
241                 AST_APP_ARG(epoch);
242                 AST_APP_ARG(timezone);
243                 AST_APP_ARG(format);
244         );
245         long epochi;
246         struct tm time;
247
248         buf[0] = '\0';
249
250         if (ast_strlen_zero(data)) {
251                 ast_log(LOG_ERROR, "Asterisk function STRFTIME() requires an argument.\n");
252                 return buf;
253         }
254         
255         if (!(parse = ast_strdupa(data)))
256                 return buf;
257         
258         AST_STANDARD_APP_ARGS(args, parse);
259
260         if (ast_strlen_zero(args.epoch) || !sscanf(args.epoch, "%ld", &epochi)) {
261                 struct timeval tv = ast_tvnow();
262                 epochi = tv.tv_sec;
263         }
264
265         ast_localtime(&epochi, &time, args.timezone);
266
267         if (!strftime(buf, len, args.format?args.format:"%c", &time)) {
268                 ast_log(LOG_WARNING, "C function strftime() output nothing?!!\n");
269         }
270         buf[len - 1] = '\0';
271
272         return buf;
273 }
274
275 static struct ast_custom_function strftime_function = {
276         .name = "STRFTIME",
277         .synopsis = "Returns the current date/time in a specified format.",
278         .syntax = "STRFTIME([<epoch>][,[timezone][,format]])",
279         .read = acf_strftime,
280 };
281
282 static char *acf_strptime(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len) 
283 {
284         AST_DECLARE_APP_ARGS(args,
285                 AST_APP_ARG(timestring);
286                 AST_APP_ARG(timezone);
287                 AST_APP_ARG(format);
288         );
289         struct tm time;
290
291         memset(&time, 0, sizeof(struct tm));
292         
293         buf[0] = '\0';
294
295         if (!data) {
296                 ast_log(LOG_ERROR, "Asterisk function STRPTIME() requires an argument.\n");
297                 return buf;
298         }
299
300         AST_STANDARD_APP_ARGS(args, data);
301
302         if (ast_strlen_zero(args.format) ) {
303                 ast_log(LOG_ERROR, "No format supplied to STRPTIME(<timestring>|<timezone>|<format>)");
304                 return buf;
305         }
306                         
307         if (!strptime(args.timestring, args.format, &time)) {
308                 ast_log(LOG_WARNING, "C function strptime() output nothing?!!\n");
309         } else {
310                 snprintf(buf, len, "%d", (int)ast_mktime(&time, args.timezone));          
311         }
312         
313         return buf;
314 }
315
316 static struct ast_custom_function strptime_function = {
317         .name = "STRPTIME",
318         .synopsis = "Returns the epoch of the arbitrary date/time string structured as described in the format.",
319         .syntax = "STRPTIME(<datetime>|<timezone>|<format>)",
320         .desc =
321 "This is useful for converting a date into an EPOCH time, possibly to pass to\n"
322 "an application like SayUnixTime or to calculate the difference between two\n"
323 "date strings.\n"
324 "\n"
325 "Example:\n"
326 "  ${STRPTIME(2006-03-01 07:30:35|America/Chicago|%Y-%m-%d %H:%M:%S)} returns 1141219835\n",
327         .read = acf_strptime,
328 };
329
330 static char *function_eval(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len) 
331 {
332         memset(buf, 0, len);
333
334         if (ast_strlen_zero(data)) {
335                 ast_log(LOG_WARNING, "EVAL requires an argument: EVAL(<string>)\n");
336                 return buf;
337         }
338
339         pbx_substitute_variables_helper(chan, data, buf, len - 1);
340
341         return buf;
342 }
343
344 static struct ast_custom_function eval_function = {
345         .name = "EVAL",
346         .synopsis = "Evaluate stored variables.",
347         .syntax = "EVAL(<variable>)",
348         .desc = "Using EVAL basically causes a string to be evaluated twice.\n"
349                 "When a variable or expression is in the dialplan, it will be\n"
350                 "evaluated at runtime. However, if the result of the evaluation\n"
351                 "is in fact a variable or expression, using EVAL will have it\n"
352                 "evaluated a second time. For example, if the variable ${MYVAR}\n"
353                 "contains \"${OTHERVAR}\", then the result of putting ${EVAL(${MYVAR})}\n"
354                 "in the dialplan will be the contents of the variable, OTHERVAR.\n"
355                 "Normally, by just putting ${MYVAR} in the dialplan, you would be\n"
356                 "left with \"${OTHERVAR}\".\n", 
357         .read = function_eval,
358 };
359
360 static char *tdesc = "String handling dialplan functions";
361
362 int unload_module(void)
363 {
364         int res = 0;
365
366         res |= ast_custom_function_unregister(&fieldqty_function);
367         res |= ast_custom_function_unregister(&filter_function);
368         res |= ast_custom_function_unregister(&regex_function);
369         res |= ast_custom_function_unregister(&array_function);
370         res |= ast_custom_function_unregister(&len_function);
371         res |= ast_custom_function_unregister(&strftime_function);
372         res |= ast_custom_function_unregister(&strptime_function);
373         res |= ast_custom_function_unregister(&eval_function);
374
375         return res;
376 }
377
378 int load_module(void)
379 {
380         int res = 0;
381
382         res |= ast_custom_function_register(&fieldqty_function);
383         res |= ast_custom_function_register(&filter_function);
384         res |= ast_custom_function_register(&regex_function);
385         res |= ast_custom_function_register(&array_function);
386         res |= ast_custom_function_register(&len_function);
387         res |= ast_custom_function_register(&strftime_function);
388         res |= ast_custom_function_register(&strptime_function);
389         res |= ast_custom_function_register(&eval_function);
390
391         return res;
392 }
393
394 char *description(void)
395 {
396         return tdesc;
397 }
398
399 int usecount(void)
400 {
401         return 0;
402 }
403
404 char *key()
405 {
406         return ASTERISK_GPL_KEY;
407 }
408
409 /*
410 Local Variables:
411 mode: C
412 c-file-style: "linux"
413 indent-tabs-mode: nil
414 End:
415 */