cb8a98bea75b61e14faf92fba1bf05a553ef2551
[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         AST_STANDARD_APP_ARGS(args, parse);
289         if (args.delim) {
290                 ast_get_encoded_char(args.delim, delim, &delim_used);
291
292                 varsubst = alloca(strlen(args.varname) + 4);
293
294                 sprintf(varsubst, "${%s}", args.varname);
295                 pbx_substitute_variables_helper(chan, varsubst, varval, sizeof(varval) - 1);
296                 if (ast_strlen_zero(varval2))
297                         fieldcount = 0;
298                 else {
299                         while (strsep(&varval2, delim))
300                                 fieldcount++;
301                 }
302         } else {
303                 fieldcount = 1;
304         }
305         snprintf(buf, len, "%d", fieldcount);
306
307         return 0;
308 }
309
310 static struct ast_custom_function fieldqty_function = {
311         .name = "FIELDQTY",
312         .read = function_fieldqty,
313 };
314
315 static int listfilter(struct ast_channel *chan, const char *cmd, char *parse, char *buf, size_t len)
316 {
317         AST_DECLARE_APP_ARGS(args,
318                 AST_APP_ARG(listname);
319                 AST_APP_ARG(delimiter);
320                 AST_APP_ARG(fieldvalue);
321         );
322         const char *orig_list, *ptr;
323         const char *begin, *cur, *next;
324         int dlen, flen, first = 1;
325         struct ast_str *result = ast_str_thread_get(&result_buf, 16);
326         char *delim;
327
328         AST_STANDARD_APP_ARGS(args, parse);
329
330         if (args.argc < 3) {
331                 ast_log(LOG_ERROR, "Usage: LISTFILTER(<listname>,<delimiter>,<fieldvalue>)\n");
332                 return -1;
333         }
334
335         /* If we don't lock the channel, the variable could disappear out from underneath us. */
336         if (chan) {
337                 ast_channel_lock(chan);
338         }
339         if (!(orig_list = pbx_builtin_getvar_helper(chan, args.listname))) {
340                 ast_log(LOG_ERROR, "List variable '%s' not found\n", args.listname);
341                 if (chan) {
342                         ast_channel_unlock(chan);
343                 }
344                 return -1;
345         }
346
347         /* If the string isn't there, just copy out the string and be done with it. */
348         if (!(ptr = strstr(orig_list, args.fieldvalue))) {
349                 ast_copy_string(buf, orig_list, len);
350                 if (chan) {
351                         ast_channel_unlock(chan);
352                 }
353                 return 0;
354         }
355
356         dlen = strlen(args.delimiter);
357         delim = alloca(dlen + 1);
358         ast_get_encoded_str(args.delimiter, delim, dlen + 1);
359
360         if ((dlen = strlen(delim)) == 0) {
361                 delim = ",";
362                 dlen = 1;
363         }
364
365         flen = strlen(args.fieldvalue);
366
367         ast_str_reset(result);
368         /* Enough space for any result */
369         ast_str_make_space(&result, strlen(orig_list) + 1);
370
371         begin = orig_list;
372         next = strstr(begin, delim);
373
374         do {
375                 /* Find next boundary */
376                 if (next) {
377                         cur = next;
378                         next = strstr(cur + dlen, delim);
379                 } else {
380                         cur = strchr(begin + dlen, '\0');
381                 }
382
383                 if (flen == cur - begin && !strncmp(begin, args.fieldvalue, flen)) {
384                         /* Skip field */
385                         begin += flen + dlen;
386                 } else {
387                         /* Copy field to output */
388                         if (!first) {
389                                 ast_str_append(&result, 0, "%s", delim);
390                         }
391
392                         ast_str_append_substr(&result, 0, begin, cur - begin + 1);
393                         first = 0;
394                         begin = cur + dlen;
395                 }
396         } while (*cur != '\0');
397         if (chan) {
398                 ast_channel_unlock(chan);
399         }
400
401         ast_copy_string(buf, ast_str_buffer(result), len);
402
403         return 0;
404 }
405
406 static struct ast_custom_function listfilter_function = {
407         .name = "LISTFILTER",
408         .read = listfilter,
409 };
410
411 static int filter(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
412                   size_t len)
413 {
414         AST_DECLARE_APP_ARGS(args,
415                              AST_APP_ARG(allowed);
416                              AST_APP_ARG(string);
417         );
418         char *outbuf = buf, ac;
419         char allowed[256] = "";
420         size_t allowedlen = 0;
421
422         AST_STANDARD_APP_ARGS(args, parse);
423
424         if (!args.string) {
425                 ast_log(LOG_ERROR, "Usage: FILTER(<allowed-chars>,<string>)\n");
426                 return -1;
427         }
428
429         /* Expand ranges */
430         for (; *(args.allowed) && allowedlen < sizeof(allowed); ) {
431                 char c1 = 0, c2 = 0;
432                 size_t consumed = 0;
433
434                 if (ast_get_encoded_char(args.allowed, &c1, &consumed))
435                         return -1;
436                 args.allowed += consumed;
437
438                 if (*(args.allowed) == '-') {
439                         if (ast_get_encoded_char(args.allowed + 1, &c2, &consumed))
440                                 c2 = -1;
441                         args.allowed += consumed + 1;
442
443                         /*!\note
444                          * Looks a little strange, until you realize that we can overflow
445                          * the size of a char.
446                          */
447                         for (ac = c1; ac != c2 && allowedlen < sizeof(allowed) - 1; ac++)
448                                 allowed[allowedlen++] = ac;
449                         allowed[allowedlen++] = ac;
450
451                         ast_debug(4, "c1=%d, c2=%d\n", c1, c2);
452
453                         /* Decrement before the loop increment */
454                         (args.allowed)--;
455                 } else
456                         allowed[allowedlen++] = c1;
457         }
458
459         ast_debug(1, "Allowed: %s\n", allowed);
460
461         for (; *(args.string) && (buf + len - 1 > outbuf); (args.string)++) {
462                 if (strchr(allowed, *(args.string)))
463                         *outbuf++ = *(args.string);
464         }
465         *outbuf = '\0';
466
467         return 0;
468 }
469
470 static struct ast_custom_function filter_function = {
471         .name = "FILTER",
472         .read = filter,
473 };
474
475 static int regex(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
476                  size_t len)
477 {
478         AST_DECLARE_APP_ARGS(args,
479                              AST_APP_ARG(null);
480                              AST_APP_ARG(reg);
481                              AST_APP_ARG(str);
482         );
483         int errcode;
484         regex_t regexbuf;
485
486         buf[0] = '\0';
487
488         AST_NONSTANDARD_APP_ARGS(args, parse, '"');
489
490         if (args.argc != 3) {
491                 ast_log(LOG_ERROR, "Unexpected arguments: should have been in the form '\"<regex>\" <string>'\n");
492                 return -1;
493         }
494         if ((*args.str == ' ') || (*args.str == '\t'))
495                 args.str++;
496
497         ast_debug(1, "FUNCTION REGEX (%s)(%s)\n", args.reg, args.str);
498
499         if ((errcode = regcomp(&regexbuf, args.reg, REG_EXTENDED | REG_NOSUB))) {
500                 regerror(errcode, &regexbuf, buf, len);
501                 ast_log(LOG_WARNING, "Malformed input %s(%s): %s\n", cmd, parse, buf);
502                 return -1;
503         }
504         
505         strcpy(buf, regexec(&regexbuf, args.str, 0, NULL, 0) ? "0" : "1");
506
507         regfree(&regexbuf);
508
509         return 0;
510 }
511
512 static struct ast_custom_function regex_function = {
513         .name = "REGEX",
514         .read = regex,
515 };
516
517 #define HASH_PREFIX     "~HASH~%s~"
518 #define HASH_FORMAT     HASH_PREFIX "%s~"
519
520 static char *app_clearhash = "ClearHash";
521
522 /* This function probably should migrate to main/pbx.c, as pbx_builtin_clearvar_prefix() */
523 static void clearvar_prefix(struct ast_channel *chan, const char *prefix)
524 {
525         struct ast_var_t *var;
526         int len = strlen(prefix);
527         AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->varshead, var, entries) {
528                 if (strncasecmp(prefix, ast_var_name(var), len) == 0) {
529                         AST_LIST_REMOVE_CURRENT(entries);
530                         ast_free(var);
531                 }
532         }
533         AST_LIST_TRAVERSE_SAFE_END
534 }
535
536 static int exec_clearhash(struct ast_channel *chan, void *data)
537 {
538         char prefix[80];
539         snprintf(prefix, sizeof(prefix), HASH_PREFIX, data ? (char *)data : "null");
540         clearvar_prefix(chan, prefix);
541         return 0;
542 }
543
544 static int array(struct ast_channel *chan, const char *cmd, char *var,
545                  const char *value)
546 {
547         AST_DECLARE_APP_ARGS(arg1,
548                              AST_APP_ARG(var)[100];
549         );
550         AST_DECLARE_APP_ARGS(arg2,
551                              AST_APP_ARG(val)[100];
552         );
553         char *origvar = "", *value2, varname[256];
554         int i, ishash = 0;
555
556         value2 = ast_strdupa(value);
557         if (!var || !value2)
558                 return -1;
559
560         if (!strcmp(cmd, "HASH")) {
561                 const char *var2 = pbx_builtin_getvar_helper(chan, "~ODBCFIELDS~");
562                 origvar = var;
563                 if (var2)
564                         var = ast_strdupa(var2);
565                 else {
566                         if (chan)
567                                 ast_autoservice_stop(chan);
568                         return -1;
569                 }
570                 ishash = 1;
571         }
572
573         /* The functions this will generally be used with are SORT and ODBC_*, which
574          * both return comma-delimited lists.  However, if somebody uses literal lists,
575          * their commas will be translated to vertical bars by the load, and I don't
576          * want them to be surprised by the result.  Hence, we prefer commas as the
577          * delimiter, but we'll fall back to vertical bars if commas aren't found.
578          */
579         ast_debug(1, "array (%s=%s)\n", var, value2);
580         AST_STANDARD_APP_ARGS(arg1, var);
581
582         AST_STANDARD_APP_ARGS(arg2, value2);
583
584         for (i = 0; i < arg1.argc; i++) {
585                 ast_debug(1, "array set value (%s=%s)\n", arg1.var[i],
586                                 arg2.val[i]);
587                 if (i < arg2.argc) {
588                         if (ishash) {
589                                 snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
590                                 pbx_builtin_setvar_helper(chan, varname, arg2.val[i]);
591                         } else {
592                                 pbx_builtin_setvar_helper(chan, arg1.var[i], arg2.val[i]);
593                         }
594                 } else {
595                         /* We could unset the variable, by passing a NULL, but due to
596                          * pushvar semantics, that could create some undesired behavior. */
597                         if (ishash) {
598                                 snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
599                                 pbx_builtin_setvar_helper(chan, varname, "");
600                         } else {
601                                 pbx_builtin_setvar_helper(chan, arg1.var[i], "");
602                         }
603                 }
604         }
605
606         return 0;
607 }
608
609 static int hashkeys_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
610 {
611         struct ast_var_t *newvar;
612         int plen;
613         char prefix[80];
614         snprintf(prefix, sizeof(prefix), HASH_PREFIX, data);
615         plen = strlen(prefix);
616
617         memset(buf, 0, len);
618         AST_LIST_TRAVERSE(&chan->varshead, newvar, entries) {
619                 if (strncasecmp(prefix, ast_var_name(newvar), plen) == 0) {
620                         /* Copy everything after the prefix */
621                         strncat(buf, ast_var_name(newvar) + plen, len - strlen(buf) - 1);
622                         /* Trim the trailing ~ */
623                         buf[strlen(buf) - 1] = ',';
624                 }
625         }
626         /* Trim the trailing comma */
627         buf[strlen(buf) - 1] = '\0';
628         return 0;
629 }
630
631 static int hash_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
632 {
633         char varname[256];
634         AST_DECLARE_APP_ARGS(arg,
635                 AST_APP_ARG(hashname);
636                 AST_APP_ARG(hashkey);
637         );
638
639         if (!strchr(var, ',')) {
640                 /* Single argument version */
641                 return array(chan, "HASH", var, value);
642         }
643
644         AST_STANDARD_APP_ARGS(arg, var);
645         snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
646         pbx_builtin_setvar_helper(chan, varname, value);
647
648         return 0;
649 }
650
651 static int hash_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
652 {
653         char varname[256];
654         const char *varvalue;
655         AST_DECLARE_APP_ARGS(arg,
656                 AST_APP_ARG(hashname);
657                 AST_APP_ARG(hashkey);
658         );
659
660         AST_STANDARD_APP_ARGS(arg, data);
661         if (arg.argc == 2) {
662                 snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
663                 varvalue = pbx_builtin_getvar_helper(chan, varname);
664                 if (varvalue)
665                         ast_copy_string(buf, varvalue, len);
666                 else
667                         *buf = '\0';
668         } else if (arg.argc == 1) {
669                 char colnames[4096];
670                 int i;
671                 AST_DECLARE_APP_ARGS(arg2,
672                         AST_APP_ARG(col)[100];
673                 );
674
675                 /* Get column names, in no particular order */
676                 hashkeys_read(chan, "HASHKEYS", arg.hashname, colnames, sizeof(colnames));
677                 pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", colnames);
678
679                 AST_STANDARD_APP_ARGS(arg2, colnames);
680                 *buf = '\0';
681
682                 /* Now get the corresponding column values, in exactly the same order */
683                 for (i = 0; i < arg2.argc; i++) {
684                         snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg2.col[i]);
685                         varvalue = pbx_builtin_getvar_helper(chan, varname);
686                         strncat(buf, varvalue, len - strlen(buf) - 1);
687                         strncat(buf, ",", len - strlen(buf) - 1);
688                 }
689
690                 /* Strip trailing comma */
691                 buf[strlen(buf) - 1] = '\0';
692         }
693
694         return 0;
695 }
696
697 static struct ast_custom_function hash_function = {
698         .name = "HASH",
699         .write = hash_write,
700         .read = hash_read,
701 };
702
703 static struct ast_custom_function hashkeys_function = {
704         .name = "HASHKEYS",
705         .read = hashkeys_read,
706 };
707
708 static struct ast_custom_function array_function = {
709         .name = "ARRAY",
710         .write = array,
711 };
712
713 static int quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
714 {
715         char *bufptr = buf, *dataptr = data;
716         *bufptr++ = '"';
717         for (; bufptr < buf + len - 1; dataptr++) {
718                 if (*dataptr == '\\') {
719                         *bufptr++ = '\\';
720                         *bufptr++ = '\\';
721                 } else if (*dataptr == '"') {
722                         *bufptr++ = '\\';
723                         *bufptr++ = '"';
724                 } else if (*dataptr == '\0') {
725                         break;
726                 } else {
727                         *bufptr++ = *dataptr;
728                 }
729         }
730         *bufptr++ = '"';
731         *bufptr = '\0';
732         return 0;
733 }
734
735 static struct ast_custom_function quote_function = {
736         .name = "QUOTE",
737         .read = quote,
738 };
739
740
741 static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf,
742                size_t buflen)
743 {
744         int length = 0;
745
746         if (data)
747                 length = strlen(data);
748
749         snprintf(buf, buflen, "%d", length);
750
751         return 0;
752 }
753
754 static struct ast_custom_function len_function = {
755         .name = "LEN",
756         .read = len,
757 };
758
759 static int acf_strftime(struct ast_channel *chan, const char *cmd, char *parse,
760                         char *buf, size_t buflen)
761 {
762         AST_DECLARE_APP_ARGS(args,
763                              AST_APP_ARG(epoch);
764                              AST_APP_ARG(timezone);
765                              AST_APP_ARG(format);
766         );
767         struct timeval when;
768         struct ast_tm tm;
769
770         buf[0] = '\0';
771
772         AST_STANDARD_APP_ARGS(args, parse);
773
774         ast_get_timeval(args.epoch, &when, ast_tvnow(), NULL);
775         ast_localtime(&when, &tm, args.timezone);
776
777         if (!args.format)
778                 args.format = "%c";
779
780         if (ast_strftime(buf, buflen, args.format, &tm) <= 0)
781                 ast_log(LOG_WARNING, "C function strftime() output nothing?!!\n");
782
783         buf[buflen - 1] = '\0';
784
785         return 0;
786 }
787
788 static struct ast_custom_function strftime_function = {
789         .name = "STRFTIME",
790         .read = acf_strftime,
791 };
792
793 static int acf_strptime(struct ast_channel *chan, const char *cmd, char *data,
794                         char *buf, size_t buflen)
795 {
796         AST_DECLARE_APP_ARGS(args,
797                              AST_APP_ARG(timestring);
798                              AST_APP_ARG(timezone);
799                              AST_APP_ARG(format);
800         );
801         struct ast_tm tm;
802
803         buf[0] = '\0';
804
805         if (!data) {
806                 ast_log(LOG_ERROR,
807                                 "Asterisk function STRPTIME() requires an argument.\n");
808                 return -1;
809         }
810
811         AST_STANDARD_APP_ARGS(args, data);
812
813         if (ast_strlen_zero(args.format)) {
814                 ast_log(LOG_ERROR,
815                                 "No format supplied to STRPTIME(<timestring>,<timezone>,<format>)");
816                 return -1;
817         }
818
819         if (!ast_strptime(args.timestring, args.format, &tm)) {
820                 ast_log(LOG_WARNING, "STRPTIME() found no time specified within the string\n");
821         } else {
822                 struct timeval when;
823                 when = ast_mktime(&tm, args.timezone);
824                 snprintf(buf, buflen, "%d", (int) when.tv_sec);
825         }
826
827         return 0;
828 }
829
830 static struct ast_custom_function strptime_function = {
831         .name = "STRPTIME",
832         .read = acf_strptime,
833 };
834
835 static int function_eval(struct ast_channel *chan, const char *cmd, char *data,
836                          char *buf, size_t buflen)
837 {
838         if (ast_strlen_zero(data)) {
839                 ast_log(LOG_WARNING, "EVAL requires an argument: EVAL(<string>)\n");
840                 return -1;
841         }
842
843         pbx_substitute_variables_helper(chan, data, buf, buflen - 1);
844
845         return 0;
846 }
847
848 static struct ast_custom_function eval_function = {
849         .name = "EVAL",
850         .read = function_eval,
851 };
852
853 static int keypadhash(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
854 {
855         char *bufptr, *dataptr;
856
857         for (bufptr = buf, dataptr = data; bufptr < buf + buflen - 1; dataptr++) {
858                 if (*dataptr == '\0') {
859                         *bufptr++ = '\0';
860                         break;
861                 } else if (*dataptr == '1') {
862                         *bufptr++ = '1';
863                 } else if (strchr("AaBbCc2", *dataptr)) {
864                         *bufptr++ = '2';
865                 } else if (strchr("DdEeFf3", *dataptr)) {
866                         *bufptr++ = '3';
867                 } else if (strchr("GgHhIi4", *dataptr)) {
868                         *bufptr++ = '4';
869                 } else if (strchr("JjKkLl5", *dataptr)) {
870                         *bufptr++ = '5';
871                 } else if (strchr("MmNnOo6", *dataptr)) {
872                         *bufptr++ = '6';
873                 } else if (strchr("PpQqRrSs7", *dataptr)) {
874                         *bufptr++ = '7';
875                 } else if (strchr("TtUuVv8", *dataptr)) {
876                         *bufptr++ = '8';
877                 } else if (strchr("WwXxYyZz9", *dataptr)) {
878                         *bufptr++ = '9';
879                 } else if (*dataptr == '0') {
880                         *bufptr++ = '0';
881                 }
882         }
883         buf[buflen - 1] = '\0';
884
885         return 0;
886 }
887
888 static struct ast_custom_function keypadhash_function = {
889         .name = "KEYPADHASH",
890         .read = keypadhash,
891 };
892
893 static int string_toupper(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
894 {
895         char *bufptr = buf, *dataptr = data;
896
897         while ((bufptr < buf + buflen - 1) && (*bufptr++ = toupper(*dataptr++)));
898
899         return 0;
900 }
901
902 static struct ast_custom_function toupper_function = {
903         .name = "TOUPPER",
904         .read = string_toupper,
905 };
906
907 static int string_tolower(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
908 {
909         char *bufptr = buf, *dataptr = data;
910
911         while ((bufptr < buf + buflen - 1) && (*bufptr++ = tolower(*dataptr++)));
912
913         return 0;
914 }
915
916 static struct ast_custom_function tolower_function = {
917         .name = "TOLOWER",
918         .read = string_tolower,
919 };
920
921 static int unload_module(void)
922 {
923         int res = 0;
924
925         res |= ast_custom_function_unregister(&fieldqty_function);
926         res |= ast_custom_function_unregister(&filter_function);
927         res |= ast_custom_function_unregister(&listfilter_function);
928         res |= ast_custom_function_unregister(&regex_function);
929         res |= ast_custom_function_unregister(&array_function);
930         res |= ast_custom_function_unregister(&quote_function);
931         res |= ast_custom_function_unregister(&len_function);
932         res |= ast_custom_function_unregister(&strftime_function);
933         res |= ast_custom_function_unregister(&strptime_function);
934         res |= ast_custom_function_unregister(&eval_function);
935         res |= ast_custom_function_unregister(&keypadhash_function);
936         res |= ast_custom_function_unregister(&hashkeys_function);
937         res |= ast_custom_function_unregister(&hash_function);
938         res |= ast_unregister_application(app_clearhash);
939         res |= ast_custom_function_unregister(&toupper_function);
940         res |= ast_custom_function_unregister(&tolower_function);
941
942         return res;
943 }
944
945 static int load_module(void)
946 {
947         int res = 0;
948
949         res |= ast_custom_function_register(&fieldqty_function);
950         res |= ast_custom_function_register(&filter_function);
951         res |= ast_custom_function_register(&listfilter_function);
952         res |= ast_custom_function_register(&regex_function);
953         res |= ast_custom_function_register(&array_function);
954         res |= ast_custom_function_register(&quote_function);
955         res |= ast_custom_function_register(&len_function);
956         res |= ast_custom_function_register(&strftime_function);
957         res |= ast_custom_function_register(&strptime_function);
958         res |= ast_custom_function_register(&eval_function);
959         res |= ast_custom_function_register(&keypadhash_function);
960         res |= ast_custom_function_register(&hashkeys_function);
961         res |= ast_custom_function_register(&hash_function);
962         res |= ast_register_application_xml(app_clearhash, exec_clearhash);
963         res |= ast_custom_function_register(&toupper_function);
964         res |= ast_custom_function_register(&tolower_function);
965
966         return res;
967 }
968
969 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "String handling dialplan functions");