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