Continue merging in changes from resolve-shadow-warnings. funcs/ this time.
[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  * \ingroup functions
26  */
27
28 #include "asterisk.h"
29
30 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
31
32 #include <regex.h>
33 #include <ctype.h>
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 #include "asterisk/localtime.h"
41
42 static int function_fieldqty(struct ast_channel *chan, const char *cmd,
43                              char *parse, char *buf, size_t len)
44 {
45         char *varsubst, varval[8192], *varval2 = varval;
46         int fieldcount = 0;
47         AST_DECLARE_APP_ARGS(args,
48                              AST_APP_ARG(varname);
49                              AST_APP_ARG(delim);
50                 );
51         char delim[2] = "";
52         size_t delim_used;
53
54         if (chan)
55                 ast_autoservice_start(chan);
56
57         AST_STANDARD_APP_ARGS(args, parse);
58         if (args.delim) {
59                 ast_get_encoded_char(args.delim, delim, &delim_used);
60
61                 varsubst = alloca(strlen(args.varname) + 4);
62
63                 sprintf(varsubst, "${%s}", args.varname);
64                 pbx_substitute_variables_helper(chan, varsubst, varval, sizeof(varval) - 1);
65                 if (ast_strlen_zero(varval2))
66                         fieldcount = 0;
67                 else {
68                         while (strsep(&varval2, delim))
69                                 fieldcount++;
70                 }
71         } else {
72                 fieldcount = 1;
73         }
74         snprintf(buf, len, "%d", fieldcount);
75
76         if (chan)
77                 ast_autoservice_stop(chan);
78
79         return 0;
80 }
81
82 static struct ast_custom_function fieldqty_function = {
83         .name = "FIELDQTY",
84         .synopsis = "Count the fields, with an arbitrary delimiter",
85         .syntax = "FIELDQTY(<varname>,<delim>)",
86         .read = function_fieldqty,
87 };
88
89 static int filter(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
90                   size_t len)
91 {
92         AST_DECLARE_APP_ARGS(args,
93                              AST_APP_ARG(allowed);
94                              AST_APP_ARG(string);
95         );
96         char *outbuf = buf, ac;
97         char allowed[256] = "";
98         size_t allowedlen = 0;
99
100         AST_STANDARD_APP_ARGS(args, parse);
101
102         if (!args.string) {
103                 ast_log(LOG_ERROR, "Usage: FILTER(<allowed-chars>,<string>)\n");
104                 return -1;
105         }
106
107         /* Expand ranges */
108         for (; *(args.allowed) && allowedlen < sizeof(allowed); (args.allowed)++) {
109                 char c1 = 0, c2 = 0;
110                 size_t consumed = 0;
111
112                 if (ast_get_encoded_char(args.allowed, &c1, &consumed))
113                         return -1;
114                 args.allowed += consumed;
115
116                 if (*(args.allowed) == '-') {
117                         if (ast_get_encoded_char(args.allowed + 1, &c2, &consumed))
118                                 c2 = -1;
119                         args.allowed += consumed + 1;
120
121                         /*!\note
122                          * Looks a little strange, until you realize that we can overflow
123                          * the size of a char.
124                          */
125                         for (ac = c1; ac != c2 && allowedlen < sizeof(allowed) - 1; ac++)
126                                 allowed[allowedlen++] = ac;
127                         allowed[allowedlen++] = ac;
128
129                         ast_debug(4, "c1=%d, c2=%d\n", c1, c2);
130
131                         /* Decrement before the loop increment */
132                         (args.allowed)--;
133                 } else
134                         allowed[allowedlen++] = c1;
135         }
136
137         ast_debug(1, "Allowed: %s\n", allowed);
138
139         for (; *(args.string) && (buf + len - 1 > outbuf); (args.string)++) {
140                 if (strchr(allowed, *(args.string)))
141                         *outbuf++ = *(args.string);
142         }
143         *outbuf = '\0';
144
145         return 0;
146 }
147
148 static struct ast_custom_function filter_function = {
149         .name = "FILTER",
150         .synopsis = "Filter the string to include only the allowed characters",
151         .syntax = "FILTER(<allowed-chars>,<string>)",
152         .read = filter,
153         .desc =
154 "Permits all characters listed in <allowed-chars>, filtering all others out.\n"
155 "In addition to literally listing the characters, you may also use ranges of\n"
156 "characters (delimited by a '-'), as well as hexadecimal characters started\n"
157 "with a \\x (i.e. \\x20) and octal characters started with \\0 (i.e. \\040).\n"
158 "Also, \\t, \\n, and \\r are recognized.  If you want a literal '-' character,\n"
159 "simply prefix it with a '\\'\n",
160 };
161
162 static int regex(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
163                  size_t len)
164 {
165         AST_DECLARE_APP_ARGS(args,
166                              AST_APP_ARG(null);
167                              AST_APP_ARG(reg);
168                              AST_APP_ARG(str);
169         );
170         int errcode;
171         regex_t regexbuf;
172
173         buf[0] = '\0';
174
175         AST_NONSTANDARD_APP_ARGS(args, parse, '"');
176
177         if (args.argc != 3) {
178                 ast_log(LOG_ERROR, "Unexpected arguments: should have been in the form '\"<regex>\" <string>'\n");
179                 return -1;
180         }
181         if ((*args.str == ' ') || (*args.str == '\t'))
182                 args.str++;
183
184         ast_debug(1, "FUNCTION REGEX (%s)(%s)\n", args.reg, args.str);
185
186         if ((errcode = regcomp(&regexbuf, args.reg, REG_EXTENDED | REG_NOSUB))) {
187                 regerror(errcode, &regexbuf, buf, len);
188                 ast_log(LOG_WARNING, "Malformed input %s(%s): %s\n", cmd, parse, buf);
189                 return -1;
190         }
191         
192         strcpy(buf, regexec(&regexbuf, args.str, 0, NULL, 0) ? "0" : "1");
193
194         regfree(&regexbuf);
195
196         return 0;
197 }
198
199 static struct ast_custom_function regex_function = {
200         .name = "REGEX",
201         .synopsis = "Regular Expression",
202         .desc =  
203                 "Returns 1 if data matches regular expression, or 0 otherwise.\n"
204                 "Please note that the space following the double quotes separating the regex from the data\n"
205                 "is optional and if present, is skipped. If a space is desired at the beginning of the data,\n"
206                 "then put two spaces there; the second will not be skipped.\n",
207         .syntax = "REGEX(\"<regular expression>\" <data>)",
208         .read = regex,
209 };
210
211 #define HASH_PREFIX     "~HASH~%s~"
212 #define HASH_FORMAT     HASH_PREFIX "%s~"
213
214 static char *app_clearhash = "ClearHash";
215 static char *syn_clearhash = "Clear the keys from a specified hashname";
216 static char *desc_clearhash =
217 "ClearHash(<hashname>)\n"
218 "  Clears all keys out of the specified hashname\n";
219
220 /* This function probably should migrate to main/pbx.c, as pbx_builtin_clearvar_prefix() */
221 static void clearvar_prefix(struct ast_channel *chan, const char *prefix)
222 {
223         struct ast_var_t *var;
224         int len = strlen(prefix);
225         AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->varshead, var, entries) {
226                 if (strncasecmp(prefix, ast_var_name(var), len) == 0) {
227                         AST_LIST_REMOVE_CURRENT(entries);
228                         ast_free(var);
229                 }
230         }
231         AST_LIST_TRAVERSE_SAFE_END
232 }
233
234 static int exec_clearhash(struct ast_channel *chan, void *data)
235 {
236         char prefix[80];
237         snprintf(prefix, sizeof(prefix), HASH_PREFIX, data ? (char *)data : "null");
238         clearvar_prefix(chan, prefix);
239         return 0;
240 }
241
242 static int array(struct ast_channel *chan, const char *cmd, char *var,
243                  const char *value)
244 {
245         AST_DECLARE_APP_ARGS(arg1,
246                              AST_APP_ARG(var)[100];
247         );
248         AST_DECLARE_APP_ARGS(arg2,
249                              AST_APP_ARG(val)[100];
250         );
251         char *origvar = "", *value2, varname[256];
252         int i, ishash = 0;
253
254         value2 = ast_strdupa(value);
255         if (!var || !value2)
256                 return -1;
257
258         if (chan)
259                 ast_autoservice_start(chan);
260
261         if (!strcmp(cmd, "HASH")) {
262                 const char *var2 = pbx_builtin_getvar_helper(chan, "~ODBCFIELDS~");
263                 origvar = var;
264                 if (var2)
265                         var = ast_strdupa(var2);
266                 else {
267                         if (chan)
268                                 ast_autoservice_stop(chan);
269                         return -1;
270                 }
271                 ishash = 1;
272         }
273
274         /* The functions this will generally be used with are SORT and ODBC_*, which
275          * both return comma-delimited lists.  However, if somebody uses literal lists,
276          * their commas will be translated to vertical bars by the load, and I don't
277          * want them to be surprised by the result.  Hence, we prefer commas as the
278          * delimiter, but we'll fall back to vertical bars if commas aren't found.
279          */
280         ast_debug(1, "array (%s=%s)\n", var, value2);
281         AST_STANDARD_APP_ARGS(arg1, var);
282
283         AST_STANDARD_APP_ARGS(arg2, value2);
284
285         for (i = 0; i < arg1.argc; i++) {
286                 ast_debug(1, "array set value (%s=%s)\n", arg1.var[i],
287                                 arg2.val[i]);
288                 if (i < arg2.argc) {
289                         if (ishash) {
290                                 snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
291                                 pbx_builtin_setvar_helper(chan, varname, arg2.val[i]);
292                         } else {
293                                 pbx_builtin_setvar_helper(chan, arg1.var[i], arg2.val[i]);
294                         }
295                 } else {
296                         /* We could unset the variable, by passing a NULL, but due to
297                          * pushvar semantics, that could create some undesired behavior. */
298                         if (ishash) {
299                                 snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
300                                 pbx_builtin_setvar_helper(chan, varname, "");
301                         } else {
302                                 pbx_builtin_setvar_helper(chan, arg1.var[i], "");
303                         }
304                 }
305         }
306
307         if (chan)
308                 ast_autoservice_stop(chan);
309
310         return 0;
311 }
312
313 static int hashkeys_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
314 {
315         struct ast_var_t *newvar;
316         int plen;
317         char prefix[80];
318         snprintf(prefix, sizeof(prefix), HASH_PREFIX, data);
319         plen = strlen(prefix);
320
321         memset(buf, 0, len);
322         AST_LIST_TRAVERSE(&chan->varshead, newvar, entries) {
323                 if (strncasecmp(prefix, ast_var_name(newvar), plen) == 0) {
324                         /* Copy everything after the prefix */
325                         strncat(buf, ast_var_name(newvar) + plen, len - strlen(buf) - 1);
326                         /* Trim the trailing ~ */
327                         buf[strlen(buf) - 1] = ',';
328                 }
329         }
330         /* Trim the trailing comma */
331         buf[strlen(buf) - 1] = '\0';
332         return 0;
333 }
334
335 static int hash_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
336 {
337         char varname[256];
338         AST_DECLARE_APP_ARGS(arg,
339                 AST_APP_ARG(hashname);
340                 AST_APP_ARG(hashkey);
341         );
342
343         if (!strchr(var, ',')) {
344                 /* Single argument version */
345                 return array(chan, "HASH", var, value);
346         }
347
348         AST_STANDARD_APP_ARGS(arg, var);
349         snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
350         pbx_builtin_setvar_helper(chan, varname, value);
351
352         return 0;
353 }
354
355 static int hash_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
356 {
357         char varname[256];
358         const char *varvalue;
359         AST_DECLARE_APP_ARGS(arg,
360                 AST_APP_ARG(hashname);
361                 AST_APP_ARG(hashkey);
362         );
363
364         AST_STANDARD_APP_ARGS(arg, data);
365         if (arg.argc == 2) {
366                 snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
367                 varvalue = pbx_builtin_getvar_helper(chan, varname);
368                 if (varvalue)
369                         ast_copy_string(buf, varvalue, len);
370                 else
371                         *buf = '\0';
372         } else if (arg.argc == 1) {
373                 char colnames[4096];
374                 int i;
375                 AST_DECLARE_APP_ARGS(arg2,
376                         AST_APP_ARG(col)[100];
377                 );
378
379                 /* Get column names, in no particular order */
380                 hashkeys_read(chan, "HASHKEYS", arg.hashname, colnames, sizeof(colnames));
381                 pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", colnames);
382
383                 AST_STANDARD_APP_ARGS(arg2, colnames);
384                 *buf = '\0';
385
386                 /* Now get the corresponding column values, in exactly the same order */
387                 for (i = 0; i < arg2.argc; i++) {
388                         snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg2.col[i]);
389                         varvalue = pbx_builtin_getvar_helper(chan, varname);
390                         strncat(buf, varvalue, len - strlen(buf) - 1);
391                         strncat(buf, ",", len - strlen(buf) - 1);
392                 }
393
394                 /* Strip trailing comma */
395                 buf[strlen(buf) - 1] = '\0';
396         }
397
398         return 0;
399 }
400
401 static struct ast_custom_function hash_function = {
402         .name = "HASH",
403         .synopsis = "Implementation of a dialplan associative array",
404         .syntax = "HASH(hashname[,hashkey])",
405         .write = hash_write,
406         .read = hash_read,
407         .desc =
408                 "In two argument mode, gets and sets values to corresponding keys within a named\n"
409                 "associative array.  The single-argument mode will only work when assigned to from\n"
410                 "a function defined by func_odbc.so.\n",
411 };
412
413 static struct ast_custom_function hashkeys_function = {
414         .name = "HASHKEYS",
415         .synopsis = "Retrieve the keys of a HASH()",
416         .syntax = "HASHKEYS(<hashname>)",
417         .read = hashkeys_read,
418         .desc =
419                 "Returns a comma-delimited list of the current keys of an associative array\n"
420                 "defined by the HASH() function.  Note that if you iterate over the keys of\n"
421                 "the result, adding keys during iteration will cause the result of the HASHKEYS\n"
422                 "function to change.\n",
423 };
424
425 static struct ast_custom_function array_function = {
426         .name = "ARRAY",
427         .synopsis = "Allows setting multiple variables at once",
428         .syntax = "ARRAY(var1[,var2[...][,varN]])",
429         .write = array,
430         .desc =
431                 "The comma-separated list passed as a value to which the function is set will\n"
432                 "be interpreted as a set of values to which the comma-separated list of\n"
433                 "variable names in the argument should be set.\n"
434                 "Hence, Set(ARRAY(var1,var2)=1,2) will set var1 to 1 and var2 to 2.\n",
435 };
436
437 static int acf_sprintf(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
438 {
439 #define SPRINTF_FLAG    0
440 #define SPRINTF_WIDTH   1
441 #define SPRINTF_PRECISION       2
442 #define SPRINTF_LENGTH  3
443 #define SPRINTF_CONVERSION      4
444         int i, state = -1, argcount = 0;
445         char *formatstart = NULL, *bufptr = buf;
446         char formatbuf[256] = "";
447         int tmpi;
448         double tmpd;
449         AST_DECLARE_APP_ARGS(arg,
450                                 AST_APP_ARG(format);
451                                 AST_APP_ARG(var)[100];
452         );
453
454         AST_STANDARD_APP_ARGS(arg, data);
455
456         /* Scan the format, converting each argument into the requisite format type. */
457         for (i = 0; arg.format[i]; i++) {
458                 switch (state) {
459                 case SPRINTF_FLAG:
460                         if (strchr("#0- +'I", arg.format[i]))
461                                 break;
462                         state = SPRINTF_WIDTH;
463                 case SPRINTF_WIDTH:
464                         if (arg.format[i] >= '0' && arg.format[i] <= '9')
465                                 break;
466
467                         /* Next character must be a period to go into a precision */
468                         if (arg.format[i] == '.') {
469                                 state = SPRINTF_PRECISION;
470                         } else {
471                                 state = SPRINTF_LENGTH;
472                                 i--;
473                         }
474                         break;
475                 case SPRINTF_PRECISION:
476                         if (arg.format[i] >= '0' && arg.format[i] <= '9')
477                                 break;
478                         state = SPRINTF_LENGTH;
479                 case SPRINTF_LENGTH:
480                         if (strchr("hl", arg.format[i])) {
481                                 if (arg.format[i + 1] == arg.format[i])
482                                         i++;
483                                 state = SPRINTF_CONVERSION;
484                                 break;
485                         } else if (strchr("Lqjzt", arg.format[i])) {
486                                 state = SPRINTF_CONVERSION;
487                                 break;
488                         }
489                         state = SPRINTF_CONVERSION;
490                 case SPRINTF_CONVERSION:
491                         if (strchr("diouxXc", arg.format[i])) {
492                                 /* Integer */
493
494                                 /* Isolate this format alone */
495                                 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
496                                 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
497
498                                 /* Convert the argument into the required type */
499                                 if (sscanf(arg.var[argcount++], "%d", &tmpi) != 1) {
500                                         ast_log(LOG_ERROR, "Argument '%s' is not an integer number for format '%s'\n", arg.var[argcount - 1], formatbuf);
501                                         goto sprintf_fail;
502                                 }
503
504                                 /* Format the argument */
505                                 snprintf(bufptr, buf + len - bufptr, formatbuf, tmpi);
506
507                                 /* Update the position of the next parameter to print */
508                                 bufptr = strchr(buf, '\0');
509                         } else if (strchr("eEfFgGaA", arg.format[i])) {
510                                 /* Double */
511
512                                 /* Isolate this format alone */
513                                 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
514                                 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
515
516                                 /* Convert the argument into the required type */
517                                 if (sscanf(arg.var[argcount++], "%lf", &tmpd) != 1) {
518                                         ast_log(LOG_ERROR, "Argument '%s' is not a floating point number for format '%s'\n", arg.var[argcount - 1], formatbuf);
519                                         goto sprintf_fail;
520                                 }
521
522                                 /* Format the argument */
523                                 snprintf(bufptr, buf + len - bufptr, formatbuf, tmpd);
524
525                                 /* Update the position of the next parameter to print */
526                                 bufptr = strchr(buf, '\0');
527                         } else if (arg.format[i] == 's') {
528                                 /* String */
529
530                                 /* Isolate this format alone */
531                                 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
532                                 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
533
534                                 /* Format the argument */
535                                 snprintf(bufptr, buf + len - bufptr, formatbuf, arg.var[argcount++]);
536
537                                 /* Update the position of the next parameter to print */
538                                 bufptr = strchr(buf, '\0');
539                         } else if (arg.format[i] == '%') {
540                                 /* Literal data to copy */
541                                 *bufptr++ = arg.format[i];
542                         } else {
543                                 /* Not supported */
544
545                                 /* Isolate this format alone */
546                                 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
547                                 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
548
549                                 ast_log(LOG_ERROR, "Format type not supported: '%s' with argument '%s'\n", formatbuf, arg.var[argcount++]);
550                                 goto sprintf_fail;
551                         }
552                         state = -1;
553                         break;
554                 default:
555                         if (arg.format[i] == '%') {
556                                 state = SPRINTF_FLAG;
557                                 formatstart = &arg.format[i];
558                                 break;
559                         } else {
560                                 /* Literal data to copy */
561                                 *bufptr++ = arg.format[i];
562                         }
563                 }
564         }
565         return 0;
566 sprintf_fail:
567         return -1;
568 }
569
570 static struct ast_custom_function sprintf_function = {
571         .name = "SPRINTF",
572         .synopsis = "Format a variable according to a format string",
573         .syntax = "SPRINTF(<format>,<arg1>[,...<argN>])",
574         .read = acf_sprintf,
575         .desc =
576 "Parses the format string specified and returns a string matching that format.\n"
577 "Supports most options supported by sprintf(3).  Returns a shortened string if\n"
578 "a format specifier is not recognized.\n",
579 };
580
581 static int quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
582 {
583         char *bufptr = buf, *dataptr = data;
584         *bufptr++ = '"';
585         for (; bufptr < buf + len - 1; dataptr++) {
586                 if (*dataptr == '\\') {
587                         *bufptr++ = '\\';
588                         *bufptr++ = '\\';
589                 } else if (*dataptr == '"') {
590                         *bufptr++ = '\\';
591                         *bufptr++ = '"';
592                 } else if (*dataptr == '\0') {
593                         break;
594                 } else {
595                         *bufptr++ = *dataptr;
596                 }
597         }
598         *bufptr++ = '"';
599         *bufptr = '\0';
600         return 0;
601 }
602
603 static struct ast_custom_function quote_function = {
604         .name = "QUOTE",
605         .synopsis = "Quotes a given string, escaping embedded quotes as necessary",
606         .syntax = "QUOTE(<string>)",
607         .read = quote,
608 };
609
610
611 static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf,
612                size_t buflen)
613 {
614         int length = 0;
615
616         if (data)
617                 length = strlen(data);
618
619         snprintf(buf, buflen, "%d", length);
620
621         return 0;
622 }
623
624 static struct ast_custom_function len_function = {
625         .name = "LEN",
626         .synopsis = "Returns the length of the argument given",
627         .syntax = "LEN(<string>)",
628         .read = len,
629 };
630
631 static int acf_strftime(struct ast_channel *chan, const char *cmd, char *parse,
632                         char *buf, size_t buflen)
633 {
634         AST_DECLARE_APP_ARGS(args,
635                              AST_APP_ARG(epoch);
636                              AST_APP_ARG(timezone);
637                              AST_APP_ARG(format);
638         );
639         struct timeval when;
640         struct ast_tm tm;
641
642         buf[0] = '\0';
643
644         AST_STANDARD_APP_ARGS(args, parse);
645
646         ast_get_timeval(args.epoch, &when, ast_tvnow(), NULL);
647         ast_localtime(&when, &tm, args.timezone);
648
649         if (!args.format)
650                 args.format = "%c";
651
652         if (ast_strftime(buf, buflen, args.format, &tm) <= 0)
653                 ast_log(LOG_WARNING, "C function strftime() output nothing?!!\n");
654
655         buf[buflen - 1] = '\0';
656
657         return 0;
658 }
659
660 static struct ast_custom_function strftime_function = {
661         .name = "STRFTIME",
662         .synopsis = "Returns the current date/time in a specified format.",
663         .syntax = "STRFTIME([<epoch>][,[timezone][,format]])",
664         .desc =
665 "STRFTIME sports all of the same formats as the underlying C function\n"
666 "strftime(3) - see the man page for details.  It also supports the\n"
667 "following format:\n"
668 " %[n]q - fractions of a second, with leading zeroes.  For example, %3q will\n"
669 "         give milliseconds and %1q will give tenths of a second.  The default\n"
670 "         is to output milliseconds (n=3).  The common case is to use it in\n"
671 "         combination with %S, as in \"%S.%3q\".\n",
672         .read = acf_strftime,
673 };
674
675 static int acf_strptime(struct ast_channel *chan, const char *cmd, char *data,
676                         char *buf, size_t buflen)
677 {
678         AST_DECLARE_APP_ARGS(args,
679                              AST_APP_ARG(timestring);
680                              AST_APP_ARG(timezone);
681                              AST_APP_ARG(format);
682         );
683         union {
684                 struct ast_tm atm;
685                 struct tm time;
686         } t = { { 0, }, };
687
688         buf[0] = '\0';
689
690         if (!data) {
691                 ast_log(LOG_ERROR,
692                                 "Asterisk function STRPTIME() requires an argument.\n");
693                 return -1;
694         }
695
696         AST_STANDARD_APP_ARGS(args, data);
697
698         if (ast_strlen_zero(args.format)) {
699                 ast_log(LOG_ERROR,
700                                 "No format supplied to STRPTIME(<timestring>,<timezone>,<format>)");
701                 return -1;
702         }
703
704         if (!strptime(args.timestring, args.format, &t.time)) {
705                 ast_log(LOG_WARNING, "C function strptime() output nothing?!!\n");
706         } else {
707                 struct timeval when;
708                 /* Since strptime(3) does not check DST, force ast_mktime() to calculate it. */
709                 t.atm.tm_isdst = -1;
710                 when = ast_mktime(&t.atm, args.timezone);
711                 snprintf(buf, buflen, "%d", (int) when.tv_sec);
712         }
713
714         return 0;
715 }
716
717 static struct ast_custom_function strptime_function = {
718         .name = "STRPTIME",
719         .synopsis =
720                 "Returns the epoch of the arbitrary date/time string structured as described in the format.",
721         .syntax = "STRPTIME(<datetime>,<timezone>,<format>)",
722         .desc =
723                 "This is useful for converting a date into an EPOCH time, possibly to pass to\n"
724                 "an application like SayUnixTime or to calculate the difference between two\n"
725                 "date strings.\n"
726                 "\n"
727                 "Example:\n"
728                 "  ${STRPTIME(2006-03-01 07:30:35,America/Chicago,%Y-%m-%d %H:%M:%S)} returns 1141219835\n",
729         .read = acf_strptime,
730 };
731
732 static int function_eval(struct ast_channel *chan, const char *cmd, char *data,
733                          char *buf, size_t buflen)
734 {
735         if (ast_strlen_zero(data)) {
736                 ast_log(LOG_WARNING, "EVAL requires an argument: EVAL(<string>)\n");
737                 return -1;
738         }
739
740         if (chan)
741                 ast_autoservice_start(chan);
742         pbx_substitute_variables_helper(chan, data, buf, buflen - 1);
743         if (chan)
744                 ast_autoservice_stop(chan);
745
746         return 0;
747 }
748
749 static struct ast_custom_function eval_function = {
750         .name = "EVAL",
751         .synopsis = "Evaluate stored variables.",
752         .syntax = "EVAL(<variable>)",
753         .desc = "Using EVAL basically causes a string to be evaluated twice.\n"
754                 "When a variable or expression is in the dialplan, it will be\n"
755                 "evaluated at runtime. However, if the result of the evaluation\n"
756                 "is in fact a variable or expression, using EVAL will have it\n"
757                 "evaluated a second time. For example, if the variable ${MYVAR}\n"
758                 "contains \"${OTHERVAR}\", then the result of putting ${EVAL(${MYVAR})}\n"
759                 "in the dialplan will be the contents of the variable, OTHERVAR.\n"
760                 "Normally, by just putting ${MYVAR} in the dialplan, you would be\n"
761                 "left with \"${OTHERVAR}\".\n",
762         .read = function_eval,
763 };
764
765 static int keypadhash(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
766 {
767         char *bufptr, *dataptr;
768
769         for (bufptr = buf, dataptr = data; bufptr < buf + buflen - 1; dataptr++) {
770                 if (*dataptr == '1') {
771                         *bufptr++ = '1';
772                 } else if (strchr("AaBbCc2", *dataptr)) {
773                         *bufptr++ = '2';
774                 } else if (strchr("DdEeFf3", *dataptr)) {
775                         *bufptr++ = '3';
776                 } else if (strchr("GgHhIi4", *dataptr)) {
777                         *bufptr++ = '4';
778                 } else if (strchr("JjKkLl5", *dataptr)) {
779                         *bufptr++ = '5';
780                 } else if (strchr("MmNnOo6", *dataptr)) {
781                         *bufptr++ = '6';
782                 } else if (strchr("PpQqRrSs7", *dataptr)) {
783                         *bufptr++ = '7';
784                 } else if (strchr("TtUuVv8", *dataptr)) {
785                         *bufptr++ = '8';
786                 } else if (strchr("WwXxYyZz9", *dataptr)) {
787                         *bufptr++ = '9';
788                 } else if (*dataptr == '0') {
789                         *bufptr++ = '0';
790                 } else if (*dataptr == '\0') {
791                         *bufptr++ = '\0';
792                         break;
793                 }
794         }
795         buf[buflen - 1] = '\0';
796
797         return 0;
798 }
799
800 static struct ast_custom_function keypadhash_function = {
801         .name = "KEYPADHASH",
802         .synopsis = "Hash the letters in the string into the equivalent keypad numbers.",
803         .syntax = "KEYPADHASH(<string>)",
804         .read = keypadhash,
805         .desc = "Example:  ${KEYPADHASH(Les)} returns \"537\"\n",
806 };
807
808 static int string_toupper(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
809 {
810         char *bufptr = buf, *dataptr = data;
811
812         while ((bufptr < buf + buflen - 1) && (*bufptr++ = toupper(*dataptr++)));
813
814         return 0;
815 }
816
817 static struct ast_custom_function toupper_function = {
818         .name = "TOUPPER",
819         .synopsis = "Convert the string to upper case.",
820         .syntax = "TOUPPER(<string>)",
821         .read = string_toupper,
822         .desc = "Example: ${TOUPPER(Example)} returns \"EXAMPLE\"\n",
823 };
824
825 static int string_tolower(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
826 {
827         char *bufptr = buf, *dataptr = data;
828
829         while ((bufptr < buf + buflen - 1) && (*bufptr++ = tolower(*dataptr++)));
830
831         return 0;
832 }
833
834 static struct ast_custom_function tolower_function = {
835         .name = "TOLOWER",
836         .synopsis = "Convert the string to lower case.",
837         .syntax = "TOLOWER(<string>)",
838         .read = string_tolower,
839         .desc = "Example: ${TOLOWER(Example)} returns \"example\"\n",
840 };
841
842 static int unload_module(void)
843 {
844         int res = 0;
845
846         res |= ast_custom_function_unregister(&fieldqty_function);
847         res |= ast_custom_function_unregister(&filter_function);
848         res |= ast_custom_function_unregister(&regex_function);
849         res |= ast_custom_function_unregister(&array_function);
850         res |= ast_custom_function_unregister(&quote_function);
851         res |= ast_custom_function_unregister(&len_function);
852         res |= ast_custom_function_unregister(&strftime_function);
853         res |= ast_custom_function_unregister(&strptime_function);
854         res |= ast_custom_function_unregister(&eval_function);
855         res |= ast_custom_function_unregister(&keypadhash_function);
856         res |= ast_custom_function_unregister(&sprintf_function);
857         res |= ast_custom_function_unregister(&hashkeys_function);
858         res |= ast_custom_function_unregister(&hash_function);
859         res |= ast_unregister_application(app_clearhash);
860         res |= ast_custom_function_unregister(&toupper_function);
861         res |= ast_custom_function_unregister(&tolower_function);
862
863         return res;
864 }
865
866 static int load_module(void)
867 {
868         int res = 0;
869
870         res |= ast_custom_function_register(&fieldqty_function);
871         res |= ast_custom_function_register(&filter_function);
872         res |= ast_custom_function_register(&regex_function);
873         res |= ast_custom_function_register(&array_function);
874         res |= ast_custom_function_register(&quote_function);
875         res |= ast_custom_function_register(&len_function);
876         res |= ast_custom_function_register(&strftime_function);
877         res |= ast_custom_function_register(&strptime_function);
878         res |= ast_custom_function_register(&eval_function);
879         res |= ast_custom_function_register(&keypadhash_function);
880         res |= ast_custom_function_register(&sprintf_function);
881         res |= ast_custom_function_register(&hashkeys_function);
882         res |= ast_custom_function_register(&hash_function);
883         res |= ast_register_application(app_clearhash, exec_clearhash, syn_clearhash, desc_clearhash);
884         res |= ast_custom_function_register(&toupper_function);
885         res |= ast_custom_function_register(&tolower_function);
886
887         return res;
888 }
889
890 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "String handling dialplan functions");