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