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