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