With respect to bug 7862, the syntax and description are misleading to users. the...
[asterisk/asterisk.git] / funcs / func_strings.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2005-2006, 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 "asterisk.h"
28
29 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
30
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <sys/types.h>
35 #include <regex.h>
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 int function_fieldqty(struct ast_channel *chan, char *cmd,
46                              char *parse, char *buf, size_t len)
47 {
48         char *varval;
49         int fieldcount = 0;
50         AST_DECLARE_APP_ARGS(args,
51                              AST_APP_ARG(varname);
52                              AST_APP_ARG(delim);
53                 );
54
55         AST_STANDARD_APP_ARGS(args, parse);
56         if (args.delim) {
57                 pbx_retrieve_variable(chan, args.varname, &varval, buf, len, NULL);
58                 while (strsep(&varval, args.delim))
59                         fieldcount++;
60         } else {
61                 fieldcount = 1;
62         }
63         snprintf(buf, len, "%d", fieldcount);
64
65         return 0;
66 }
67
68 static struct ast_custom_function fieldqty_function = {
69         .name = "FIELDQTY",
70         .synopsis = "Count the fields, with an arbitrary delimiter",
71         .syntax = "FIELDQTY(<varname>|<delim>)",
72         .read = function_fieldqty,
73 };
74
75 static int filter(struct ast_channel *chan, char *cmd, char *parse, char *buf,
76                   size_t len)
77 {
78         AST_DECLARE_APP_ARGS(args,
79                              AST_APP_ARG(allowed);
80                              AST_APP_ARG(string);
81         );
82         char *outbuf = buf;
83
84         AST_STANDARD_APP_ARGS(args, parse);
85
86         if (!args.string) {
87                 ast_log(LOG_ERROR, "Usage: FILTER(<allowed-chars>|<string>)\n");
88                 return -1;
89         }
90
91         for (; *(args.string) && (buf + len - 1 > outbuf); (args.string)++) {
92                 if (strchr(args.allowed, *(args.string)))
93                         *outbuf++ = *(args.string);
94         }
95         *outbuf = '\0';
96
97         return 0;
98 }
99
100 static struct ast_custom_function filter_function = {
101         .name = "FILTER",
102         .synopsis = "Filter the string to include only the allowed characters",
103         .syntax = "FILTER(<allowed-chars>|<string>)",
104         .read = filter,
105 };
106
107 static int regex(struct ast_channel *chan, char *cmd, char *parse, char *buf,
108                  size_t len)
109 {
110         AST_DECLARE_APP_ARGS(args,
111                              AST_APP_ARG(null);
112                              AST_APP_ARG(reg);
113                              AST_APP_ARG(str);
114         );
115         int errcode;
116         regex_t regexbuf;
117
118         buf[0] = '\0';
119
120         AST_NONSTANDARD_APP_ARGS(args, parse, '"');
121
122         if (args.argc != 3) {
123                 ast_log(LOG_ERROR, "Unexpected arguments: should have been in the form '\"<regex>\" <string>'\n");
124                 return -1;
125         }
126
127         ast_log(LOG_DEBUG, "FUNCTION REGEX (%s)(%s)\n", args.reg, args.str);
128
129         if ((errcode = regcomp(&regexbuf, args.reg, REG_EXTENDED | REG_NOSUB))) {
130                 regerror(errcode, &regexbuf, buf, len);
131                 ast_log(LOG_WARNING, "Malformed input %s(%s): %s\n", cmd, parse, buf);
132                 return -1;
133         }
134         
135         strcpy(buf, regexec(&regexbuf, args.str, 0, NULL, 0) ? "0" : "1");
136
137         regfree(&regexbuf);
138
139         return 0;
140 }
141
142 static struct ast_custom_function regex_function = {
143         .name = "REGEX",
144         .synopsis = "Regular Expression",
145         .desc =  
146                 "Returns 1 if data matches regular expression, or 0 otherwise.\n"
147                 "Please note that the double quotes separating the expression from the data\n"
148                 "should not have any neighboring spaces, either before or after, unless you\n"
149                 "intend them to be in either the expression or the data!\n",
150         .syntax = "REGEX(\"<regular expression>\"<data>)",
151         .read = regex,
152 };
153
154 static int array(struct ast_channel *chan, char *cmd, char *var,
155                  const char *value)
156 {
157         AST_DECLARE_APP_ARGS(arg1,
158                              AST_APP_ARG(var)[100];
159         );
160         AST_DECLARE_APP_ARGS(arg2,
161                              AST_APP_ARG(val)[100];
162         );
163         char *value2;
164         int i;
165
166         value2 = ast_strdupa(value);
167         if (!var || !value2)
168                 return -1;
169
170         /* The functions this will generally be used with are SORT and ODBC_*, which
171          * both return comma-delimited lists.  However, if somebody uses literal lists,
172          * their commas will be translated to vertical bars by the load, and I don't
173          * want them to be surprised by the result.  Hence, we prefer commas as the
174          * delimiter, but we'll fall back to vertical bars if commas aren't found.
175          */
176         ast_log(LOG_DEBUG, "array (%s=%s)\n", var, value2);
177         if (strchr(var, ','))
178                 AST_NONSTANDARD_APP_ARGS(arg1, var, ',');
179         else
180                 AST_STANDARD_APP_ARGS(arg1, var);
181
182         if (strchr(value2, ','))
183                 AST_NONSTANDARD_APP_ARGS(arg2, value2, ',');
184         else
185                 AST_STANDARD_APP_ARGS(arg2, value2);
186
187         for (i = 0; i < arg1.argc; i++) {
188                 ast_log(LOG_DEBUG, "array set value (%s=%s)\n", arg1.var[i],
189                         arg2.val[i]);
190                 if (i < arg2.argc) {
191                         pbx_builtin_setvar_helper(chan, arg1.var[i], arg2.val[i]);
192                 } else {
193                         /* We could unset the variable, by passing a NULL, but due to
194                          * pushvar semantics, that could create some undesired behavior. */
195                         pbx_builtin_setvar_helper(chan, arg1.var[i], "");
196                 }
197         }
198
199         return 0;
200 }
201
202 static struct ast_custom_function array_function = {
203         .name = "ARRAY",
204         .synopsis = "Allows setting multiple variables at once",
205         .syntax = "ARRAY(var1[|var2[...][|varN]])",
206         .write = array,
207         .desc =
208                 "The comma-separated list passed as a value to which the function is set will\n"
209                 "be interpreted as a set of values to which the comma-separated list of\n"
210                 "variable names in the argument should be set.\n"
211                 "Hence, Set(ARRAY(var1|var2)=1\\,2) will set var1 to 1 and var2 to 2\n"
212                 "Note: remember to either backslash your commas in extensions.conf or quote the\n"
213                 "entire argument, since Set can take multiple arguments itself.\n",
214 };
215
216 static int acf_sprintf(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
217 {
218 #define SPRINTF_FLAG    0
219 #define SPRINTF_WIDTH   1
220 #define SPRINTF_PRECISION       2
221 #define SPRINTF_LENGTH  3
222 #define SPRINTF_CONVERSION      4
223         int i, state = -1, argcount = 0;
224         char *formatstart = NULL, *bufptr = buf;
225         char formatbuf[256] = "";
226         int tmpi;
227         double tmpd;
228         AST_DECLARE_APP_ARGS(arg,
229                                 AST_APP_ARG(format);
230                                 AST_APP_ARG(var)[100];
231         );
232
233         AST_STANDARD_APP_ARGS(arg, data);
234
235         /* Scan the format, converting each argument into the requisite format type. */
236         for (i = 0; arg.format[i]; i++) {
237                 switch (state) {
238                 case SPRINTF_FLAG:
239                         if (strchr("#0- +'I", arg.format[i]))
240                                 break;
241                         state = SPRINTF_WIDTH;
242                 case SPRINTF_WIDTH:
243                         if (arg.format[i] >= '0' && arg.format[i] <= '9')
244                                 break;
245
246                         /* Next character must be a period to go into a precision */
247                         if (arg.format[i] == '.') {
248                                 state = SPRINTF_PRECISION;
249                         } else {
250                                 state = SPRINTF_LENGTH;
251                                 i--;
252                         }
253                         break;
254                 case SPRINTF_PRECISION:
255                         if (arg.format[i] >= '0' && arg.format[i] <= '9')
256                                 break;
257                         state = SPRINTF_LENGTH;
258                 case SPRINTF_LENGTH:
259                         if (strchr("hl", arg.format[i])) {
260                                 if (arg.format[i + 1] == arg.format[i])
261                                         i++;
262                                 state = SPRINTF_CONVERSION;
263                                 break;
264                         } else if (strchr("Lqjzt", arg.format[i]))
265                                 state = SPRINTF_CONVERSION;
266                                 break;
267                         state = SPRINTF_CONVERSION;
268                 case SPRINTF_CONVERSION:
269                         if (strchr("diouxXc", arg.format[i])) {
270                                 /* Integer */
271
272                                 /* Isolate this format alone */
273                                 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
274                                 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
275
276                                 /* Convert the argument into the required type */
277                                 if (sscanf(arg.var[argcount++], "%i", &tmpi) != 1) {
278                                         ast_log(LOG_ERROR, "Argument '%s' is not an integer number for format '%s'\n", arg.var[argcount - 1], formatbuf);
279                                         goto sprintf_fail;
280                                 }
281
282                                 /* Format the argument */
283                                 snprintf(bufptr, buf + len - bufptr, formatbuf, tmpi);
284
285                                 /* Update the position of the next parameter to print */
286                                 bufptr = strchr(buf, '\0');
287                         } else if (strchr("eEfFgGaA", arg.format[i])) {
288                                 /* Double */
289
290                                 /* Isolate this format alone */
291                                 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
292                                 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
293
294                                 /* Convert the argument into the required type */
295                                 if (sscanf(arg.var[argcount++], "%lf", &tmpd) != 1) {
296                                         ast_log(LOG_ERROR, "Argument '%s' is not a floating point number for format '%s'\n", arg.var[argcount - 1], formatbuf);
297                                         goto sprintf_fail;
298                                 }
299
300                                 /* Format the argument */
301                                 snprintf(bufptr, buf + len - bufptr, formatbuf, tmpd);
302
303                                 /* Update the position of the next parameter to print */
304                                 bufptr = strchr(buf, '\0');
305                         } else if (arg.format[i] == 's') {
306                                 /* String */
307
308                                 /* Isolate this format alone */
309                                 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
310                                 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
311
312                                 /* Format the argument */
313                                 snprintf(bufptr, buf + len - bufptr, formatbuf, arg.var[argcount++]);
314
315                                 /* Update the position of the next parameter to print */
316                                 bufptr = strchr(buf, '\0');
317                         } else if (arg.format[i] == '%') {
318                                 /* Literal data to copy */
319                                 *bufptr++ = arg.format[i];
320                         } else {
321                                 /* Not supported */
322
323                                 /* Isolate this format alone */
324                                 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
325                                 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
326
327                                 ast_log(LOG_ERROR, "Format type not supported: '%s' with argument '%s'\n", formatbuf, arg.var[argcount++]);
328                                 goto sprintf_fail;
329                         }
330                         state = -1;
331                         break;
332                 default:
333                         if (arg.format[i] == '%') {
334                                 state = SPRINTF_FLAG;
335                                 formatstart = &arg.format[i];
336                                 break;
337                         } else {
338                                 /* Literal data to copy */
339                                 *bufptr++ = arg.format[i];
340                         }
341                 }
342         }
343         return 0;
344 sprintf_fail:
345         return -1;
346 }
347
348 static struct ast_custom_function sprintf_function = {
349         .name = "SPRINTF",
350         .synopsis = "Format a variable according to a format string",
351         .syntax = "SPRINTF(<format>|<arg1>[|...<argN>])",
352         .read = acf_sprintf,
353         .desc =
354 "Parses the format string specified and returns a string matching that format.\n"
355 "Supports most options supported by sprintf(3).  Returns a shortened string if\n"
356 "a format specifier is not recognized.\n",
357 };
358
359 static int quote(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
360 {
361         char *bufptr = buf, *dataptr = data;
362         *bufptr++ = '"';
363         for (; bufptr < buf + len - 1; dataptr++) {
364                 if (*dataptr == '\\') {
365                         *bufptr++ = '\\';
366                         *bufptr++ = '\\';
367                 } else if (*dataptr == '"') {
368                         *bufptr++ = '\\';
369                         *bufptr++ = '"';
370                 } else if (*dataptr == '\0') {
371                         break;
372                 } else {
373                         *bufptr++ = *dataptr;
374                 }
375         }
376         *bufptr++ = '"';
377         *bufptr = '\0';
378         return 0;
379 }
380
381 static struct ast_custom_function quote_function = {
382         .name = "QUOTE",
383         .synopsis = "Quotes a given string, escaping embedded quotes as necessary",
384         .syntax = "QUOTE(<string>)",
385         .read = quote,
386 };
387
388
389 static int len(struct ast_channel *chan, char *cmd, char *data, char *buf,
390                size_t len)
391 {
392         int length = 0;
393
394         if (data)
395                 length = strlen(data);
396
397         snprintf(buf, len, "%d", length);
398
399         return 0;
400 }
401
402 static struct ast_custom_function len_function = {
403         .name = "LEN",
404         .synopsis = "Returns the length of the argument given",
405         .syntax = "LEN(<string>)",
406         .read = len,
407 };
408
409 static int acf_strftime(struct ast_channel *chan, char *cmd, char *parse,
410                         char *buf, size_t len)
411 {
412         AST_DECLARE_APP_ARGS(args,
413                              AST_APP_ARG(epoch);
414                              AST_APP_ARG(timezone);
415                              AST_APP_ARG(format);
416         );
417         time_t epochi;
418         struct tm tm;
419
420         buf[0] = '\0';
421
422         if (ast_strlen_zero(parse)) {
423                 ast_log(LOG_ERROR,
424                                 "Asterisk function STRFTIME() requires an argument.\n");
425                 return -1;
426         }
427
428         AST_STANDARD_APP_ARGS(args, parse);
429
430         ast_get_time_t(args.epoch, &epochi, time(NULL), NULL);
431         ast_localtime(&epochi, &tm, args.timezone);
432
433         if (!args.format)
434                 args.format = "%c";
435
436         if (!strftime(buf, len, args.format, &tm))
437                 ast_log(LOG_WARNING, "C function strftime() output nothing?!!\n");
438
439         buf[len - 1] = '\0';
440
441         return 0;
442 }
443
444 static struct ast_custom_function strftime_function = {
445         .name = "STRFTIME",
446         .synopsis = "Returns the current date/time in a specified format.",
447         .syntax = "STRFTIME([<epoch>][|[timezone][|format]])",
448         .read = acf_strftime,
449 };
450
451 static int acf_strptime(struct ast_channel *chan, char *cmd, char *data,
452                         char *buf, size_t len)
453 {
454         AST_DECLARE_APP_ARGS(args,
455                              AST_APP_ARG(timestring);
456                              AST_APP_ARG(timezone);
457                              AST_APP_ARG(format);
458         );
459         struct tm time;
460
461         memset(&time, 0, sizeof(struct tm));
462
463         buf[0] = '\0';
464
465         if (!data) {
466                 ast_log(LOG_ERROR,
467                                 "Asterisk function STRPTIME() requires an argument.\n");
468                 return -1;
469         }
470
471         AST_STANDARD_APP_ARGS(args, data);
472
473         if (ast_strlen_zero(args.format)) {
474                 ast_log(LOG_ERROR,
475                                 "No format supplied to STRPTIME(<timestring>|<timezone>|<format>)");
476                 return -1;
477         }
478
479         if (!strptime(args.timestring, args.format, &time)) {
480                 ast_log(LOG_WARNING, "C function strptime() output nothing?!!\n");
481         } else {
482                 snprintf(buf, len, "%d", (int) ast_mktime(&time, args.timezone));
483         }
484
485         return 0;
486 }
487
488 static struct ast_custom_function strptime_function = {
489         .name = "STRPTIME",
490         .synopsis =
491                 "Returns the epoch of the arbitrary date/time string structured as described in the format.",
492         .syntax = "STRPTIME(<datetime>|<timezone>|<format>)",
493         .desc =
494                 "This is useful for converting a date into an EPOCH time, possibly to pass to\n"
495                 "an application like SayUnixTime or to calculate the difference between two\n"
496                 "date strings.\n"
497                 "\n"
498                 "Example:\n"
499                 "  ${STRPTIME(2006-03-01 07:30:35|America/Chicago|%Y-%m-%d %H:%M:%S)} returns 1141219835\n",
500         .read = acf_strptime,
501 };
502
503 static int function_eval(struct ast_channel *chan, char *cmd, char *data,
504                          char *buf, size_t len)
505 {
506         buf[0] = '\0';
507
508         if (ast_strlen_zero(data)) {
509                 ast_log(LOG_WARNING, "EVAL requires an argument: EVAL(<string>)\n");
510                 return -1;
511         }
512
513         pbx_substitute_variables_helper(chan, data, buf, len - 1);
514
515         return 0;
516 }
517
518 static struct ast_custom_function eval_function = {
519         .name = "EVAL",
520         .synopsis = "Evaluate stored variables.",
521         .syntax = "EVAL(<variable>)",
522         .desc = "Using EVAL basically causes a string to be evaluated twice.\n"
523                 "When a variable or expression is in the dialplan, it will be\n"
524                 "evaluated at runtime. However, if the result of the evaluation\n"
525                 "is in fact a variable or expression, using EVAL will have it\n"
526                 "evaluated a second time. For example, if the variable ${MYVAR}\n"
527                 "contains \"${OTHERVAR}\", then the result of putting ${EVAL(${MYVAR})}\n"
528                 "in the dialplan will be the contents of the variable, OTHERVAR.\n"
529                 "Normally, by just putting ${MYVAR} in the dialplan, you would be\n"
530                 "left with \"${OTHERVAR}\".\n",
531         .read = function_eval,
532 };
533
534 static int keypadhash(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
535 {
536         char *bufptr, *dataptr;
537
538         for (bufptr = buf, dataptr = data; bufptr < buf + len - 1; dataptr++) {
539                 if (*dataptr == '1') {
540                         *bufptr++ = '1';
541                 } else if (strchr("AaBbCc2", *dataptr)) {
542                         *bufptr++ = '2';
543                 } else if (strchr("DdEeFf3", *dataptr)) {
544                         *bufptr++ = '3';
545                 } else if (strchr("GgHhIi4", *dataptr)) {
546                         *bufptr++ = '4';
547                 } else if (strchr("JjKkLl5", *dataptr)) {
548                         *bufptr++ = '5';
549                 } else if (strchr("MmNnOo6", *dataptr)) {
550                         *bufptr++ = '6';
551                 } else if (strchr("PpQqRrSs7", *dataptr)) {
552                         *bufptr++ = '7';
553                 } else if (strchr("TtUuVv8", *dataptr)) {
554                         *bufptr++ = '8';
555                 } else if (strchr("WwXxYyZz9", *dataptr)) {
556                         *bufptr++ = '9';
557                 } else if (*dataptr == '0') {
558                         *bufptr++ = '0';
559                 } else if (*dataptr == '\0') {
560                         *bufptr++ = '\0';
561                         break;
562                 }
563         }
564         buf[len - 1] = '\0';
565
566         return 0;
567 }
568
569 static struct ast_custom_function keypadhash_function = {
570         .name = "KEYPADHASH",
571         .synopsis = "Hash the letters in the string into the equivalent keypad numbers.",
572         .syntax = "KEYPADHASH(<string>)",
573         .read = keypadhash,
574         .desc = "Example:  ${KEYPADHASH(Les)} returns \"537\"\n",
575 };
576
577 static int unload_module(void)
578 {
579         int res = 0;
580
581         res |= ast_custom_function_unregister(&fieldqty_function);
582         res |= ast_custom_function_unregister(&filter_function);
583         res |= ast_custom_function_unregister(&regex_function);
584         res |= ast_custom_function_unregister(&array_function);
585         res |= ast_custom_function_unregister(&quote_function);
586         res |= ast_custom_function_unregister(&len_function);
587         res |= ast_custom_function_unregister(&strftime_function);
588         res |= ast_custom_function_unregister(&strptime_function);
589         res |= ast_custom_function_unregister(&eval_function);
590         res |= ast_custom_function_unregister(&keypadhash_function);
591         res |= ast_custom_function_unregister(&sprintf_function);
592
593         return res;
594 }
595
596 static int load_module(void)
597 {
598         int res = 0;
599
600         res |= ast_custom_function_register(&fieldqty_function);
601         res |= ast_custom_function_register(&filter_function);
602         res |= ast_custom_function_register(&regex_function);
603         res |= ast_custom_function_register(&array_function);
604         res |= ast_custom_function_register(&quote_function);
605         res |= ast_custom_function_register(&len_function);
606         res |= ast_custom_function_register(&strftime_function);
607         res |= ast_custom_function_register(&strptime_function);
608         res |= ast_custom_function_register(&eval_function);
609         res |= ast_custom_function_register(&keypadhash_function);
610         res |= ast_custom_function_register(&sprintf_function);
611
612         return res;
613 }
614
615 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "String handling dialplan functions");