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