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