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