055fd0c4e09597282c509f89494b257dd8ddd906
[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 /*** MODULEINFO
29         <support_level>core</support_level>
30  ***/
31
32 #include "asterisk.h"
33
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
35
36 #include <regex.h>
37 #include <ctype.h>
38
39 #include "asterisk/module.h"
40 #include "asterisk/channel.h"
41 #include "asterisk/pbx.h"
42 #include "asterisk/utils.h"
43 #include "asterisk/app.h"
44 #include "asterisk/localtime.h"
45 #include "asterisk/test.h"
46
47 AST_THREADSTORAGE(result_buf);
48 AST_THREADSTORAGE(tmp_buf);
49
50 /*** DOCUMENTATION
51         <function name="FIELDQTY" language="en_US">
52                 <synopsis>
53                         Count the fields with an arbitrary delimiter
54                 </synopsis>
55                 <syntax>
56                         <parameter name="varname" required="true" />
57                         <parameter name="delim" required="true" />
58                 </syntax>
59                 <description>
60                         <para>The delimiter may be specified as a special or extended ASCII character, by encoding it.  The characters
61                         <literal>\n</literal>, <literal>\r</literal>, and <literal>\t</literal> are all recognized as the newline,
62                         carriage return, and tab characters, respectively.  Also, octal and hexadecimal specifications are recognized
63                         by the patterns <literal>\0nnn</literal> and <literal>\xHH</literal>, respectively.  For example, if you wanted
64                         to encode a comma as the delimiter, you could use either <literal>\054</literal> or <literal>\x2C</literal>.</para>
65                         <para>Example: If ${example} contains <literal>ex-amp-le</literal>, then ${FIELDQTY(example,-)} returns 3.</para>
66                 </description>
67         </function>
68         <function name="FIELDNUM" language="en_US">
69                 <synopsis>
70                         Return the 1-based offset of a field in a list
71                 </synopsis>
72                 <syntax>
73                         <parameter name="varname" required="true" />
74                         <parameter name="delim" required="true" />
75                         <parameter name="value" required="true" />
76                 </syntax>
77                 <description>
78                         <para>Search the variable named <replaceable>varname</replaceable> for the string <replaceable>value</replaceable>
79                         delimited by <replaceable>delim</replaceable> and return a 1-based offset as to its location. If not found
80                         or an error occured, return <literal>0</literal>.</para>
81                         <para>The delimiter may be specified as a special or extended ASCII character, by encoding it.  The characters
82                         <literal>\n</literal>, <literal>\r</literal>, and <literal>\t</literal> are all recognized as the newline,
83                         carriage return, and tab characters, respectively.  Also, octal and hexadecimal specifications are recognized
84                         by the patterns <literal>\0nnn</literal> and <literal>\xHH</literal>, respectively.  For example, if you wanted
85                         to encode a comma as the delimiter, you could use either <literal>\054</literal> or <literal>\x2C</literal>.</para>
86                         <para>Example: If ${example} contains <literal>ex-amp-le</literal>, then ${FIELDNUM(example,-,amp)} returns 2.</para>
87                 </description>
88         </function>
89         <function name="LISTFILTER" language="en_US">
90                 <synopsis>Remove an item from a list, by name.</synopsis>
91                 <syntax>
92                         <parameter name="varname" required="true" />
93                         <parameter name="delim" required="true" default="," />
94                         <parameter name="value" required="true" />
95                 </syntax>
96                 <description>
97                         <para>Remove <replaceable>value</replaceable> from the list contained in the <replaceable>varname</replaceable>
98                         variable, where the list delimiter is specified by the <replaceable>delim</replaceable> parameter.  This is
99                         very useful for removing a single channel name from a list of channels, for example.</para>
100                 </description>
101         </function>
102         <function name="FILTER" language="en_US">
103                 <synopsis>
104                         Filter the string to include only the allowed characters
105                 </synopsis>
106                 <syntax>
107                         <parameter name="allowed-chars" required="true" />
108                         <parameter name="string" required="true" />
109                 </syntax>
110                 <description>
111                         <para>Permits all characters listed in <replaceable>allowed-chars</replaceable>, 
112                         filtering all others outs. In addition to literally listing the characters, 
113                         you may also use ranges of characters (delimited by a <literal>-</literal></para>
114                         <para>Hexadecimal characters started with a <literal>\x</literal>(i.e. \x20)</para>
115                         <para>Octal characters started with a <literal>\0</literal> (i.e. \040)</para>
116                         <para>Also <literal>\t</literal>,<literal>\n</literal> and <literal>\r</literal> are recognized.</para> 
117                         <note><para>If you want the <literal>-</literal> character it needs to be prefixed with a 
118                         <literal>\</literal></para></note>
119                 </description>
120         </function>
121         <function name="REPLACE" language="en_US">
122                 <synopsis>
123                         Replace a set of characters in a given string with another character.
124                 </synopsis>
125                 <syntax>
126                         <parameter name="varname" required="true" />
127                         <parameter name="find-chars" required="true" />
128                         <parameter name="replace-char" required="false" />
129                 </syntax>
130                 <description>
131                         <para>Iterates through a string replacing all the <replaceable>find-chars</replaceable> with
132                         <replaceable>replace-char</replaceable>.  <replaceable>replace-char</replaceable> may be either
133                         empty or contain one character.  If empty, all <replaceable>find-chars</replaceable> will be
134                         deleted from the output.</para>
135                         <note><para>The replacement only occurs in the output.  The original variable is not
136                         altered.</para></note>
137                 </description>
138         </function>
139         <function name="STRREPLACE" language="en_US">
140                 <synopsis>
141                         Replace instances of a substring within a string with another string.
142                 </synopsis>
143                 <syntax>
144                         <parameter name="varname" required="true" />
145                         <parameter name="find-string" required="true" />
146                         <parameter name="replace-string" required="false" />
147                         <parameter name="max-replacements" required="false" />
148                 </syntax>
149                 <description>
150                         <para>Searches for all instances of the <replaceable>find-string</replaceable> in provided variable and
151                         replaces them with <replaceable>replace-string</replaceable>.  If <replaceable>replace-string</replaceable>
152                         is an empty string, this will effecively delete that substring.  If <replaceable>max-replacements</replaceable>
153                         is specified, this function will stop after performing replacements <replaceable>max-replacements</replaceable> times.</para>
154                         <note><para>The replacement only occurs in the output.  The original variable is not altered.</para></note>
155                 </description>
156         </function>
157         <function name="PASSTHRU" language="en_US">
158                 <synopsis>
159                         Pass the given argument back as a value.
160                 </synopsis>
161                 <syntax>
162                         <parameter name="string" required="false" />
163                 </syntax>
164                 <description>
165                         <para>Literally returns the given <replaceable>string</replaceable>.  The intent is to permit
166                         other dialplan functions which take a variable name as an argument to be able to take a literal
167                         string, instead.</para>
168                 </description>
169         </function>
170         <function name="REGEX" language="en_US">
171                 <synopsis>
172                         Check string against a regular expression.
173                 </synopsis>
174                 <syntax argsep=" ">
175                         <parameter name="&quot;regular expression&quot;" required="true" />
176                         <parameter name="string" required="true" />
177                 </syntax>
178                 <description>
179                         <para>Return <literal>1</literal> on regular expression match or <literal>0</literal> otherwise</para>
180                         <para>Please note that the space following the double quotes separating the 
181                         regex from the data is optional and if present, is skipped. If a space is 
182                         desired at the beginning of the data, then put two spaces there; the second 
183                         will not be skipped.</para>
184                 </description>
185         </function>
186         <application name="ClearHash" language="en_US">
187                 <synopsis>
188                         Clear the keys from a specified hashname.
189                 </synopsis>
190                 <syntax>
191                         <parameter name="hashname" required="true" />
192                 </syntax>
193                 <description>
194                         <para>Clears all keys out of the specified <replaceable>hashname</replaceable>.</para>
195                 </description>
196         </application>
197         <function name="HASH" language="en_US">
198                 <synopsis>
199                         Implementation of a dialplan associative array
200                 </synopsis>
201                 <syntax>
202                         <parameter name="hashname" required="true" />
203                         <parameter name="hashkey" />
204                 </syntax>
205                 <description>
206                         <para>In two arguments mode, gets and sets values to corresponding keys within
207                         a named associative array. The single-argument mode will only work when assigned
208                         to from a function defined by func_odbc</para>
209                 </description>
210         </function>
211         <function name="HASHKEYS" language="en_US">
212                 <synopsis>
213                         Retrieve the keys of the HASH() function.
214                 </synopsis>
215                 <syntax>
216                         <parameter name="hashname" required="true" />
217                 </syntax>
218                 <description>
219                         <para>Returns a comma-delimited list of the current keys of the associative array 
220                         defined by the HASH() function. Note that if you iterate over the keys of 
221                         the result, adding keys during iteration will cause the result of the HASHKEYS()
222                         function to change.</para>
223                 </description>
224         </function>
225         <function name="KEYPADHASH" language="en_US">
226                 <synopsis>
227                         Hash the letters in string into equivalent keypad numbers.
228                 </synopsis>
229                 <syntax>
230                         <parameter name="string" required="true" />
231                 </syntax>
232                 <description>
233                         <para>Example: ${KEYPADHASH(Les)} returns "537"</para>
234                 </description>
235         </function>
236         <function name="ARRAY" language="en_US">
237                 <synopsis>
238                         Allows setting multiple variables at once.
239                 </synopsis>
240                 <syntax>
241                         <parameter name="var1" required="true" />
242                         <parameter name="var2" required="false" multiple="true" />
243                         <parameter name="varN" required="false" />
244                 </syntax>
245                 <description>
246                         <para>The comma-delimited list passed as a value to which the function is set will 
247                         be interpreted as a set of values to which the comma-delimited list of 
248                         variable names in the argument should be set.</para>
249                         <para>Example: Set(ARRAY(var1,var2)=1,2) will set var1 to 1 and var2 to 2</para>
250                 </description>
251         </function>
252         <function name="STRPTIME" language="en_US">
253                 <synopsis>
254                         Returns the epoch of the arbitrary date/time string structured as described by the format.
255                 </synopsis>
256                 <syntax>
257                         <parameter name="datetime" required="true" />
258                         <parameter name="timezone" required="true" />
259                         <parameter name="format" required="true" />
260                 </syntax>
261                 <description>
262                         <para>This is useful for converting a date into <literal>EPOCH</literal> time, 
263                         possibly to pass to an application like SayUnixTime or to calculate the difference
264                         between the two date strings</para>
265                         <para>Example: ${STRPTIME(2006-03-01 07:30:35,America/Chicago,%Y-%m-%d %H:%M:%S)} returns 1141219835</para>
266                 </description>
267         </function>
268         <function name="STRFTIME" language="en_US">
269                 <synopsis>
270                         Returns the current date/time in the specified format.
271                 </synopsis>
272                 <syntax>
273                         <parameter name="epoch" />
274                         <parameter name="timezone" />
275                         <parameter name="format" />
276                 </syntax>
277                 <description>
278                         <para>STRFTIME supports all of the same formats as the underlying C function
279                         <emphasis>strftime(3)</emphasis>.
280                         It also supports the following format: <literal>%[n]q</literal> - fractions of a second,
281                         with leading zeros.</para>
282                         <para>Example: <literal>%3q</literal> will give milliseconds and <literal>%1q</literal>
283                         will give tenths of a second. The default is set at milliseconds (n=3).
284                         The common case is to use it in combination with %S, as in <literal>%S.%3q</literal>.</para>
285                 </description>
286                 <see-also>
287                         <ref type="manpage">strftime(3)</ref>
288                 </see-also>
289         </function>
290         <function name="EVAL" language="en_US">
291                 <synopsis>
292                         Evaluate stored variables
293                 </synopsis>
294                 <syntax>
295                         <parameter name="variable" required="true" />
296                 </syntax>
297                 <description>
298                         <para>Using EVAL basically causes a string to be evaluated twice.
299                         When a variable or expression is in the dialplan, it will be
300                         evaluated at runtime. However, if the results of the evaluation
301                         is in fact another variable or expression, using EVAL will have it
302                         evaluated a second time.</para>
303                         <para>Example: If the <variable>MYVAR</variable> contains
304                         <variable>OTHERVAR</variable>, then the result of ${EVAL(
305                         <variable>MYVAR</variable>)} in the dialplan will be the
306                         contents of <variable>OTHERVAR</variable>. Normally just
307                         putting <variable>MYVAR</variable> in the dialplan the result
308                         would be <variable>OTHERVAR</variable>.</para>
309                 </description>
310         </function>
311         <function name="TOUPPER" language="en_US">
312                 <synopsis>
313                         Convert string to all uppercase letters.
314                 </synopsis>
315                 <syntax>
316                         <parameter name="string" required="true" />
317                 </syntax>
318                 <description>
319                         <para>Example: ${TOUPPER(Example)} returns "EXAMPLE"</para>
320                 </description>
321         </function>
322         <function name="TOLOWER" language="en_US">
323                 <synopsis>
324                         Convert string to all lowercase letters.
325                 </synopsis>
326                 <syntax>
327                         <parameter name="string" required="true" />
328                 </syntax>
329                 <description>
330                         <para>Example: ${TOLOWER(Example)} returns "example"</para>
331                 </description>
332         </function>
333         <function name="LEN" language="en_US">
334                 <synopsis>
335                         Return the length of the string given.
336                 </synopsis>
337                 <syntax>
338                         <parameter name="string" required="true" />
339                 </syntax>
340                 <description>
341                         <para>Example: ${LEN(example)} returns 7</para>
342                 </description>
343         </function>
344         <function name="QUOTE" language="en_US">
345                 <synopsis>
346                         Quotes a given string, escaping embedded quotes as necessary
347                 </synopsis>
348                 <syntax>
349                         <parameter name="string" required="true" />
350                 </syntax>
351                 <description>
352                         <para>Example: ${QUOTE(ab"c"de)} will return "abcde"</para>
353                 </description>
354         </function>
355         <function name="CSV_QUOTE" language="en_US">
356                 <synopsis>
357                         Quotes a given string for use in a CSV file, escaping embedded quotes as necessary
358                 </synopsis>
359                 <syntax>
360                         <parameter name="string" required="true" />
361                 </syntax>
362                 <description>
363                         <para>Example: ${CSV_QUOTE("a,b" 123)} will return """a,b"" 123"</para>
364                 </description>
365         </function>
366         <function name="SHIFT" language="en_US">
367                 <synopsis>
368                         Removes and returns the first item off of a variable containing delimited text
369                 </synopsis>
370                 <syntax>
371                         <parameter name="varname" required="true" />
372                         <parameter name="delimiter" required="false" default="," />
373                 </syntax>
374                 <description>
375                         <para>Example:</para>
376                         <para>exten => s,1,Set(array=one,two,three)</para>
377                         <para>exten => s,n,While($["${SET(var=${SHIFT(array)})}" != ""])</para>
378                         <para>exten => s,n,NoOp(var is ${var})</para>
379                         <para>exten => s,n,EndWhile</para>
380                         <para>This would iterate over each value in array, left to right, and
381                                 would result in NoOp(var is one), NoOp(var is two), and
382                                 NoOp(var is three) being executed.
383                         </para>
384                 </description>
385         </function>     
386         <function name="POP" language="en_US">
387                 <synopsis>
388                         Removes and returns the last item off of a variable containing delimited text
389                 </synopsis>
390                 <syntax>
391                         <parameter name="varname" required="true" />
392                         <parameter name="delimiter" required="false" default="," />
393                 </syntax>
394                 <description>
395                         <para>Example:</para>
396                         <para>exten => s,1,Set(array=one,two,three)</para>
397                         <para>exten => s,n,While($["${SET(var=${POP(array)})}" != ""])</para>
398                         <para>exten => s,n,NoOp(var is ${var})</para>
399                         <para>exten => s,n,EndWhile</para>
400                         <para>This would iterate over each value in array, right to left, and
401                                 would result in NoOp(var is three), NoOp(var is two), and
402                                 NoOp(var is one) being executed.
403                         </para>
404                 </description>
405         </function>     
406         <function name="PUSH" language="en_US">
407                 <synopsis>
408                         Appends one or more values to the end of a variable containing delimited text
409                 </synopsis>
410                 <syntax>
411                         <parameter name="varname" required="true" />
412                         <parameter name="delimiter" required="false" default="," />
413                 </syntax>
414                 <description>
415                         <para>Example: Set(PUSH(array)=one,two,three) would append one,
416                                 two, and three to the end of the values stored in the variable
417                                 "array".
418                         </para>
419                 </description>
420         </function>
421         <function name="UNSHIFT" language="en_US">
422                 <synopsis>
423                         Inserts one or more values to the beginning of a variable containing delimited text
424                 </synopsis>
425                 <syntax>
426                         <parameter name="varname" required="true" />
427                         <parameter name="delimiter" required="false" default="," />
428                 </syntax>
429                 <description>
430                         <para>Example: Set(UNSHIFT(array)=one,two,three) would insert one,
431                                 two, and three before the values stored in the variable
432                                 "array".
433                         </para>
434                 </description>
435         </function>
436  ***/
437
438 static int function_fieldqty_helper(struct ast_channel *chan, const char *cmd,
439                              char *parse, char *buf, struct ast_str **sbuf, ssize_t len)
440 {
441         char *varsubst;
442         struct ast_str *str = ast_str_thread_get(&result_buf, 16);
443         int fieldcount = 0;
444         AST_DECLARE_APP_ARGS(args,
445                              AST_APP_ARG(varname);
446                              AST_APP_ARG(delim);
447                 );
448         char delim[2] = "";
449         size_t delim_used;
450
451         if (!str) {
452                 return -1;
453         }
454
455         AST_STANDARD_APP_ARGS(args, parse);
456         if (args.delim) {
457                 ast_get_encoded_char(args.delim, delim, &delim_used);
458
459                 varsubst = alloca(strlen(args.varname) + 4);
460
461                 sprintf(varsubst, "${%s}", args.varname);
462                 ast_str_substitute_variables(&str, 0, chan, varsubst);
463                 if (ast_str_strlen(str) == 0) {
464                         fieldcount = 0;
465                 } else {
466                         char *varval = ast_str_buffer(str);
467                         while (strsep(&varval, delim)) {
468                                 fieldcount++;
469                         }
470                 }
471         } else {
472                 fieldcount = 1;
473         }
474         if (sbuf) {
475                 ast_str_set(sbuf, len, "%d", fieldcount);
476         } else {
477                 snprintf(buf, len, "%d", fieldcount);
478         }
479
480         return 0;
481 }
482
483 static int function_fieldqty(struct ast_channel *chan, const char *cmd,
484                              char *parse, char *buf, size_t len)
485 {
486         return function_fieldqty_helper(chan, cmd, parse, buf, NULL, len);
487 }
488
489 static int function_fieldqty_str(struct ast_channel *chan, const char *cmd,
490                                  char *parse, struct ast_str **buf, ssize_t len)
491 {
492         return function_fieldqty_helper(chan, cmd, parse, NULL, buf, len);
493 }
494
495 static struct ast_custom_function fieldqty_function = {
496         .name = "FIELDQTY",
497         .read = function_fieldqty,
498         .read2 = function_fieldqty_str,
499 };
500
501 static int function_fieldnum_helper(struct ast_channel *chan, const char *cmd,
502                                 char *parse, char *buf, struct ast_str **sbuf, ssize_t len)
503 {
504         char *varsubst, *field;
505         struct ast_str *str = ast_str_thread_get(&result_buf, 16);
506         int fieldindex = 0, res = 0;
507         AST_DECLARE_APP_ARGS(args,
508                 AST_APP_ARG(varname);
509                 AST_APP_ARG(delim);
510                 AST_APP_ARG(field);
511         );
512         char delim[2] = "";
513         size_t delim_used;
514
515         if (!str) {
516                 return -1;
517         }
518
519         AST_STANDARD_APP_ARGS(args, parse);
520
521         if (args.argc < 3) {
522                 ast_log(LOG_ERROR, "Usage: FIELDNUM(<listname>,<delimiter>,<fieldvalue>)\n");
523                 res = -1;
524         } else {
525                 varsubst = alloca(strlen(args.varname) + 4);
526                 sprintf(varsubst, "${%s}", args.varname);
527
528                 ast_str_substitute_variables(&str, 0, chan, varsubst);
529
530                 if (ast_str_strlen(str) == 0 || ast_strlen_zero(args.delim)) {
531                         fieldindex = 0;
532                 } else if (ast_get_encoded_char(args.delim, delim, &delim_used) == -1) {
533                         res = -1;
534                 } else {
535                         char *varval = ast_str_buffer(str);
536
537                         while ((field = strsep(&varval, delim)) != NULL) {
538                                 fieldindex++;
539
540                                 if (!strcasecmp(field, args.field)) {
541                                         break;
542                                 }
543                         }
544
545                         if (!field) {
546                                 fieldindex = 0;
547                         }
548
549                         res = 0;
550                 }
551         }
552
553         if (sbuf) {
554                 ast_str_set(sbuf, len, "%d", fieldindex);
555         } else {
556                 snprintf(buf, len, "%d", fieldindex);
557         }
558
559         return res;
560 }
561
562 static int function_fieldnum(struct ast_channel *chan, const char *cmd,
563                              char *parse, char *buf, size_t len)
564 {
565         return function_fieldnum_helper(chan, cmd, parse, buf, NULL, len);
566 }
567
568 static int function_fieldnum_str(struct ast_channel *chan, const char *cmd,
569                                  char *parse, struct ast_str **buf, ssize_t len)
570 {
571         return function_fieldnum_helper(chan, cmd, parse, NULL, buf, len);
572 }
573
574 static struct ast_custom_function fieldnum_function = {
575         .name = "FIELDNUM",
576         .read = function_fieldnum,
577         .read2 = function_fieldnum_str,
578 };
579
580 static int listfilter(struct ast_channel *chan, const char *cmd, char *parse, char *buf, struct ast_str **bufstr, ssize_t len)
581 {
582         AST_DECLARE_APP_ARGS(args,
583                 AST_APP_ARG(listname);
584                 AST_APP_ARG(delimiter);
585                 AST_APP_ARG(fieldvalue);
586         );
587         const char *ptr;
588         struct ast_str *orig_list = ast_str_thread_get(&tmp_buf, 16);
589         const char *begin, *cur, *next;
590         int dlen, flen, first = 1;
591         struct ast_str *result, **result_ptr = &result;
592         char *delim, *varsubst;
593
594         AST_STANDARD_APP_ARGS(args, parse);
595
596         if (buf) {
597                 if (!(result = ast_str_thread_get(&result_buf, 16))) {
598                         return -1;
599                 }
600         } else {
601                 /* Place the result directly into the output buffer */
602                 result_ptr = bufstr;
603         }
604
605         if (args.argc < 3) {
606                 ast_log(LOG_ERROR, "Usage: LISTFILTER(<listname>,<delimiter>,<fieldvalue>)\n");
607                 return -1;
608         }
609
610         varsubst = alloca(strlen(args.listname) + 4);
611         sprintf(varsubst, "${%s}", args.listname);
612
613         /* If we don't lock the channel, the variable could disappear out from underneath us. */
614         if (chan) {
615                 ast_channel_lock(chan);
616         }
617         ast_str_substitute_variables(&orig_list, 0, chan, varsubst);
618         if (!ast_str_strlen(orig_list)) {
619                 ast_log(LOG_ERROR, "List variable '%s' not found\n", args.listname);
620                 if (chan) {
621                         ast_channel_unlock(chan);
622                 }
623                 return -1;
624         }
625
626         /* If the string isn't there, just copy out the string and be done with it. */
627         if (!(ptr = strstr(ast_str_buffer(orig_list), args.fieldvalue))) {
628                 if (buf) {
629                         ast_copy_string(buf, ast_str_buffer(orig_list), len);
630                 } else {
631                         ast_str_set(result_ptr, len, "%s", ast_str_buffer(orig_list));
632                 }
633                 if (chan) {
634                         ast_channel_unlock(chan);
635                 }
636                 return 0;
637         }
638
639         dlen = strlen(args.delimiter);
640         delim = alloca(dlen + 1);
641         ast_get_encoded_str(args.delimiter, delim, dlen + 1);
642
643         if ((dlen = strlen(delim)) == 0) {
644                 delim = ",";
645                 dlen = 1;
646         }
647
648         flen = strlen(args.fieldvalue);
649
650         ast_str_reset(*result_ptr);
651         /* Enough space for any result */
652         if (len > -1) {
653                 ast_str_make_space(result_ptr, len ? len : ast_str_strlen(orig_list) + 1);
654         }
655
656         begin = ast_str_buffer(orig_list);
657         next = strstr(begin, delim);
658
659         do {
660                 /* Find next boundary */
661                 if (next) {
662                         cur = next;
663                         next = strstr(cur + dlen, delim);
664                 } else {
665                         cur = strchr(begin + dlen, '\0');
666                 }
667
668                 if (flen == cur - begin && !strncmp(begin, args.fieldvalue, flen)) {
669                         /* Skip field */
670                         begin += flen + dlen;
671                 } else {
672                         /* Copy field to output */
673                         if (!first) {
674                                 ast_str_append(result_ptr, len, "%s", delim);
675                         }
676
677                         ast_str_append_substr(result_ptr, len, begin, cur - begin);
678                         first = 0;
679                         begin = cur + dlen;
680                 }
681         } while (*cur != '\0');
682         if (chan) {
683                 ast_channel_unlock(chan);
684         }
685
686         if (buf) {
687                 ast_copy_string(buf, ast_str_buffer(result), len);
688         }
689
690         return 0;
691 }
692
693 static int listfilter_read(struct ast_channel *chan, const char *cmd, char *parse, char *buf, size_t len)
694 {
695         return listfilter(chan, cmd, parse, buf, NULL, len);
696 }
697
698 static int listfilter_read2(struct ast_channel *chan, const char *cmd, char *parse, struct ast_str **buf, ssize_t len)
699 {
700         return listfilter(chan, cmd, parse, NULL, buf, len);
701 }
702
703 static struct ast_custom_function listfilter_function = {
704         .name = "LISTFILTER",
705         .read = listfilter_read,
706         .read2 = listfilter_read2,
707 };
708
709 static int filter(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
710                   size_t len)
711 {
712         AST_DECLARE_APP_ARGS(args,
713                              AST_APP_ARG(allowed);
714                              AST_APP_ARG(string);
715         );
716         char *outbuf = buf;
717         unsigned char ac;
718         char allowed[256] = "";
719         size_t allowedlen = 0;
720         int32_t bitfield[8] = { 0, }; /* 256 bits */
721
722         AST_STANDARD_RAW_ARGS(args, parse);
723
724         if (!args.string) {
725                 ast_log(LOG_ERROR, "Usage: FILTER(<allowed-chars>,<string>)\n");
726                 return -1;
727         }
728
729         if (args.allowed[0] == '"' && !ast_opt_dont_warn) {
730                 ast_log(LOG_WARNING, "FILTER allowed characters includes the quote (\") character.  This may not be what you want.\n");
731         }
732
733         /* Expand ranges */
734         for (; *(args.allowed);) {
735                 char c1 = 0, c2 = 0;
736                 size_t consumed = 0;
737
738                 if (ast_get_encoded_char(args.allowed, &c1, &consumed))
739                         return -1;
740                 args.allowed += consumed;
741
742                 if (*(args.allowed) == '-') {
743                         if (ast_get_encoded_char(args.allowed + 1, &c2, &consumed))
744                                 c2 = c1;
745                         args.allowed += consumed + 1;
746
747                         if ((unsigned char) c2 < (unsigned char) c1 && !ast_opt_dont_warn) {
748                                 ast_log(LOG_WARNING, "Range wrapping in FILTER(%s,%s).  This may not be what you want.\n", parse, args.string);
749                         }
750
751                         /*!\note
752                          * Looks a little strange, until you realize that we can overflow
753                          * the size of a char.
754                          */
755                         for (ac = (unsigned char) c1; ac != (unsigned char) c2; ac++) {
756                                 bitfield[ac / 32] |= 1 << (ac % 32);
757                         }
758                         bitfield[ac / 32] |= 1 << (ac % 32);
759
760                         ast_debug(4, "c1=%d, c2=%d\n", c1, c2);
761                 } else {
762                         ac = (unsigned char) c1;
763                         ast_debug(4, "c1=%d, consumed=%d, args.allowed=%s\n", c1, (int) consumed, args.allowed - consumed);
764                         bitfield[ac / 32] |= 1 << (ac % 32);
765                 }
766         }
767
768         for (ac = 1; ac != 0; ac++) {
769                 if (bitfield[ac / 32] & (1 << (ac % 32))) {
770                         allowed[allowedlen++] = ac;
771                 }
772         }
773
774         ast_debug(1, "Allowed: %s\n", allowed);
775
776         for (; *(args.string) && (buf + len - 1 > outbuf); (args.string)++) {
777                 if (strchr(allowed, *(args.string)))
778                         *outbuf++ = *(args.string);
779         }
780         *outbuf = '\0';
781
782         return 0;
783 }
784
785 static struct ast_custom_function filter_function = {
786         .name = "FILTER",
787         .read = filter,
788 };
789
790 static int replace(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
791 {
792         AST_DECLARE_APP_ARGS(args,
793                 AST_APP_ARG(varname);
794                 AST_APP_ARG(find);
795                 AST_APP_ARG(replace);
796         );
797         char *strptr, *varsubst;
798         struct ast_str *str = ast_str_thread_get(&result_buf, 16);
799         char find[256]; /* Only 256 characters possible */
800         char replace[2] = "";
801         size_t unused;
802
803         AST_STANDARD_APP_ARGS(args, data);
804
805         if (!str) {
806                 return -1;
807         }
808
809         if (args.argc < 2) {
810                 ast_log(LOG_ERROR, "Usage: %s(<varname>,<search-chars>[,<replace-char>])\n", cmd);
811                 return -1;
812         }
813
814         /* Decode escapes */
815         ast_get_encoded_str(args.find, find, sizeof(find));
816         ast_get_encoded_char(args.replace, replace, &unused);
817
818         if (ast_strlen_zero(find) || ast_strlen_zero(args.varname)) {
819                 ast_log(LOG_ERROR, "The characters to search for and the variable name must not be empty.\n");
820                 return -1;
821         }
822
823         varsubst = alloca(strlen(args.varname) + 4);
824         sprintf(varsubst, "${%s}", args.varname);
825         ast_str_substitute_variables(&str, 0, chan, varsubst);
826
827         if (!ast_str_strlen(str)) {
828                 /* Blank, nothing to replace */
829                 return -1;
830         }
831
832         ast_debug(3, "String to search: (%s)\n", ast_str_buffer(str));
833         ast_debug(3, "Characters to find: (%s)\n", find);
834         ast_debug(3, "Character to replace with: (%s)\n", replace);
835
836         for (strptr = ast_str_buffer(str); *strptr; strptr++) {
837                 /* buf is already a mutable buffer, so we construct the result
838                  * directly there */
839                 if (strchr(find, *strptr)) {
840                         if (ast_strlen_zero(replace)) {
841                                 /* Remove character */
842                                 strcpy(strptr, strptr + 1); /* SAFE */
843                                 strptr--;
844                         } else {
845                                 /* Replace character */
846                                 *strptr = *replace;
847                         }
848                 }
849         }
850
851         ast_str_set(buf, len, "%s", ast_str_buffer(str));
852         return 0;
853 }
854
855 static struct ast_custom_function replace_function = {
856         .name = "REPLACE",
857         .read2 = replace,
858 };
859
860 static int strreplace(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
861 {
862         char *starts[len]; /* marks starts of substrings */
863         char *varsubstr; /* substring for input var */
864         int count_len = 0; /* counter for starts */
865         char *p; /* tracks position of cursor for search and replace */
866         int find_size; /* length of given find-string */
867         int max_matches; /* number of matches we find before terminating search */
868
869         int x; /* loop counter */
870         struct ast_str *str = ast_str_thread_get(&result_buf, 16); /* Holds the data obtained from varname */
871
872         AST_DECLARE_APP_ARGS(args,
873                 AST_APP_ARG(varname);
874                 AST_APP_ARG(find_string);
875                 AST_APP_ARG(replace_string);
876                 AST_APP_ARG(max_replacements);
877         );
878
879         AST_STANDARD_APP_ARGS(args, data);
880
881         if (!str) { /* If we failed to allocate str, forget it.  We failed. */
882                 return -1;
883         }
884
885         if (args.argc < 2) { /* Didn't receive enough arguments to do anything */
886                 ast_log(LOG_ERROR, "Usage: %s(<varname>,<find-string>[,<replace-string>, <max-replacements>])\n", cmd);
887                 return -1;
888         }
889
890         /* No var name specified. Return failure, string is already empty. */
891         if (ast_strlen_zero(args.varname)) {
892                 return -1;
893         }
894
895         /* set varsubstr to the matching variable */
896         varsubstr = alloca(strlen(args.varname) + 4);
897         sprintf(varsubstr, "${%s}", args.varname);
898         ast_str_substitute_variables(&str, 0, chan, varsubstr);
899
900         p = ast_str_buffer(str);
901
902         /* Zero length find strings are a no-no. Kill the function if we run into one. */
903         if (ast_strlen_zero(args.find_string)) {
904                 ast_log(LOG_ERROR, "The <find-string> must have length > 0\n");
905                 return -1;
906         }
907         find_size = strlen(args.find_string);
908
909         /* If the replace string is a null pointer, set it to an empty string */
910         if (!args.replace_string) {
911                 args.replace_string = "";
912         }
913
914         /*
915          * If max_replacements specified and is a number, max_matches will become that.
916          * otherwise, just go the length of the input string
917          */
918         if (!(args.max_replacements && (max_matches = atoi(args.max_replacements)))) {
919                 max_matches = strlen(p);
920         }
921
922         /* Iterate through string finding matches until it is exhausted or we reach max_matches */
923         for (x = 0; x < max_matches; x++) {
924                 if ((p = strstr(p, args.find_string))) {
925                         starts[count_len++] = p;
926                         *p++ = '\0';
927                 } else {
928                         break;
929                 }
930         }
931
932         p = ast_str_buffer(str);
933
934         /* here we rebuild the string with the replaced words by using fancy ast_string_append on the buffer */
935         for (x = 0; x < count_len; x++) {
936                 ast_str_append(buf, 0, "%s", p);
937                 p = starts[x];
938                 p += find_size;
939                 ast_str_append(buf, 0, "%s", args.replace_string);
940         }
941         ast_str_append(buf, 0, "%s", p);
942
943         return 0;
944 }
945
946 static struct ast_custom_function strreplace_function = {
947         .name = "STRREPLACE",
948         .read2 = strreplace,
949 };
950
951 static int regex(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
952                  size_t len)
953 {
954         AST_DECLARE_APP_ARGS(args,
955                              AST_APP_ARG(null);
956                              AST_APP_ARG(reg);
957                              AST_APP_ARG(str);
958         );
959         int errcode;
960         regex_t regexbuf;
961
962         buf[0] = '\0';
963
964         AST_NONSTANDARD_APP_ARGS(args, parse, '"');
965
966         if (args.argc != 3) {
967                 ast_log(LOG_ERROR, "Unexpected arguments: should have been in the form '\"<regex>\" <string>'\n");
968                 return -1;
969         }
970         if ((*args.str == ' ') || (*args.str == '\t'))
971                 args.str++;
972
973         ast_debug(1, "FUNCTION REGEX (%s)(%s)\n", args.reg, args.str);
974
975         if ((errcode = regcomp(&regexbuf, args.reg, REG_EXTENDED | REG_NOSUB))) {
976                 regerror(errcode, &regexbuf, buf, len);
977                 ast_log(LOG_WARNING, "Malformed input %s(%s): %s\n", cmd, parse, buf);
978                 return -1;
979         }
980         
981         strcpy(buf, regexec(&regexbuf, args.str, 0, NULL, 0) ? "0" : "1");
982
983         regfree(&regexbuf);
984
985         return 0;
986 }
987
988 static struct ast_custom_function regex_function = {
989         .name = "REGEX",
990         .read = regex,
991 };
992
993 #define HASH_PREFIX     "~HASH~%s~"
994 #define HASH_FORMAT     HASH_PREFIX "%s~"
995
996 static char *app_clearhash = "ClearHash";
997
998 /* This function probably should migrate to main/pbx.c, as pbx_builtin_clearvar_prefix() */
999 static void clearvar_prefix(struct ast_channel *chan, const char *prefix)
1000 {
1001         struct ast_var_t *var;
1002         int len = strlen(prefix);
1003         AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->varshead, var, entries) {
1004                 if (strncasecmp(prefix, ast_var_name(var), len) == 0) {
1005                         AST_LIST_REMOVE_CURRENT(entries);
1006                         ast_free(var);
1007                 }
1008         }
1009         AST_LIST_TRAVERSE_SAFE_END
1010 }
1011
1012 static int exec_clearhash(struct ast_channel *chan, const char *data)
1013 {
1014         char prefix[80];
1015         snprintf(prefix, sizeof(prefix), HASH_PREFIX, data ? (char *)data : "null");
1016         clearvar_prefix(chan, prefix);
1017         return 0;
1018 }
1019
1020 static int array(struct ast_channel *chan, const char *cmd, char *var,
1021                  const char *value)
1022 {
1023         AST_DECLARE_APP_ARGS(arg1,
1024                              AST_APP_ARG(var)[100];
1025         );
1026         AST_DECLARE_APP_ARGS(arg2,
1027                              AST_APP_ARG(val)[100];
1028         );
1029         char *origvar = "", *value2, varname[256];
1030         int i, ishash = 0;
1031
1032         value2 = ast_strdupa(value);
1033         if (!var || !value2)
1034                 return -1;
1035
1036         if (!strcmp(cmd, "HASH")) {
1037                 const char *var2 = pbx_builtin_getvar_helper(chan, "~ODBCFIELDS~");
1038                 origvar = var;
1039                 if (var2)
1040                         var = ast_strdupa(var2);
1041                 else {
1042                         if (chan)
1043                                 ast_autoservice_stop(chan);
1044                         return -1;
1045                 }
1046                 ishash = 1;
1047         }
1048
1049         /* The functions this will generally be used with are SORT and ODBC_*, which
1050          * both return comma-delimited lists.  However, if somebody uses literal lists,
1051          * their commas will be translated to vertical bars by the load, and I don't
1052          * want them to be surprised by the result.  Hence, we prefer commas as the
1053          * delimiter, but we'll fall back to vertical bars if commas aren't found.
1054          */
1055         ast_debug(1, "array (%s=%s)\n", var, S_OR(value2, ""));
1056         AST_STANDARD_APP_ARGS(arg1, var);
1057
1058         AST_STANDARD_APP_ARGS(arg2, value2);
1059
1060         for (i = 0; i < arg1.argc; i++) {
1061                 ast_debug(1, "array set value (%s=%s)\n", arg1.var[i],
1062                                 S_OR(arg2.val[i], ""));
1063                 if (i < arg2.argc) {
1064                         if (ishash) {
1065                                 if (origvar[0] == '_') {
1066                                         if (origvar[1] == '_') {
1067                                                 snprintf(varname, sizeof(varname), "__" HASH_FORMAT, origvar + 2, arg1.var[i]);
1068                                         } else {
1069                                                 snprintf(varname, sizeof(varname), "_" HASH_FORMAT, origvar + 1, arg1.var[i]);
1070                                         }
1071                                 } else {
1072                                         snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
1073                                 }
1074
1075                                 pbx_builtin_setvar_helper(chan, varname, arg2.val[i]);
1076                         } else {
1077                                 pbx_builtin_setvar_helper(chan, arg1.var[i], arg2.val[i]);
1078                         }
1079                 } else {
1080                         /* We could unset the variable, by passing a NULL, but due to
1081                          * pushvar semantics, that could create some undesired behavior. */
1082                         if (ishash) {
1083                                 snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
1084                                 pbx_builtin_setvar_helper(chan, varname, "");
1085                         } else {
1086                                 pbx_builtin_setvar_helper(chan, arg1.var[i], "");
1087                         }
1088                 }
1089         }
1090
1091         return 0;
1092 }
1093
1094 static int hashkeys_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1095 {
1096         struct ast_var_t *newvar;
1097         struct ast_str *prefix = ast_str_alloca(80);
1098
1099         ast_str_set(&prefix, -1, HASH_PREFIX, data);
1100         memset(buf, 0, len);
1101
1102         AST_LIST_TRAVERSE(&chan->varshead, newvar, entries) {
1103                 if (strncasecmp(ast_str_buffer(prefix), ast_var_name(newvar), ast_str_strlen(prefix)) == 0) {
1104                         /* Copy everything after the prefix */
1105                         strncat(buf, ast_var_name(newvar) + ast_str_strlen(prefix), len - strlen(buf) - 1);
1106                         /* Trim the trailing ~ */
1107                         buf[strlen(buf) - 1] = ',';
1108                 }
1109         }
1110         /* Trim the trailing comma */
1111         buf[strlen(buf) - 1] = '\0';
1112         return 0;
1113 }
1114
1115 static int hashkeys_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
1116 {
1117         struct ast_var_t *newvar;
1118         struct ast_str *prefix = ast_str_alloca(80);
1119         char *tmp;
1120
1121         ast_str_set(&prefix, -1, HASH_PREFIX, data);
1122
1123         AST_LIST_TRAVERSE(&chan->varshead, newvar, entries) {
1124                 if (strncasecmp(ast_str_buffer(prefix), ast_var_name(newvar), ast_str_strlen(prefix)) == 0) {
1125                         /* Copy everything after the prefix */
1126                         ast_str_append(buf, len, "%s", ast_var_name(newvar) + ast_str_strlen(prefix));
1127                         /* Trim the trailing ~ */
1128                         tmp = ast_str_buffer(*buf);
1129                         tmp[ast_str_strlen(*buf) - 1] = ',';
1130                 }
1131         }
1132         /* Trim the trailing comma */
1133         tmp = ast_str_buffer(*buf);
1134         tmp[ast_str_strlen(*buf) - 1] = '\0';
1135         return 0;
1136 }
1137
1138 static int hash_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
1139 {
1140         char varname[256];
1141         AST_DECLARE_APP_ARGS(arg,
1142                 AST_APP_ARG(hashname);
1143                 AST_APP_ARG(hashkey);
1144         );
1145
1146         if (!strchr(var, ',')) {
1147                 /* Single argument version */
1148                 return array(chan, "HASH", var, value);
1149         }
1150
1151         AST_STANDARD_APP_ARGS(arg, var);
1152         if (arg.hashname[0] == '_') {
1153                 if (arg.hashname[1] == '_') {
1154                         snprintf(varname, sizeof(varname), "__" HASH_FORMAT, arg.hashname + 2, arg.hashkey);
1155                 } else {
1156                         snprintf(varname, sizeof(varname), "_" HASH_FORMAT, arg.hashname + 1, arg.hashkey);
1157                 }
1158         } else {
1159                 snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
1160         }
1161         pbx_builtin_setvar_helper(chan, varname, value);
1162
1163         return 0;
1164 }
1165
1166 static int hash_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1167 {
1168         char varname[256];
1169         const char *varvalue;
1170         AST_DECLARE_APP_ARGS(arg,
1171                 AST_APP_ARG(hashname);
1172                 AST_APP_ARG(hashkey);
1173         );
1174
1175         AST_STANDARD_APP_ARGS(arg, data);
1176         if (arg.argc == 2) {
1177                 snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
1178                 varvalue = pbx_builtin_getvar_helper(chan, varname);
1179                 if (varvalue)
1180                         ast_copy_string(buf, varvalue, len);
1181                 else
1182                         *buf = '\0';
1183         } else if (arg.argc == 1) {
1184                 char colnames[4096];
1185                 int i;
1186                 AST_DECLARE_APP_ARGS(arg2,
1187                         AST_APP_ARG(col)[100];
1188                 );
1189
1190                 /* Get column names, in no particular order */
1191                 hashkeys_read(chan, "HASHKEYS", arg.hashname, colnames, sizeof(colnames));
1192                 pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", colnames);
1193
1194                 AST_STANDARD_APP_ARGS(arg2, colnames);
1195                 *buf = '\0';
1196
1197                 /* Now get the corresponding column values, in exactly the same order */
1198                 for (i = 0; i < arg2.argc; i++) {
1199                         snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg2.col[i]);
1200                         varvalue = pbx_builtin_getvar_helper(chan, varname);
1201                         strncat(buf, varvalue, len - strlen(buf) - 1);
1202                         strncat(buf, ",", len - strlen(buf) - 1);
1203                 }
1204
1205                 /* Strip trailing comma */
1206                 buf[strlen(buf) - 1] = '\0';
1207         }
1208
1209         return 0;
1210 }
1211
1212 static struct ast_custom_function hash_function = {
1213         .name = "HASH",
1214         .write = hash_write,
1215         .read = hash_read,
1216 };
1217
1218 static struct ast_custom_function hashkeys_function = {
1219         .name = "HASHKEYS",
1220         .read = hashkeys_read,
1221         .read2 = hashkeys_read2,
1222 };
1223
1224 static struct ast_custom_function array_function = {
1225         .name = "ARRAY",
1226         .write = array,
1227 };
1228
1229 static int quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1230 {
1231         char *bufptr = buf, *dataptr = data;
1232
1233         if (len < 3){ /* at least two for quotes and one for binary zero */
1234                 ast_log(LOG_ERROR, "Not enough buffer");
1235                 return -1;
1236         }
1237
1238         if (ast_strlen_zero(data)) {
1239                 ast_log(LOG_WARNING, "No argument specified!\n");
1240                 ast_copy_string(buf, "\"\"", len);
1241                 return 0;
1242         }
1243
1244         *bufptr++ = '"';
1245         for (; bufptr < buf + len - 3; dataptr++) {
1246                 if (*dataptr == '\\') {
1247                         *bufptr++ = '\\';
1248                         *bufptr++ = '\\';
1249                 } else if (*dataptr == '"') {
1250                         *bufptr++ = '\\';
1251                         *bufptr++ = '"';
1252                 } else if (*dataptr == '\0') {
1253                         break;
1254                 } else {
1255                         *bufptr++ = *dataptr;
1256                 }
1257         }
1258         *bufptr++ = '"';
1259         *bufptr = '\0';
1260         return 0;
1261 }
1262
1263 static struct ast_custom_function quote_function = {
1264         .name = "QUOTE",
1265         .read = quote,
1266 };
1267
1268 static int csv_quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1269 {
1270         char *bufptr = buf, *dataptr = data;
1271
1272         if (len < 3) { /* at least two for quotes and one for binary zero */
1273                 ast_log(LOG_ERROR, "Not enough buffer");
1274                 return -1;
1275         }
1276
1277         if (ast_strlen_zero(data)) {
1278                 ast_copy_string(buf, "\"\"", len);
1279                 return 0;
1280         }
1281
1282         *bufptr++ = '"';
1283         for (; bufptr < buf + len - 3; dataptr++){
1284                 if (*dataptr == '"') {
1285                         *bufptr++ = '"';
1286                         *bufptr++ = '"';
1287                 } else if (*dataptr == '\0') {
1288                         break;
1289                 } else {
1290                         *bufptr++ = *dataptr;
1291                 }
1292         }
1293         *bufptr++ = '"';
1294         *bufptr='\0';
1295         return 0;
1296 }
1297
1298 static struct ast_custom_function csv_quote_function = {
1299         .name = "CSV_QUOTE",
1300         .read = csv_quote,
1301 };
1302
1303 static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
1304 {
1305         int length = 0;
1306
1307         if (data)
1308                 length = strlen(data);
1309
1310         snprintf(buf, buflen, "%d", length);
1311
1312         return 0;
1313 }
1314
1315 static struct ast_custom_function len_function = {
1316         .name = "LEN",
1317         .read = len,
1318         .read_max = 12,
1319 };
1320
1321 static int acf_strftime(struct ast_channel *chan, const char *cmd, char *parse,
1322                         char *buf, size_t buflen)
1323 {
1324         AST_DECLARE_APP_ARGS(args,
1325                              AST_APP_ARG(epoch);
1326                              AST_APP_ARG(timezone);
1327                              AST_APP_ARG(format);
1328         );
1329         struct timeval when;
1330         struct ast_tm tm;
1331
1332         buf[0] = '\0';
1333
1334         AST_STANDARD_APP_ARGS(args, parse);
1335
1336         ast_get_timeval(args.epoch, &when, ast_tvnow(), NULL);
1337         ast_localtime(&when, &tm, args.timezone);
1338
1339         if (!args.format)
1340                 args.format = "%c";
1341
1342         if (ast_strftime(buf, buflen, args.format, &tm) <= 0)
1343                 ast_log(LOG_WARNING, "C function strftime() output nothing?!!\n");
1344
1345         buf[buflen - 1] = '\0';
1346
1347         return 0;
1348 }
1349
1350 static struct ast_custom_function strftime_function = {
1351         .name = "STRFTIME",
1352         .read = acf_strftime,
1353 };
1354
1355 static int acf_strptime(struct ast_channel *chan, const char *cmd, char *data,
1356                         char *buf, size_t buflen)
1357 {
1358         AST_DECLARE_APP_ARGS(args,
1359                              AST_APP_ARG(timestring);
1360                              AST_APP_ARG(timezone);
1361                              AST_APP_ARG(format);
1362         );
1363         struct ast_tm tm;
1364
1365         buf[0] = '\0';
1366
1367         if (!data) {
1368                 ast_log(LOG_ERROR,
1369                                 "Asterisk function STRPTIME() requires an argument.\n");
1370                 return -1;
1371         }
1372
1373         AST_STANDARD_APP_ARGS(args, data);
1374
1375         if (ast_strlen_zero(args.format)) {
1376                 ast_log(LOG_ERROR,
1377                                 "No format supplied to STRPTIME(<timestring>,<timezone>,<format>)");
1378                 return -1;
1379         }
1380
1381         if (!ast_strptime(args.timestring, args.format, &tm)) {
1382                 ast_log(LOG_WARNING, "STRPTIME() found no time specified within the string\n");
1383         } else {
1384                 struct timeval when;
1385                 when = ast_mktime(&tm, args.timezone);
1386                 snprintf(buf, buflen, "%d", (int) when.tv_sec);
1387         }
1388
1389         return 0;
1390 }
1391
1392 static struct ast_custom_function strptime_function = {
1393         .name = "STRPTIME",
1394         .read = acf_strptime,
1395 };
1396
1397 static int function_eval(struct ast_channel *chan, const char *cmd, char *data,
1398                          char *buf, size_t buflen)
1399 {
1400         if (ast_strlen_zero(data)) {
1401                 ast_log(LOG_WARNING, "EVAL requires an argument: EVAL(<string>)\n");
1402                 return -1;
1403         }
1404
1405         pbx_substitute_variables_helper(chan, data, buf, buflen - 1);
1406
1407         return 0;
1408 }
1409
1410 static int function_eval2(struct ast_channel *chan, const char *cmd, char *data,
1411                          struct ast_str **buf, ssize_t buflen)
1412 {
1413         if (ast_strlen_zero(data)) {
1414                 ast_log(LOG_WARNING, "EVAL requires an argument: EVAL(<string>)\n");
1415                 return -1;
1416         }
1417
1418         ast_str_substitute_variables(buf, buflen, chan, data);
1419
1420         return 0;
1421 }
1422
1423 static struct ast_custom_function eval_function = {
1424         .name = "EVAL",
1425         .read = function_eval,
1426         .read2 = function_eval2,
1427 };
1428
1429 static int keypadhash(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
1430 {
1431         char *bufptr, *dataptr;
1432
1433         for (bufptr = buf, dataptr = data; bufptr < buf + buflen - 1; dataptr++) {
1434                 if (*dataptr == '\0') {
1435                         *bufptr++ = '\0';
1436                         break;
1437                 } else if (*dataptr == '1') {
1438                         *bufptr++ = '1';
1439                 } else if (strchr("AaBbCc2", *dataptr)) {
1440                         *bufptr++ = '2';
1441                 } else if (strchr("DdEeFf3", *dataptr)) {
1442                         *bufptr++ = '3';
1443                 } else if (strchr("GgHhIi4", *dataptr)) {
1444                         *bufptr++ = '4';
1445                 } else if (strchr("JjKkLl5", *dataptr)) {
1446                         *bufptr++ = '5';
1447                 } else if (strchr("MmNnOo6", *dataptr)) {
1448                         *bufptr++ = '6';
1449                 } else if (strchr("PpQqRrSs7", *dataptr)) {
1450                         *bufptr++ = '7';
1451                 } else if (strchr("TtUuVv8", *dataptr)) {
1452                         *bufptr++ = '8';
1453                 } else if (strchr("WwXxYyZz9", *dataptr)) {
1454                         *bufptr++ = '9';
1455                 } else if (*dataptr == '0') {
1456                         *bufptr++ = '0';
1457                 }
1458         }
1459         buf[buflen - 1] = '\0';
1460
1461         return 0;
1462 }
1463
1464 static struct ast_custom_function keypadhash_function = {
1465         .name = "KEYPADHASH",
1466         .read = keypadhash,
1467 };
1468
1469 static int string_toupper(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
1470 {
1471         char *bufptr = buf, *dataptr = data;
1472
1473         while ((bufptr < buf + buflen - 1) && (*bufptr++ = toupper(*dataptr++)));
1474
1475         return 0;
1476 }
1477
1478 static int string_toupper2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t buflen)
1479 {
1480         char *bufptr, *dataptr = data;
1481
1482         if (buflen > -1) {
1483                 ast_str_make_space(buf, buflen > 0 ? buflen : strlen(data) + 1);
1484         }
1485         bufptr = ast_str_buffer(*buf);
1486         while ((bufptr < ast_str_buffer(*buf) + ast_str_size(*buf) - 1) && (*bufptr++ = toupper(*dataptr++)));
1487         ast_str_update(*buf);
1488
1489         return 0;
1490 }
1491
1492 static struct ast_custom_function toupper_function = {
1493         .name = "TOUPPER",
1494         .read = string_toupper,
1495         .read2 = string_toupper2,
1496 };
1497
1498 static int string_tolower(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
1499 {
1500         char *bufptr = buf, *dataptr = data;
1501
1502         while ((bufptr < buf + buflen - 1) && (*bufptr++ = tolower(*dataptr++)));
1503
1504         return 0;
1505 }
1506
1507 static int string_tolower2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t buflen)
1508 {
1509         char *bufptr, *dataptr = data;
1510
1511         if (buflen > -1) {
1512                 ast_str_make_space(buf, buflen > 0 ? buflen : strlen(data) + 1);
1513         }
1514         bufptr = ast_str_buffer(*buf);
1515         while ((bufptr < ast_str_buffer(*buf) + ast_str_size(*buf) - 1) && (*bufptr++ = tolower(*dataptr++)));
1516         ast_str_update(*buf);
1517
1518         return 0;
1519 }
1520
1521 static struct ast_custom_function tolower_function = {
1522         .name = "TOLOWER",
1523         .read = string_tolower,
1524         .read2 = string_tolower2,
1525 };
1526
1527 static int shift_pop(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
1528 {
1529 #define beginning       (cmd[0] == 'S') /* SHIFT */
1530         char *after, delimiter[2] = ",", *varsubst;
1531         size_t unused;
1532         struct ast_str *before = ast_str_thread_get(&result_buf, 16);
1533         char *(*search_func)(const char *s, int c) = (beginning ? strchr : strrchr);
1534         AST_DECLARE_APP_ARGS(args,
1535                 AST_APP_ARG(var);
1536                 AST_APP_ARG(delimiter);
1537         );
1538
1539         if (!before) {
1540                 return -1;
1541         }
1542
1543         AST_STANDARD_APP_ARGS(args, data);
1544
1545         if (ast_strlen_zero(args.var)) {
1546                 ast_log(LOG_WARNING, "%s requires a variable name\n", cmd);
1547                 return -1;
1548         }
1549
1550         varsubst = alloca(strlen(args.var) + 4);
1551         sprintf(varsubst, "${%s}", args.var);
1552         ast_str_substitute_variables(&before, 0, chan, varsubst);
1553
1554         if (args.argc > 1 && !ast_strlen_zero(args.delimiter)) {
1555                 ast_get_encoded_char(args.delimiter, delimiter, &unused);
1556         }
1557
1558         if (!ast_str_strlen(before)) {
1559                 /* Nothing to pop */
1560                 return -1;
1561         }
1562
1563         if (!(after = search_func(ast_str_buffer(before), delimiter[0]))) {
1564                 /* Only one entry in array */
1565                 ast_str_set(buf, len, "%s", ast_str_buffer(before));
1566                 pbx_builtin_setvar_helper(chan, args.var, "");
1567         } else {
1568                 *after++ = '\0';
1569                 ast_str_set(buf, len, "%s", beginning ? ast_str_buffer(before) : after);
1570                 pbx_builtin_setvar_helper(chan, args.var, beginning ? after : ast_str_buffer(before));
1571         }
1572
1573         return 0;
1574 #undef beginning
1575 }
1576
1577 static struct ast_custom_function shift_function = {
1578         .name = "SHIFT",
1579         .read2 = shift_pop,
1580 };
1581
1582 static struct ast_custom_function pop_function = {
1583         .name = "POP",
1584         .read2 = shift_pop,
1585 };
1586
1587 static int unshift_push(struct ast_channel *chan, const char *cmd, char *data, const char *new_value)
1588 {
1589 #define beginning       (cmd[0] == 'U') /* UNSHIFT */
1590         char delimiter[2] = ",", *varsubst;
1591         size_t unused;
1592         struct ast_str *buf, *previous_value;
1593         AST_DECLARE_APP_ARGS(args,
1594                 AST_APP_ARG(var);
1595                 AST_APP_ARG(delimiter);
1596         );
1597
1598         if (!(buf = ast_str_thread_get(&result_buf, 16)) ||
1599                 !(previous_value = ast_str_thread_get(&tmp_buf, 16))) {
1600                 return -1;
1601         }
1602
1603         AST_STANDARD_APP_ARGS(args, data);
1604
1605         if (ast_strlen_zero(args.var)) {
1606                 ast_log(LOG_WARNING, "%s requires a variable name\n", cmd);
1607                 return -1;
1608         }
1609
1610         if (args.argc > 1 && !ast_strlen_zero(args.delimiter)) {
1611                 ast_get_encoded_char(args.delimiter, delimiter, &unused);
1612         }
1613
1614         varsubst = alloca(strlen(args.var) + 4);
1615         sprintf(varsubst, "${%s}", args.var);
1616         ast_str_substitute_variables(&previous_value, 0, chan, varsubst);
1617
1618         if (!ast_str_strlen(previous_value)) {
1619                 ast_str_set(&buf, 0, "%s", new_value);
1620         } else {
1621                 ast_str_set(&buf, 0, "%s%c%s",
1622                         beginning ? new_value : ast_str_buffer(previous_value),
1623                         delimiter[0],
1624                         beginning ? ast_str_buffer(previous_value) : new_value);
1625         }
1626
1627         pbx_builtin_setvar_helper(chan, args.var, ast_str_buffer(buf));
1628
1629         return 0;
1630 #undef beginning
1631 }
1632
1633 static struct ast_custom_function push_function = {
1634         .name = "PUSH",
1635         .write = unshift_push,
1636 };
1637
1638 static struct ast_custom_function unshift_function = {
1639         .name = "UNSHIFT",
1640         .write = unshift_push,
1641 };
1642
1643 static int passthru(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
1644 {
1645         ast_str_set(buf, len, "%s", data);
1646         return 0;
1647 }
1648
1649 static struct ast_custom_function passthru_function = {
1650         .name = "PASSTHRU",
1651         .read2 = passthru,
1652 };
1653
1654 #ifdef TEST_FRAMEWORK
1655 AST_TEST_DEFINE(test_FIELDNUM)
1656 {
1657         int i, res = AST_TEST_PASS;
1658         struct ast_channel *chan;
1659         struct ast_str *str;
1660         char expression[256];
1661         struct {
1662                 const char *fields;
1663                 const char *delim;
1664                 const char *field;
1665                 const char *expected;
1666         } test_args[] = {
1667                 {"abc,def,ghi,jkl", "\\,",     "ghi", "3"},
1668                 {"abc def ghi jkl", " ",       "abc", "1"},
1669                 {"abc/def/ghi/jkl", "\\\\x2f", "def", "2"},
1670                 {"abc$def$ghi$jkl", "",        "ghi", "0"},
1671                 {"abc,def,ghi,jkl", "-",       "",    "0"},
1672                 {"abc-def-ghi-jkl", "-",       "mno", "0"}
1673         };
1674
1675         switch (cmd) {
1676         case TEST_INIT:
1677                 info->name = "func_FIELDNUM_test";
1678                 info->category = "/funcs/func_strings/";
1679                 info->summary = "Test FIELDNUM function";
1680                 info->description = "Verify FIELDNUM behavior";
1681                 return AST_TEST_NOT_RUN;
1682         case TEST_EXECUTE:
1683                 break;
1684         }
1685
1686         if (!(chan = ast_dummy_channel_alloc())) {
1687                 ast_test_status_update(test, "Unable to allocate dummy channel\n");
1688                 return AST_TEST_FAIL;
1689         }
1690
1691         if (!(str = ast_str_create(16))) {
1692                 ast_test_status_update(test, "Unable to allocate dynamic string buffer\n");
1693                 ast_channel_release(chan);
1694                 return AST_TEST_FAIL;
1695         }
1696
1697         for (i = 0; i < ARRAY_LEN(test_args); i++) {
1698                 struct ast_var_t *var = ast_var_assign("FIELDS", test_args[i].fields);
1699                 AST_LIST_INSERT_HEAD(&chan->varshead, var, entries);
1700
1701                 snprintf(expression, sizeof(expression), "${FIELDNUM(%s,%s,%s)}", var->name, test_args[i].delim, test_args[i].field);
1702                 ast_str_substitute_variables(&str, 0, chan, expression);
1703
1704                 AST_LIST_REMOVE(&chan->varshead, var, entries);
1705                 ast_var_delete(var);
1706
1707                 if (strcasecmp(ast_str_buffer(str), test_args[i].expected)) {
1708                         ast_test_status_update(test, "Evaluation of '%s' returned '%s' instead of the expected value '%s'\n",
1709                                 expression, ast_str_buffer(str), test_args[i].expected);
1710                         res = AST_TEST_FAIL;
1711                         break;
1712                 }
1713         }
1714
1715         ast_free(str);
1716         ast_channel_release(chan);
1717
1718         return res;
1719 }
1720
1721 AST_TEST_DEFINE(test_FILTER)
1722 {
1723         int i, res = AST_TEST_PASS;
1724         const char *test_strings[][2] = {
1725                 {"A-R",            "DAHDI"},
1726                 {"A\\-R",          "A"},
1727                 {"\\x41-R",        "DAHDI"},
1728                 {"0-9A-Ca-c",      "0042133333A12212"},
1729                 {"0-9a-cA-C_+\\-", "0042133333A12212"},
1730                 {NULL,             NULL},
1731         };
1732
1733         switch (cmd) {
1734         case TEST_INIT:
1735                 info->name = "func_FILTER_test";
1736                 info->category = "/funcs/func_strings/";
1737                 info->summary = "Test FILTER function";
1738                 info->description = "Verify FILTER behavior";
1739                 return AST_TEST_NOT_RUN;
1740         case TEST_EXECUTE:
1741                 break;
1742         }
1743
1744         for (i = 0; test_strings[i][0]; i++) {
1745                 char tmp[256], tmp2[256] = "";
1746                 snprintf(tmp, sizeof(tmp), "${FILTER(%s,0042133333&DAHDI/g1/2212)}", test_strings[i][0]);
1747                 pbx_substitute_variables_helper(NULL, tmp, tmp2, sizeof(tmp2) - 1);
1748                 if (strcmp(test_strings[i][1], tmp2)) {
1749                         ast_test_status_update(test, "Format string '%s' substituted to '%s'.  Expected '%s'.\n", test_strings[i][0], tmp2, test_strings[i][1]);
1750                         res = AST_TEST_FAIL;
1751                 }
1752         }
1753         return res;
1754 }
1755
1756 AST_TEST_DEFINE(test_STRREPLACE)
1757 {
1758         int i, res = AST_TEST_PASS;
1759         struct ast_channel *chan; /* dummy channel */
1760         struct ast_str *str; /* fancy string for holding comparing value */
1761
1762         const char *test_strings[][5] = {
1763                 {"Weasels have eaten my telephone system", "have eaten my", "are eating our", "", "Weasels are eating our telephone system"}, /*Test normal conditions */
1764                 {"Did you know twenty plus two is twenty-two?", "twenty", "thirty", NULL, "Did you know thirty plus two is thirty-two?"}, /* Test no third comma */
1765                 {"My pet dog once ate a dog who sat on a dog while eating a corndog.", "dog", "cat", "3", "My pet cat once ate a cat who sat on a cat while eating a corndog."},
1766                 {"One and one and one is three", "and", "plus", "1", "One plus one and one is three"}, /* Test <max-replacements> = 1*/
1767                 {"", "fhqwagads", "spelunker", NULL, ""}, /* Empty primary string */
1768                 {"Part of this string is missing.", "missing", NULL, NULL, "Part of this string is ."}, /* Empty replace string */
1769                 {"'Accidentally' left off a bunch of stuff.", NULL, NULL, NULL, ""}, /* Deliberate error test from too few args */
1770                 {"This test will also error.", "", "", "", ""}, /* Deliberate error test from blank find string */
1771                 {"This is an \"escape character\" test.", "\\\"escape character\\\"", "evil", NULL, "This is an evil test."}
1772         };
1773
1774         switch (cmd) {
1775         case TEST_INIT:
1776                 info->name = "func_STRREPLACE_test";
1777                 info->category = "/funcs/func_strings/";
1778                 info->summary = "Test STRREPLACE function";
1779                 info->description = "Verify STRREPLACE behavior";
1780                 return AST_TEST_NOT_RUN;
1781         case TEST_EXECUTE:
1782                 break;
1783         }
1784
1785         if (!(chan = ast_dummy_channel_alloc())) {
1786                 ast_test_status_update(test, "Unable to allocate dummy channel\n");
1787                 return AST_TEST_FAIL;
1788         }
1789
1790         if (!(str = ast_str_create(64))) {
1791                 ast_test_status_update(test, "Unable to allocate dynamic string buffer\n");
1792                 ast_channel_release(chan);
1793                 return AST_TEST_FAIL;
1794         }
1795
1796         for (i = 0; i < ARRAY_LEN(test_strings); i++) {
1797                 char tmp[512], tmp2[512] = "";
1798
1799                 struct ast_var_t *var = ast_var_assign("test_string", test_strings[i][0]);
1800                 AST_LIST_INSERT_HEAD(&chan->varshead, var, entries);
1801
1802                 if (test_strings[i][3]) {
1803                         snprintf(tmp, sizeof(tmp), "${STRREPLACE(%s,%s,%s,%s)}", "test_string", test_strings[i][1], test_strings[i][2], test_strings[i][3]);
1804                 } else if (test_strings[i][2]) {
1805                         snprintf(tmp, sizeof(tmp), "${STRREPLACE(%s,%s,%s)}", "test_string", test_strings[i][1], test_strings[i][2]);
1806                 } else if (test_strings[i][1]) {
1807                         snprintf(tmp, sizeof(tmp), "${STRREPLACE(%s,%s)}", "test_string", test_strings[i][1]);
1808                 } else {
1809                         snprintf(tmp, sizeof(tmp), "${STRREPLACE(%s)}", "test_string");
1810                 }
1811                 ast_str_substitute_variables(&str, 0, chan, tmp);
1812                 if (strcmp(test_strings[i][4], ast_str_buffer(str))) {
1813                         ast_test_status_update(test, "Format string '%s' substituted to '%s'.  Expected '%s'.\n", test_strings[i][0], tmp2, test_strings[i][4]);
1814                         res = AST_TEST_FAIL;
1815                 }
1816         }
1817
1818         ast_free(str);
1819         ast_channel_release(chan);
1820
1821         return res;
1822 }
1823 #endif
1824
1825 static int unload_module(void)
1826 {
1827         int res = 0;
1828
1829         AST_TEST_UNREGISTER(test_FIELDNUM);
1830         AST_TEST_UNREGISTER(test_FILTER);
1831         AST_TEST_UNREGISTER(test_STRREPLACE);
1832         res |= ast_custom_function_unregister(&fieldqty_function);
1833         res |= ast_custom_function_unregister(&fieldnum_function);
1834         res |= ast_custom_function_unregister(&filter_function);
1835         res |= ast_custom_function_unregister(&replace_function);
1836         res |= ast_custom_function_unregister(&strreplace_function);
1837         res |= ast_custom_function_unregister(&listfilter_function);
1838         res |= ast_custom_function_unregister(&regex_function);
1839         res |= ast_custom_function_unregister(&array_function);
1840         res |= ast_custom_function_unregister(&quote_function);
1841         res |= ast_custom_function_unregister(&csv_quote_function);
1842         res |= ast_custom_function_unregister(&len_function);
1843         res |= ast_custom_function_unregister(&strftime_function);
1844         res |= ast_custom_function_unregister(&strptime_function);
1845         res |= ast_custom_function_unregister(&eval_function);
1846         res |= ast_custom_function_unregister(&keypadhash_function);
1847         res |= ast_custom_function_unregister(&hashkeys_function);
1848         res |= ast_custom_function_unregister(&hash_function);
1849         res |= ast_unregister_application(app_clearhash);
1850         res |= ast_custom_function_unregister(&toupper_function);
1851         res |= ast_custom_function_unregister(&tolower_function);
1852         res |= ast_custom_function_unregister(&shift_function);
1853         res |= ast_custom_function_unregister(&pop_function);
1854         res |= ast_custom_function_unregister(&push_function);
1855         res |= ast_custom_function_unregister(&unshift_function);
1856         res |= ast_custom_function_unregister(&passthru_function);
1857
1858         return res;
1859 }
1860
1861 static int load_module(void)
1862 {
1863         int res = 0;
1864
1865         AST_TEST_REGISTER(test_FIELDNUM);
1866         AST_TEST_REGISTER(test_FILTER);
1867         AST_TEST_REGISTER(test_STRREPLACE);
1868         res |= ast_custom_function_register(&fieldqty_function);
1869         res |= ast_custom_function_register(&fieldnum_function);
1870         res |= ast_custom_function_register(&filter_function);
1871         res |= ast_custom_function_register(&replace_function);
1872         res |= ast_custom_function_register(&strreplace_function);
1873         res |= ast_custom_function_register(&listfilter_function);
1874         res |= ast_custom_function_register(&regex_function);
1875         res |= ast_custom_function_register(&array_function);
1876         res |= ast_custom_function_register(&quote_function);
1877         res |= ast_custom_function_register(&csv_quote_function);
1878         res |= ast_custom_function_register(&len_function);
1879         res |= ast_custom_function_register(&strftime_function);
1880         res |= ast_custom_function_register(&strptime_function);
1881         res |= ast_custom_function_register(&eval_function);
1882         res |= ast_custom_function_register(&keypadhash_function);
1883         res |= ast_custom_function_register(&hashkeys_function);
1884         res |= ast_custom_function_register(&hash_function);
1885         res |= ast_register_application_xml(app_clearhash, exec_clearhash);
1886         res |= ast_custom_function_register(&toupper_function);
1887         res |= ast_custom_function_register(&tolower_function);
1888         res |= ast_custom_function_register(&shift_function);
1889         res |= ast_custom_function_register(&pop_function);
1890         res |= ast_custom_function_register(&push_function);
1891         res |= ast_custom_function_register(&unshift_function);
1892         res |= ast_custom_function_register(&passthru_function);
1893
1894         return res;
1895 }
1896
1897 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "String handling dialplan functions");