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