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