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