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