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