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