Asterisk - The Open Source Telephony Project  21.4.1
func_strings.c
Go to the documentation of this file.
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  * Portions Copyright (C) 2021, 2022, Naveen Albert
8  *
9  * See http://www.asterisk.org for more information about
10  * the Asterisk project. Please do not directly contact
11  * any of the maintainers of this project for assistance;
12  * the project provides a web site, mailing lists and IRC
13  * channels for your use.
14  *
15  * This program is free software, distributed under the terms of
16  * the GNU General Public License Version 2. See the LICENSE file
17  * at the top of the source tree.
18  */
19 
20 /*! \file
21  *
22  * \brief String manipulation dialplan functions
23  *
24  * \author Tilghman Lesher
25  * \author Anthony Minessale II
26  * \author Naveen Albert
27  * \ingroup functions
28  */
29 
30 /*** MODULEINFO
31  <support_level>core</support_level>
32  ***/
33 
34 #include "asterisk.h"
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  <example title="Prints 3">
66  exten => s,1,Set(example=ex-amp-le)
67  same => n,NoOp(${FIELDQTY(example,-)})
68  </example>
69  </description>
70  </function>
71  <function name="FIELDNUM" language="en_US">
72  <synopsis>
73  Return the 1-based offset of a field in a list
74  </synopsis>
75  <syntax>
76  <parameter name="varname" required="true" />
77  <parameter name="delim" required="true" />
78  <parameter name="value" required="true" />
79  </syntax>
80  <description>
81  <para>Search the variable named <replaceable>varname</replaceable> for the string <replaceable>value</replaceable>
82  delimited by <replaceable>delim</replaceable> and return a 1-based offset as to its location. If not found
83  or an error occured, return <literal>0</literal>.</para>
84  <para>The delimiter may be specified as a special or extended ASCII character, by encoding it. The characters
85  <literal>\n</literal>, <literal>\r</literal>, and <literal>\t</literal> are all recognized as the newline,
86  carriage return, and tab characters, respectively. Also, octal and hexadecimal specifications are recognized
87  by the patterns <literal>\0nnn</literal> and <literal>\xHH</literal>, respectively. For example, if you wanted
88  to encode a comma as the delimiter, you could use either <literal>\054</literal> or <literal>\x2C</literal>.</para>
89  <example title="Prints 2">
90  exten => s,1,Set(example=ex-amp-le)
91  same => n,NoOp(${FIELDNUM(example,-,amp)})
92  </example>
93  </description>
94  </function>
95  <function name="LISTFILTER" language="en_US">
96  <synopsis>Remove an item from a list, by name.</synopsis>
97  <syntax>
98  <parameter name="varname" required="true" />
99  <parameter name="delim" required="true" default="," />
100  <parameter name="value" required="true" />
101  </syntax>
102  <description>
103  <para>Remove <replaceable>value</replaceable> from the list contained in the <replaceable>varname</replaceable>
104  variable, where the list delimiter is specified by the <replaceable>delim</replaceable> parameter. This is
105  very useful for removing a single channel name from a list of channels, for example.</para>
106  </description>
107  </function>
108  <function name="FILTER" language="en_US">
109  <synopsis>
110  Filter the string to include only the allowed characters
111  </synopsis>
112  <syntax>
113  <parameter name="allowed-chars" required="true" />
114  <parameter name="string" required="true" />
115  </syntax>
116  <description>
117  <para>Permits all characters listed in <replaceable>allowed-chars</replaceable>,
118  filtering all others outs. In addition to literally listing the characters,
119  you may also use ranges of characters (delimited by a <literal>-</literal></para>
120  <para>Hexadecimal characters started with a <literal>\x</literal>(i.e. \x20)</para>
121  <para>Octal characters started with a <literal>\0</literal> (i.e. \040)</para>
122  <para>Also <literal>\t</literal>,<literal>\n</literal> and <literal>\r</literal> are recognized.</para>
123  <note><para>If you want the <literal>-</literal> character it needs to be prefixed with a
124  <literal></literal></para></note>
125  </description>
126  </function>
127  <function name="REPLACE" language="en_US">
128  <synopsis>
129  Replace a set of characters in a given string with another character.
130  </synopsis>
131  <syntax>
132  <parameter name="varname" required="true" />
133  <parameter name="find-chars" required="true" />
134  <parameter name="replace-char" required="false" />
135  </syntax>
136  <description>
137  <para>Iterates through a string replacing all the <replaceable>find-chars</replaceable> with
138  <replaceable>replace-char</replaceable>. <replaceable>replace-char</replaceable> may be either
139  empty or contain one character. If empty, all <replaceable>find-chars</replaceable> will be
140  deleted from the output.</para>
141  <note><para>The replacement only occurs in the output. The original variable is not
142  altered.</para></note>
143  </description>
144  </function>
145  <function name="STRREPLACE" language="en_US">
146  <synopsis>
147  Replace instances of a substring within a string with another string.
148  </synopsis>
149  <syntax>
150  <parameter name="varname" required="true" />
151  <parameter name="find-string" required="true" />
152  <parameter name="replace-string" required="false" />
153  <parameter name="max-replacements" required="false" />
154  </syntax>
155  <description>
156  <para>Searches for all instances of the <replaceable>find-string</replaceable> in provided variable and
157  replaces them with <replaceable>replace-string</replaceable>. If <replaceable>replace-string</replaceable>
158  is an empty string, this will effectively delete that substring. If <replaceable>max-replacements</replaceable>
159  is specified, this function will stop after performing replacements <replaceable>max-replacements</replaceable> times.</para>
160  <note><para>The replacement only occurs in the output. The original variable is not altered.</para></note>
161  </description>
162  </function>
163  <function name="STRBETWEEN" language="en_US">
164  <since>
165  <version>16.21.0</version>
166  <version>18.7.0</version>
167  <version>19.0.0</version>
168  </since>
169  <synopsis>
170  Inserts a substring between each character in a string.
171  </synopsis>
172  <syntax>
173  <parameter name="varname" required="true" />
174  <parameter name="insert-string" required="true" />
175  </syntax>
176  <description>
177  <para>Inserts a substring <replaceable>find-string</replaceable> between each character in
178  <replaceable>varname</replaceable>.</para>
179  <note><para>The replacement only occurs in the output. The original variable is not altered.</para></note>
180  <example title="Add half-second pause between dialed digits">
181  same => n,Set(digits=5551212)
182  same => n,SendDTMF(${STRBETWEEN(digits,w)) ; this will send 5w5w5w1w2w1w2
183  </example>
184  </description>
185  </function>
186  <function name="TRIM" language="en_US">
187  <synopsis>
188  Trim leading and trailing whitespace in a string
189  </synopsis>
190  <syntax>
191  <parameter name="string" required="true" />
192  </syntax>
193  <description>
194  <para>Replaces all leading and trailing whitespace in the provided string.</para>
195  </description>
196  <see-also>
197  <ref type="function">LTRIM</ref>
198  <ref type="function">RTRIM</ref>
199  </see-also>
200  </function>
201  <function name="LTRIM" language="en_US">
202  <synopsis>
203  Trim leading whitespace in a string
204  </synopsis>
205  <syntax>
206  <parameter name="string" required="true" />
207  </syntax>
208  <description>
209  <para>Replaces all leading whitespace in the provided string.</para>
210  </description>
211  <see-also>
212  <ref type="function">TRIM</ref>
213  <ref type="function">RTRIM</ref>
214  </see-also>
215  </function>
216  <function name="RTRIM" language="en_US">
217  <synopsis>
218  Trim trailing whitespace in a string
219  </synopsis>
220  <syntax>
221  <parameter name="string" required="true" />
222  </syntax>
223  <description>
224  <para>Replaces all trailing whitespace in the provided string.</para>
225  </description>
226  <see-also>
227  <ref type="function">TRIM</ref>
228  <ref type="function">LTRIM</ref>
229  </see-also>
230  </function>
231  <function name="PASSTHRU" language="en_US">
232  <synopsis>
233  Pass the given argument back as a value.
234  </synopsis>
235  <syntax>
236  <parameter name="string" required="false" />
237  </syntax>
238  <description>
239  <para>Literally returns the given <replaceable>string</replaceable>. The intent is to permit
240  other dialplan functions which take a variable name as an argument to be able to take a literal
241  string, instead.</para>
242  <note><para>The functions which take a variable name need to be passed var and not
243  ${var}. Similarly, use PASSTHRU() and not ${PASSTHRU()}.</para></note>
244  <example title="Prints 321">
245  exten => s,1,NoOp(${CHANNEL}) ; contains SIP/321-1
246  same => n,NoOp(${CUT(PASSTHRU(${CUT(CHANNEL,-,1)}),/,2)})
247  </example>
248  </description>
249  </function>
250  <function name="REGEX" language="en_US">
251  <synopsis>
252  Check string against a regular expression.
253  </synopsis>
254  <syntax argsep=" ">
255  <parameter name="&quot;regular expression&quot;" required="true" />
256  <parameter name="string" required="true" />
257  </syntax>
258  <description>
259  <para>Return <literal>1</literal> on regular expression match or <literal>0</literal> otherwise</para>
260  <para>Please note that the space following the double quotes separating the
261  regex from the data is optional and if present, is skipped. If a space is
262  desired at the beginning of the data, then put two spaces there; the second
263  will not be skipped.</para>
264  </description>
265  </function>
266  <application name="ClearHash" language="en_US">
267  <synopsis>
268  Clear the keys from a specified hashname.
269  </synopsis>
270  <syntax>
271  <parameter name="hashname" required="true" />
272  </syntax>
273  <description>
274  <para>Clears all keys out of the specified <replaceable>hashname</replaceable>.</para>
275  </description>
276  </application>
277  <function name="HASH" language="en_US">
278  <synopsis>
279  Implementation of a dialplan associative array
280  </synopsis>
281  <syntax>
282  <parameter name="hashname" required="true" />
283  <parameter name="hashkey" />
284  </syntax>
285  <description>
286  <para>In two arguments mode, gets and sets values to corresponding keys within
287  a named associative array. The single-argument mode will only work when assigned
288  to from a function defined by func_odbc</para>
289  </description>
290  </function>
291  <function name="HASHKEYS" language="en_US">
292  <synopsis>
293  Retrieve the keys of the HASH() function.
294  </synopsis>
295  <syntax>
296  <parameter name="hashname" required="true" />
297  </syntax>
298  <description>
299  <para>Returns a comma-delimited list of the current keys of the associative array
300  defined by the HASH() function. Note that if you iterate over the keys of
301  the result, adding keys during iteration will cause the result of the HASHKEYS()
302  function to change.</para>
303  </description>
304  </function>
305  <function name="KEYPADHASH" language="en_US">
306  <synopsis>
307  Hash the letters in string into equivalent keypad numbers.
308  </synopsis>
309  <syntax>
310  <parameter name="string" required="true" />
311  </syntax>
312  <description>
313  <example title="Returns 537">
314  exten => s,1,Return(${KEYPADHASH(Les)})
315  </example>
316  </description>
317  </function>
318  <function name="ARRAY" language="en_US">
319  <synopsis>
320  Allows setting multiple variables at once.
321  </synopsis>
322  <syntax>
323  <parameter name="var1" required="true" />
324  <parameter name="var2" required="false" multiple="true" />
325  <parameter name="varN" required="false" />
326  </syntax>
327  <description>
328  <para>The comma-delimited list passed as a value to which the function is set will
329  be interpreted as a set of values to which the comma-delimited list of
330  variable names in the argument should be set.</para>
331  <example title="Set var1 to 1 and var2 to 2">
332  same => n,Set(ARRAY(var1,var2)=1,2)
333  </example>
334  </description>
335  </function>
336  <function name="STRPTIME" language="en_US">
337  <synopsis>
338  Returns the epoch of the arbitrary date/time string structured as described by the format.
339  </synopsis>
340  <syntax>
341  <parameter name="datetime" required="true" />
342  <parameter name="timezone" required="true" />
343  <parameter name="format" required="true" />
344  </syntax>
345  <description>
346  <para>This is useful for converting a date into <literal>EPOCH</literal> time,
347  possibly to pass to an application like SayUnixTime or to calculate the difference
348  between the two date strings</para>
349  <example title="Prints 1141219835">
350  same => n,NoOp(${STRPTIME(2006-03-01 07:30:35,America/Chicago,%Y-%m-%d %H:%M:%S)})
351  </example>
352  </description>
353  </function>
354  <function name="STRFTIME" language="en_US">
355  <synopsis>
356  Returns the current date/time in the specified format.
357  </synopsis>
358  <syntax>
359  <parameter name="epoch" />
360  <parameter name="timezone" />
361  <parameter name="format" />
362  </syntax>
363  <description>
364  <para>STRFTIME supports all of the same formats as the underlying C function
365  <emphasis>strftime(3)</emphasis>.
366  It also supports the following format: <literal>%[n]q</literal> - fractions of a second,
367  with leading zeros.</para>
368  <para>Example: <literal>%3q</literal> will give milliseconds and <literal>%1q</literal>
369  will give tenths of a second. The default is set at milliseconds (n=3).
370  The common case is to use it in combination with %S, as in <literal>%S.%3q</literal>.</para>
371  </description>
372  <see-also>
373  <ref type="manpage">strftime(3)</ref>
374  </see-also>
375  </function>
376  <function name="EVAL" language="en_US">
377  <synopsis>
378  Evaluate stored variables
379  </synopsis>
380  <syntax>
381  <parameter name="variable" required="true" />
382  </syntax>
383  <description>
384  <para>Using EVAL basically causes a string to be evaluated twice.
385  When a variable or expression is in the dialplan, it will be
386  evaluated at runtime. However, if the results of the evaluation
387  is in fact another variable or expression, using EVAL will have it
388  evaluated a second time.</para>
389  <para>Example: If the <variable>MYVAR</variable> contains
390  <variable>OTHERVAR</variable>, then the result of ${EVAL(
391  <variable>MYVAR</variable>)} in the dialplan will be the
392  contents of <variable>OTHERVAR</variable>. Normally just
393  putting <variable>MYVAR</variable> in the dialplan the result
394  would be <variable>OTHERVAR</variable>.</para>
395  </description>
396  </function>
397  <function name="TOUPPER" language="en_US">
398  <synopsis>
399  Convert string to all uppercase letters.
400  </synopsis>
401  <syntax>
402  <parameter name="string" required="true" />
403  </syntax>
404  <description>
405  <example title="Prints EXAMPLE">
406  exten => s,1,NoOp(${TOUPPER(Example)})
407  </example>
408  </description>
409  </function>
410  <function name="TOLOWER" language="en_US">
411  <synopsis>
412  Convert string to all lowercase letters.
413  </synopsis>
414  <syntax>
415  <parameter name="string" required="true" />
416  </syntax>
417  <description>
418  <example title="Prints example">
419  exten => s,1,NoOp(${TOLOWER(Example)})
420  </example>
421  </description>
422  </function>
423  <function name="LEN" language="en_US">
424  <synopsis>
425  Return the length of the string given.
426  </synopsis>
427  <syntax>
428  <parameter name="string" required="true" />
429  </syntax>
430  <description>
431  <example title="Prints 7">
432  exten => s,1,NoOp(${LEN(example)})
433  </example>
434  </description>
435  </function>
436  <function name="QUOTE" language="en_US">
437  <synopsis>
438  Quotes a given string, escaping embedded quotes as necessary
439  </synopsis>
440  <syntax>
441  <parameter name="string" required="true" />
442  </syntax>
443  <description>
444  <para>Example: ${QUOTE(ab"c"de)} will return ""ab\"c\"de""</para>
445  </description>
446  </function>
447  <function name="CSV_QUOTE" language="en_US">
448  <synopsis>
449  Quotes a given string for use in a CSV file, escaping embedded quotes as necessary
450  </synopsis>
451  <syntax>
452  <parameter name="string" required="true" />
453  </syntax>
454  <description>
455  <para>Example: ${CSV_QUOTE("a,b" 123)} will return """a,b"" 123"</para>
456  </description>
457  </function>
458  <function name="SHIFT" language="en_US">
459  <synopsis>
460  Removes and returns the first item off of a variable containing delimited text
461  </synopsis>
462  <syntax>
463  <parameter name="varname" required="true" />
464  <parameter name="delimiter" required="false" default="," />
465  </syntax>
466  <description>
467  <example title="SHIFT example">
468  exten => s,1,Set(array=one,two,three)
469  exten => s,n,While($["${SET(var=${SHIFT(array)})}" != ""])
470  exten => s,n,NoOp(var is ${var})
471  exten => s,n,EndWhile
472  </example>
473  <para>This would iterate over each value in array, left to right, and
474  would result in NoOp(var is one), NoOp(var is two), and
475  NoOp(var is three) being executed.
476  </para>
477  </description>
478  </function>
479  <function name="POP" language="en_US">
480  <synopsis>
481  Removes and returns the last item off of a variable containing delimited text
482  </synopsis>
483  <syntax>
484  <parameter name="varname" required="true" />
485  <parameter name="delimiter" required="false" default="," />
486  </syntax>
487  <description>
488  <example title="POP example">
489  exten => s,1,Set(array=one,two,three)
490  exten => s,n,While($["${SET(var=${POP(array)})}" != ""])
491  exten => s,n,NoOp(var is ${var})
492  exten => s,n,EndWhile
493  </example>
494  <para>This would iterate over each value in array, right to left, and
495  would result in NoOp(var is three), NoOp(var is two), and
496  NoOp(var is one) being executed.
497  </para>
498  </description>
499  </function>
500  <function name="PUSH" language="en_US">
501  <synopsis>
502  Appends one or more values to the end of a variable containing delimited text
503  </synopsis>
504  <syntax>
505  <parameter name="varname" required="true" />
506  <parameter name="delimiter" required="false" default="," />
507  </syntax>
508  <description>
509  <example title="PUSH example">
510  exten => s,1,Set(PUSH(array)=one,two,three)
511  </example>
512  <para>This would append one,
513  two, and three to the end of the values stored in the variable
514  "array".
515  </para>
516  </description>
517  </function>
518  <function name="UNSHIFT" language="en_US">
519  <synopsis>
520  Inserts one or more values to the beginning of a variable containing delimited text
521  </synopsis>
522  <syntax>
523  <parameter name="varname" required="true" />
524  <parameter name="delimiter" required="false" default="," />
525  </syntax>
526  <description>
527  <example title="UNSHIFT example">
528  exten => s,1,Set(UNSHIFT(array)=one,two,three)
529  </example>
530  <para>This would insert one,
531  two, and three before the values stored in the variable
532  "array".
533  </para>
534  </description>
535  </function>
536  ***/
537 
538 static int function_fieldqty_helper(struct ast_channel *chan, const char *cmd,
539  char *parse, char *buf, struct ast_str **sbuf, ssize_t len)
540 {
541  char *varsubst;
542  struct ast_str *str = ast_str_thread_get(&result_buf, 16);
543  int fieldcount = 0;
545  AST_APP_ARG(varname);
546  AST_APP_ARG(delim);
547  );
548  char delim[2] = "";
549  size_t delim_used;
550 
551  if (!str) {
552  return -1;
553  }
554 
555  AST_STANDARD_APP_ARGS(args, parse);
556  if (args.delim) {
557  ast_get_encoded_char(args.delim, delim, &delim_used);
558 
559  varsubst = ast_alloca(strlen(args.varname) + 4);
560 
561  sprintf(varsubst, "${%s}", args.varname);
562  ast_str_substitute_variables(&str, 0, chan, varsubst);
563  if (ast_str_strlen(str) == 0) {
564  fieldcount = 0;
565  } else {
566  char *varval = ast_str_buffer(str);
567  while (strsep(&varval, delim)) {
568  fieldcount++;
569  }
570  }
571  } else {
572  fieldcount = 1;
573  }
574  if (sbuf) {
575  ast_str_set(sbuf, len, "%d", fieldcount);
576  } else {
577  snprintf(buf, len, "%d", fieldcount);
578  }
579 
580  return 0;
581 }
582 
583 static int function_fieldqty(struct ast_channel *chan, const char *cmd,
584  char *parse, char *buf, size_t len)
585 {
586  return function_fieldqty_helper(chan, cmd, parse, buf, NULL, len);
587 }
588 
589 static int function_fieldqty_str(struct ast_channel *chan, const char *cmd,
590  char *parse, struct ast_str **buf, ssize_t len)
591 {
592  return function_fieldqty_helper(chan, cmd, parse, NULL, buf, len);
593 }
594 
595 static struct ast_custom_function fieldqty_function = {
596  .name = "FIELDQTY",
597  .read = function_fieldqty,
598  .read2 = function_fieldqty_str,
599 };
600 
601 static int function_fieldnum_helper(struct ast_channel *chan, const char *cmd,
602  char *parse, char *buf, struct ast_str **sbuf, ssize_t len)
603 {
604  char *varsubst, *field;
605  struct ast_str *str = ast_str_thread_get(&result_buf, 16);
606  int fieldindex = 0, res = 0;
608  AST_APP_ARG(varname);
609  AST_APP_ARG(delim);
610  AST_APP_ARG(field);
611  );
612  char delim[2] = "";
613  size_t delim_used;
614 
615  if (!str) {
616  return -1;
617  }
618 
619  AST_STANDARD_APP_ARGS(args, parse);
620 
621  if (args.argc < 3) {
622  ast_log(LOG_ERROR, "Usage: FIELDNUM(<listname>,<delimiter>,<fieldvalue>)\n");
623  res = -1;
624  } else {
625  varsubst = ast_alloca(strlen(args.varname) + 4);
626  sprintf(varsubst, "${%s}", args.varname);
627 
628  ast_str_substitute_variables(&str, 0, chan, varsubst);
629 
630  if (ast_str_strlen(str) == 0 || ast_strlen_zero(args.delim)) {
631  fieldindex = 0;
632  } else if (ast_get_encoded_char(args.delim, delim, &delim_used) == -1) {
633  res = -1;
634  } else {
635  char *varval = ast_str_buffer(str);
636 
637  while ((field = strsep(&varval, delim)) != NULL) {
638  fieldindex++;
639 
640  if (!strcasecmp(field, args.field)) {
641  break;
642  }
643  }
644 
645  if (!field) {
646  fieldindex = 0;
647  }
648 
649  res = 0;
650  }
651  }
652 
653  if (sbuf) {
654  ast_str_set(sbuf, len, "%d", fieldindex);
655  } else {
656  snprintf(buf, len, "%d", fieldindex);
657  }
658 
659  return res;
660 }
661 
662 static int function_fieldnum(struct ast_channel *chan, const char *cmd,
663  char *parse, char *buf, size_t len)
664 {
665  return function_fieldnum_helper(chan, cmd, parse, buf, NULL, len);
666 }
667 
668 static int function_fieldnum_str(struct ast_channel *chan, const char *cmd,
669  char *parse, struct ast_str **buf, ssize_t len)
670 {
671  return function_fieldnum_helper(chan, cmd, parse, NULL, buf, len);
672 }
673 
674 static struct ast_custom_function fieldnum_function = {
675  .name = "FIELDNUM",
676  .read = function_fieldnum,
677  .read2 = function_fieldnum_str,
678 };
679 
680 static int listfilter(struct ast_channel *chan, const char *cmd, char *parse, char *buf, struct ast_str **bufstr, ssize_t len)
681 {
683  AST_APP_ARG(listname);
684  AST_APP_ARG(delimiter);
685  AST_APP_ARG(fieldvalue);
686  );
687  struct ast_str *orig_list = ast_str_thread_get(&tmp_buf, 16);
688  const char *begin, *cur, *next;
689  int dlen, flen, first = 1;
690  struct ast_str *result, **result_ptr = &result;
691  char *delim, *varsubst;
692 
693  AST_STANDARD_APP_ARGS(args, parse);
694 
695  if (buf) {
696  if (!(result = ast_str_thread_get(&result_buf, 16))) {
697  return -1;
698  }
699  } else {
700  /* Place the result directly into the output buffer */
701  result_ptr = bufstr;
702  }
703 
704  if (args.argc < 3) {
705  ast_log(LOG_ERROR, "Usage: LISTFILTER(<listname>,<delimiter>,<fieldvalue>)\n");
706  return -1;
707  }
708 
709  varsubst = ast_alloca(strlen(args.listname) + 4);
710  sprintf(varsubst, "${%s}", args.listname);
711 
712  /* If we don't lock the channel, the variable could disappear out from underneath us. */
713  if (chan) {
714  ast_channel_lock(chan);
715  }
716  ast_str_substitute_variables(&orig_list, 0, chan, varsubst);
717  if (!ast_str_strlen(orig_list)) {
718  if (chan) {
719  ast_channel_unlock(chan);
720  }
721  return -1;
722  }
723 
724  /* If the string isn't there, just copy out the string and be done with it. */
725  if (!strstr(ast_str_buffer(orig_list), args.fieldvalue)) {
726  if (buf) {
727  ast_copy_string(buf, ast_str_buffer(orig_list), len);
728  } else {
729  ast_str_set(result_ptr, len, "%s", ast_str_buffer(orig_list));
730  }
731  if (chan) {
732  ast_channel_unlock(chan);
733  }
734  return 0;
735  }
736 
737  dlen = strlen(args.delimiter);
738  delim = ast_alloca(dlen + 1);
739  ast_get_encoded_str(args.delimiter, delim, dlen + 1);
740 
741  if ((dlen = strlen(delim)) == 0) {
742  delim = ",";
743  dlen = 1;
744  }
745 
746  flen = strlen(args.fieldvalue);
747 
748  ast_str_reset(*result_ptr);
749  /* Enough space for any result */
750  if (len > -1) {
751  ast_str_make_space(result_ptr, len ? len : ast_str_strlen(orig_list) + 1);
752  }
753 
754  begin = ast_str_buffer(orig_list);
755  next = strstr(begin, delim);
756 
757  do {
758  /* Find next boundary */
759  if (next) {
760  cur = next;
761  next = strstr(cur + dlen, delim);
762  } else {
763  cur = strchr(begin + dlen, '\0');
764  }
765 
766  if (flen == cur - begin && !strncmp(begin, args.fieldvalue, flen)) {
767  /* Skip field */
768  begin += flen + dlen;
769  } else {
770  /* Copy field to output */
771  if (!first) {
772  ast_str_append(result_ptr, len, "%s", delim);
773  }
774 
775  ast_str_append_substr(result_ptr, len, begin, cur - begin);
776  first = 0;
777  begin = cur + dlen;
778  }
779  } while (*cur != '\0');
780  if (chan) {
781  ast_channel_unlock(chan);
782  }
783 
784  if (buf) {
785  ast_copy_string(buf, ast_str_buffer(result), len);
786  }
787 
788  return 0;
789 }
790 
791 static int listfilter_read(struct ast_channel *chan, const char *cmd, char *parse, char *buf, size_t len)
792 {
793  return listfilter(chan, cmd, parse, buf, NULL, len);
794 }
795 
796 static int listfilter_read2(struct ast_channel *chan, const char *cmd, char *parse, struct ast_str **buf, ssize_t len)
797 {
798  return listfilter(chan, cmd, parse, NULL, buf, len);
799 }
800 
801 static struct ast_custom_function listfilter_function = {
802  .name = "LISTFILTER",
803  .read = listfilter_read,
804  .read2 = listfilter_read2,
805 };
806 
807 static int filter(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
808  size_t len)
809 {
811  AST_APP_ARG(allowed);
812  AST_APP_ARG(string);
813  );
814  char *outbuf = buf;
815  unsigned char ac;
816  char allowed[256] = "";
817  size_t allowedlen = 0;
818  int32_t bitfield[8] = { 0, }; /* 256 bits */
819 
820  AST_STANDARD_RAW_ARGS(args, parse);
821 
822  if (!args.string) {
823  ast_log(LOG_ERROR, "Usage: FILTER(<allowed-chars>,<string>)\n");
824  return -1;
825  }
826 
827  if (args.allowed[0] == '"' && !ast_opt_dont_warn) {
828  ast_log(LOG_WARNING, "FILTER allowed characters includes the quote (\") character. This may not be what you want.\n");
829  }
830 
831  /* Expand ranges */
832  for (; *(args.allowed);) {
833  char c1 = 0, c2 = 0;
834  size_t consumed = 0;
835 
836  if (ast_get_encoded_char(args.allowed, &c1, &consumed))
837  return -1;
838  args.allowed += consumed;
839 
840  if (*(args.allowed) == '-') {
841  if (ast_get_encoded_char(args.allowed + 1, &c2, &consumed))
842  c2 = c1;
843  args.allowed += consumed + 1;
844 
845  if ((unsigned char) c2 < (unsigned char) c1 && !ast_opt_dont_warn) {
846  ast_log(LOG_WARNING, "Range wrapping in FILTER(%s,%s). This may not be what you want.\n", parse, args.string);
847  }
848 
849  /*!\note
850  * Looks a little strange, until you realize that we can overflow
851  * the size of a char.
852  */
853  for (ac = (unsigned char) c1; ac != (unsigned char) c2; ac++) {
854  bitfield[ac / 32] |= 1 << (ac % 32);
855  }
856  bitfield[ac / 32] |= 1 << (ac % 32);
857 
858  ast_debug(4, "c1=%d, c2=%d\n", c1, c2);
859  } else {
860  ac = (unsigned char) c1;
861  ast_debug(4, "c1=%d, consumed=%d, args.allowed=%s\n", c1, (int) consumed, args.allowed - consumed);
862  bitfield[ac / 32] |= 1 << (ac % 32);
863  }
864  }
865 
866  for (ac = 1; ac != 0; ac++) {
867  if (bitfield[ac / 32] & (1 << (ac % 32))) {
868  allowed[allowedlen++] = ac;
869  }
870  }
871 
872  ast_debug(1, "Allowed: %s\n", allowed);
873 
874  for (; *(args.string) && (buf + len - 1 > outbuf); (args.string)++) {
875  if (strchr(allowed, *(args.string)))
876  *outbuf++ = *(args.string);
877  }
878  *outbuf = '\0';
879 
880  return 0;
881 }
882 
883 static struct ast_custom_function filter_function = {
884  .name = "FILTER",
885  .read = filter,
886 };
887 
888 static int replace(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
889 {
891  AST_APP_ARG(varname);
892  AST_APP_ARG(find);
893  AST_APP_ARG(replace);
894  );
895  char *strptr, *varsubst;
896  RAII_VAR(struct ast_str *, str, ast_str_create(16), ast_free);
897  char find[256]; /* Only 256 characters possible */
898  char replace[2] = "";
899  size_t unused;
900 
901  AST_STANDARD_APP_ARGS(args, data);
902 
903  if (!str) {
904  return -1;
905  }
906 
907  if (args.argc < 2) {
908  ast_log(LOG_ERROR, "Usage: %s(<varname>,<search-chars>[,<replace-char>])\n", cmd);
909  return -1;
910  }
911 
912  /* Decode escapes */
913  ast_get_encoded_str(args.find, find, sizeof(find));
914  ast_get_encoded_char(args.replace, replace, &unused);
915 
916  if (ast_strlen_zero(find) || ast_strlen_zero(args.varname)) {
917  ast_log(LOG_ERROR, "The characters to search for and the variable name must not be empty.\n");
918  return -1;
919  }
920 
921  varsubst = ast_alloca(strlen(args.varname) + 4);
922  sprintf(varsubst, "${%s}", args.varname);
923  ast_str_substitute_variables(&str, 0, chan, varsubst);
924 
925  if (!ast_str_strlen(str)) {
926  /* Blank, nothing to replace */
927  return -1;
928  }
929 
930  ast_debug(3, "String to search: (%s)\n", ast_str_buffer(str));
931  ast_debug(3, "Characters to find: (%s)\n", find);
932  ast_debug(3, "Character to replace with: (%s)\n", replace);
933 
934  for (strptr = ast_str_buffer(str); *strptr; strptr++) {
935  /* buf is already a mutable buffer, so we construct the result
936  * directly there */
937  if (strchr(find, *strptr)) {
938  if (ast_strlen_zero(replace)) {
939  memmove(strptr, strptr + 1, strlen(strptr + 1) + 1);
940  strptr--;
941  } else {
942  /* Replace character */
943  *strptr = *replace;
944  }
945  }
946  }
947 
948  ast_str_set(buf, len, "%s", ast_str_buffer(str));
949  return 0;
950 }
951 
952 static struct ast_custom_function replace_function = {
953  .name = "REPLACE",
954  .read2 = replace,
955 };
956 
957 static int strreplace(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
958 {
959  char *varsubstr; /* substring for input var */
960  char *start; /* Starting pos of substring search. */
961  char *end; /* Ending pos of substring search. */
962  int find_size; /* length of given find-string */
963  unsigned max_matches; /* number of matches we find before terminating search */
964  unsigned count; /* loop counter */
965  struct ast_str *str = ast_str_thread_get(&result_buf, 16); /* Holds the data obtained from varname */
966 
968  AST_APP_ARG(varname);
969  AST_APP_ARG(find_string);
970  AST_APP_ARG(replace_string);
971  AST_APP_ARG(max_replacements);
972  AST_APP_ARG(other); /* Any remining unused arguments */
973  );
974 
975  /* Guarantee output string is empty to start with. */
976  ast_str_reset(*buf);
977 
978  if (!str) {
979  /* We failed to allocate str, forget it. We failed. */
980  return -1;
981  }
982 
983  /* Parse the arguments. */
984  AST_STANDARD_APP_ARGS(args, data);
985 
986  if (args.argc < 2) {
987  /* Didn't receive enough arguments to do anything */
988  ast_log(LOG_ERROR,
989  "Usage: %s(<varname>,<find-string>[,<replace-string>,[<max-replacements>]])\n",
990  cmd);
991  return -1;
992  }
993 
994  /* No var name specified. Return failure, string is already empty. */
995  if (ast_strlen_zero(args.varname)) {
996  return -1;
997  }
998 
999  /* Zero length find strings are a no-no. Kill the function if we run into one. */
1000  if (ast_strlen_zero(args.find_string)) {
1001  ast_log(LOG_ERROR, "No <find-string> specified\n");
1002  return -1;
1003  }
1004  find_size = strlen(args.find_string);
1005 
1006  /* set varsubstr to the matching variable */
1007  varsubstr = ast_alloca(strlen(args.varname) + 4);
1008  sprintf(varsubstr, "${%s}", args.varname);
1009  ast_str_substitute_variables(&str, 0, chan, varsubstr);
1010 
1011  /* Determine how many replacements are allowed. */
1012  if (!args.max_replacements
1013  || (max_matches = atoi(args.max_replacements)) <= 0) {
1014  /* Unlimited replacements are allowed. */
1015  max_matches = -1;
1016  }
1017 
1018  /* Generate the search and replaced string. */
1019  start = ast_str_buffer(str);
1020  for (count = 0; count < max_matches; ++count) {
1021  end = strstr(start, args.find_string);
1022  if (!end) {
1023  /* Did not find a matching substring in the remainder. */
1024  break;
1025  }
1026 
1027  /* Replace the found substring. */
1028  *end = '\0';
1029  ast_str_append(buf, len, "%s", start);
1030  if (args.replace_string) {
1031  /* Append the replacement string */
1032  ast_str_append(buf, len, "%s", args.replace_string);
1033  }
1034  start = end + find_size;
1035  }
1036  ast_str_append(buf, len, "%s", start);
1037 
1038  return 0;
1039 }
1040 
1041 static struct ast_custom_function strreplace_function = {
1042  .name = "STRREPLACE",
1043  .read2 = strreplace,
1044 };
1045 
1046 static int strbetween(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
1047 {
1048  int c, origsize;
1049  char *varsubstr, *origstr;
1050  struct ast_str *str = ast_str_thread_get(&result_buf, 16); /* Holds the data obtained from varname */
1051 
1052  AST_DECLARE_APP_ARGS(args,
1053  AST_APP_ARG(varname);
1054  AST_APP_ARG(insert_string);
1055  AST_APP_ARG(other); /* Any remining unused arguments */
1056  );
1057 
1058  ast_str_reset(*buf);
1059 
1060  if (!str) {
1061  ast_log(LOG_ERROR, "Couldn't obtain string\n");
1062  return -1;
1063  }
1064 
1065  AST_STANDARD_APP_ARGS(args, data);
1066 
1067  if (args.argc != 2 || ast_strlen_zero(args.varname)) {
1068  ast_log(LOG_ERROR, "Usage: %s(<varname>,<insert-string>)\n", cmd);
1069  return -1;
1070  }
1071 
1072  varsubstr = ast_alloca(strlen(args.varname) + 4);
1073  sprintf(varsubstr, "${%s}", args.varname);
1074  ast_str_substitute_variables(&str, 0, chan, varsubstr);
1075  origstr = ast_str_buffer(str);
1076  origsize = strlen(origstr);
1077  for (c = 0; c < origsize; c++) {
1078  ast_str_append(buf, len, "%c", origstr[c]);
1079  /* no insert after the last character */
1080  if (c < (origsize - 1)) {
1081  ast_str_append(buf, len, "%s", args.insert_string);
1082  }
1083  }
1084 
1085  return 0;
1086 }
1087 
1088 static struct ast_custom_function strbetween_function = {
1089  .name = "STRBETWEEN",
1090  .read2 = strbetween,
1091 };
1092 
1093 #define ltrim(s) while (isspace(*s)) s++;
1094 #define rtrim(s) { \
1095  if (s) { \
1096  char *back = s + strlen(s); \
1097  while (back != s && isspace(*--back)); \
1098  if (*s) { \
1099  *(back + 1) = '\0'; \
1100  } \
1101  } \
1102 }
1103 
1104 static int function_trim(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1105 {
1106  char *c;
1107 
1108  if (ast_strlen_zero(data)) {
1109  return -1;
1110  }
1111 
1112  c = ast_strdupa(data);
1113  ltrim(c);
1114  rtrim(c);
1115 
1116  ast_copy_string(buf, c, len);
1117 
1118  return 0;
1119 }
1120 
1121 static int function_ltrim(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1122 {
1123  char *c;
1124 
1125  if (ast_strlen_zero(data)) {
1126  return -1;
1127  }
1128 
1129  c = data;
1130  ltrim(c);
1131 
1132  ast_copy_string(buf, c, len);
1133 
1134  return 0;
1135 }
1136 
1137 static int function_rtrim(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1138 {
1139  char *c;
1140 
1141  if (ast_strlen_zero(data)) {
1142  return -1;
1143  }
1144 
1145  c = ast_strdupa(data);
1146  rtrim(c);
1147 
1148  ast_copy_string(buf, c, len);
1149 
1150  return 0;
1151 }
1152 
1153 #undef ltrim
1154 #undef rtrim
1155 
1156 static struct ast_custom_function trim_function = {
1157  .name = "TRIM",
1158  .read = function_trim,
1159 };
1160 
1161 static struct ast_custom_function ltrim_function = {
1162  .name = "LTRIM",
1163  .read = function_ltrim,
1164 };
1165 
1166 static struct ast_custom_function rtrim_function = {
1167  .name = "RTRIM",
1168  .read = function_rtrim,
1169 };
1170 
1171 static int regex(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
1172  size_t len)
1173 {
1174  AST_DECLARE_APP_ARGS(args,
1175  AST_APP_ARG(null);
1176  AST_APP_ARG(reg);
1177  AST_APP_ARG(str);
1178  );
1179  int errcode;
1180  regex_t regexbuf;
1181 
1182  buf[0] = '\0';
1183 
1184  AST_NONSTANDARD_APP_ARGS(args, parse, '"');
1185 
1186  if (args.argc != 3) {
1187  ast_log(LOG_ERROR, "Unexpected arguments: should have been in the form '\"<regex>\" <string>'\n");
1188  return -1;
1189  }
1190  if ((*args.str == ' ') || (*args.str == '\t'))
1191  args.str++;
1192 
1193  ast_debug(1, "FUNCTION REGEX (%s)(%s)\n", args.reg, args.str);
1194 
1195  if ((errcode = regcomp(&regexbuf, args.reg, REG_EXTENDED | REG_NOSUB))) {
1196  regerror(errcode, &regexbuf, buf, len);
1197  ast_log(LOG_WARNING, "Malformed input %s(%s): %s\n", cmd, parse, buf);
1198  return -1;
1199  }
1200 
1201  strcpy(buf, regexec(&regexbuf, args.str, 0, NULL, 0) ? "0" : "1");
1202 
1203  regfree(&regexbuf);
1204 
1205  return 0;
1206 }
1207 
1208 static struct ast_custom_function regex_function = {
1209  .name = "REGEX",
1210  .read = regex,
1211 };
1212 
1213 #define HASH_PREFIX "~HASH~%s~"
1214 #define HASH_FORMAT HASH_PREFIX "%s~"
1215 
1216 static char *app_clearhash = "ClearHash";
1217 
1218 /* This function probably should migrate to main/pbx.c, as pbx_builtin_clearvar_prefix() */
1219 static void clearvar_prefix(struct ast_channel *chan, const char *prefix)
1220 {
1221  struct ast_var_t *var;
1222  int len = strlen(prefix);
1223  AST_LIST_TRAVERSE_SAFE_BEGIN(ast_channel_varshead(chan), var, entries) {
1224  if (strncmp(prefix, ast_var_name(var), len) == 0) {
1225  AST_LIST_REMOVE_CURRENT(entries);
1226  ast_free(var);
1227  }
1228  }
1230 }
1231 
1232 static int exec_clearhash(struct ast_channel *chan, const char *data)
1233 {
1234  char prefix[80];
1235  snprintf(prefix, sizeof(prefix), HASH_PREFIX, data ? (char *)data : "null");
1236  clearvar_prefix(chan, prefix);
1237  return 0;
1238 }
1239 
1240 static int array(struct ast_channel *chan, const char *cmd, char *var,
1241  const char *value)
1242 {
1243  AST_DECLARE_APP_ARGS(arg1,
1244  AST_APP_ARG(var)[100];
1245  );
1246  AST_DECLARE_APP_ARGS(arg2,
1247  AST_APP_ARG(val)[100];
1248  );
1249  char *origvar = "", *value2, varname[256];
1250  int i, ishash = 0;
1251 
1252  if (!var) {
1253  return -1;
1254  }
1255  value2 = ast_strdupa(value);
1256 
1257  if (!strcmp(cmd, "HASH")) {
1258  const char *var2 = pbx_builtin_getvar_helper(chan, "~ODBCFIELDS~");
1259  origvar = var;
1260  if (var2)
1261  var = ast_strdupa(var2);
1262  else {
1263  if (chan)
1264  ast_autoservice_stop(chan);
1265  return -1;
1266  }
1267  ishash = 1;
1268  }
1269 
1270  /* The functions this will generally be used with are SORT and ODBC_*, which
1271  * both return comma-delimited lists. However, if somebody uses literal lists,
1272  * their commas will be translated to vertical bars by the load, and I don't
1273  * want them to be surprised by the result. Hence, we prefer commas as the
1274  * delimiter, but we'll fall back to vertical bars if commas aren't found.
1275  */
1276  ast_debug(1, "array (%s=%s)\n", var, S_OR(value2, ""));
1277  AST_STANDARD_APP_ARGS(arg1, var);
1278 
1279  AST_STANDARD_APP_ARGS(arg2, value2);
1280 
1281  for (i = 0; i < arg1.argc; i++) {
1282  ast_debug(1, "array set value (%s=%s)\n", arg1.var[i],
1283  S_OR(arg2.val[i], ""));
1284  if (i < arg2.argc) {
1285  if (ishash) {
1286  if (origvar[0] == '_') {
1287  if (origvar[1] == '_') {
1288  snprintf(varname, sizeof(varname), "__" HASH_FORMAT, origvar + 2, arg1.var[i]);
1289  } else {
1290  snprintf(varname, sizeof(varname), "_" HASH_FORMAT, origvar + 1, arg1.var[i]);
1291  }
1292  } else {
1293  snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
1294  }
1295 
1296  pbx_builtin_setvar_helper(chan, varname, arg2.val[i]);
1297  } else {
1298  pbx_builtin_setvar_helper(chan, arg1.var[i], arg2.val[i]);
1299  }
1300  } else {
1301  /* We could unset the variable, by passing a NULL, but due to
1302  * pushvar semantics, that could create some undesired behavior. */
1303  if (ishash) {
1304  snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
1305  pbx_builtin_setvar_helper(chan, varname, "");
1306  } else {
1307  pbx_builtin_setvar_helper(chan, arg1.var[i], "");
1308  }
1309  }
1310  }
1311 
1312  return 0;
1313 }
1314 
1315 static const char *get_key(const struct ast_str *prefix, const struct ast_var_t *var)
1316 {
1317  const char *prefix_name = ast_str_buffer(prefix);
1318  const char *var_name = ast_var_name(var);
1319  int prefix_len;
1320  int var_len;
1321 
1322  if (ast_strlen_zero(var_name)) {
1323  return NULL;
1324  }
1325 
1326  prefix_len = ast_str_strlen(prefix);
1327  var_len = strlen(var_name);
1328 
1329  /*
1330  * Make sure we only match on non-empty, hash function created keys. If valid
1331  * then return a pointer to the variable that's just after the prefix.
1332  */
1333  return var_len > (prefix_len + 1) && var_name[var_len - 1] == '~' &&
1334  strncmp(prefix_name, var_name, prefix_len) == 0 ? var_name + prefix_len : NULL;
1335 }
1336 
1337 static int hashkeys_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1338 {
1339  struct ast_var_t *newvar;
1340  struct ast_str *prefix = ast_str_alloca(80);
1341  size_t buf_len;
1342 
1343  if (!chan) {
1344  ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
1345  return -1;
1346  }
1347 
1348  ast_str_set(&prefix, -1, HASH_PREFIX, data);
1349  memset(buf, 0, len);
1350 
1351  AST_LIST_TRAVERSE(ast_channel_varshead(chan), newvar, entries) {
1352  const char *key = get_key(prefix, newvar);
1353 
1354  if (key) {
1355  strncat(buf, key, len - strlen(buf) - 1);
1356  /* Replace the trailing ~ */
1357  buf[strlen(buf) - 1] = ',';
1358  }
1359  }
1360  /* Trim the trailing comma */
1361  buf_len = strlen(buf);
1362  if (buf_len) {
1363  buf[buf_len - 1] = '\0';
1364  }
1365  return 0;
1366 }
1367 
1368 static int hashkeys_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
1369 {
1370  struct ast_var_t *newvar;
1371  struct ast_str *prefix = ast_str_alloca(80);
1372 
1373  if (!chan) {
1374  ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
1375  return -1;
1376  }
1377 
1378  ast_str_set(&prefix, -1, HASH_PREFIX, data);
1379 
1380  AST_LIST_TRAVERSE(ast_channel_varshead(chan), newvar, entries) {
1381  const char *key = get_key(prefix, newvar);
1382 
1383  if (key) {
1384  char *tmp;
1385 
1386  ast_str_append(buf, len, "%s", key);
1387  /* Replace the trailing ~ */
1388  tmp = ast_str_buffer(*buf);
1389  tmp[ast_str_strlen(*buf) - 1] = ',';
1390  }
1391  }
1392 
1393  ast_str_truncate(*buf, -1);
1394  return 0;
1395 }
1396 
1397 static int hash_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
1398 {
1399  char varname[256];
1401  AST_APP_ARG(hashname);
1402  AST_APP_ARG(hashkey);
1403  );
1404 
1405  if (!strchr(var, ',')) {
1406  /* Single argument version */
1407  return array(chan, "HASH", var, value);
1408  }
1409 
1410  AST_STANDARD_APP_ARGS(arg, var);
1411  if (arg.hashname[0] == '_') {
1412  if (arg.hashname[1] == '_') {
1413  snprintf(varname, sizeof(varname), "__" HASH_FORMAT, arg.hashname + 2, arg.hashkey);
1414  } else {
1415  snprintf(varname, sizeof(varname), "_" HASH_FORMAT, arg.hashname + 1, arg.hashkey);
1416  }
1417  } else {
1418  snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
1419  }
1420  pbx_builtin_setvar_helper(chan, varname, value);
1421 
1422  return 0;
1423 }
1424 
1425 static int hash_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1426 {
1427  char varname[256];
1428  const char *varvalue;
1430  AST_APP_ARG(hashname);
1431  AST_APP_ARG(hashkey);
1432  );
1433 
1434  AST_STANDARD_APP_ARGS(arg, data);
1435  if (arg.argc == 2) {
1436  snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
1437  varvalue = pbx_builtin_getvar_helper(chan, varname);
1438  if (varvalue)
1439  ast_copy_string(buf, varvalue, len);
1440  else
1441  *buf = '\0';
1442  } else if (arg.argc == 1) {
1443  char colnames[4096];
1444  int i;
1445  AST_DECLARE_APP_ARGS(arg2,
1446  AST_APP_ARG(col)[100];
1447  );
1448 
1449  if (!chan) {
1450  ast_log(LOG_WARNING, "No channel and only 1 parameter was provided to %s function.\n", cmd);
1451  return -1;
1452  }
1453 
1454  /* Get column names, in no particular order */
1455  hashkeys_read(chan, "HASHKEYS", arg.hashname, colnames, sizeof(colnames));
1456  pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", colnames);
1457 
1458  AST_STANDARD_APP_ARGS(arg2, colnames);
1459  *buf = '\0';
1460 
1461  /* Now get the corresponding column values, in exactly the same order */
1462  for (i = 0; i < arg2.argc; i++) {
1463  snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg2.col[i]);
1464  varvalue = pbx_builtin_getvar_helper(chan, varname);
1465  strncat(buf, varvalue, len - strlen(buf) - 1);
1466  strncat(buf, ",", len - strlen(buf) - 1);
1467  }
1468 
1469  /* Strip trailing comma */
1470  buf[strlen(buf) - 1] = '\0';
1471  }
1472 
1473  return 0;
1474 }
1475 
1476 static struct ast_custom_function hash_function = {
1477  .name = "HASH",
1478  .write = hash_write,
1479  .read = hash_read,
1480 };
1481 
1482 static struct ast_custom_function hashkeys_function = {
1483  .name = "HASHKEYS",
1484  .read = hashkeys_read,
1485  .read2 = hashkeys_read2,
1486 };
1487 
1488 static struct ast_custom_function array_function = {
1489  .name = "ARRAY",
1490  .write = array,
1491 };
1492 
1493 static int quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1494 {
1495  char *bufptr = buf, *dataptr = data;
1496 
1497  if (len < 3){ /* at least two for quotes and one for binary zero */
1498  ast_log(LOG_ERROR, "Not enough buffer\n");
1499  return -1;
1500  }
1501 
1502  if (ast_strlen_zero(data)) {
1503  ast_log(LOG_WARNING, "No argument specified!\n");
1504  ast_copy_string(buf, "\"\"", len);
1505  return 0;
1506  }
1507 
1508  *bufptr++ = '"';
1509  for (; bufptr < buf + len - 3; dataptr++) {
1510  if (*dataptr == '\\') {
1511  *bufptr++ = '\\';
1512  *bufptr++ = '\\';
1513  } else if (*dataptr == '"') {
1514  *bufptr++ = '\\';
1515  *bufptr++ = '"';
1516  } else if (*dataptr == '\0') {
1517  break;
1518  } else {
1519  *bufptr++ = *dataptr;
1520  }
1521  }
1522  *bufptr++ = '"';
1523  *bufptr = '\0';
1524  return 0;
1525 }
1526 
1527 static struct ast_custom_function quote_function = {
1528  .name = "QUOTE",
1529  .read = quote,
1530 };
1531 
1532 static int csv_quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
1533 {
1534  char *bufptr = buf, *dataptr = data;
1535 
1536  if (len < 3) { /* at least two for quotes and one for binary zero */
1537  ast_log(LOG_ERROR, "Not enough buffer\n");
1538  return -1;
1539  }
1540 
1541  if (ast_strlen_zero(data)) {
1542  ast_copy_string(buf, "\"\"", len);
1543  return 0;
1544  }
1545 
1546  *bufptr++ = '"';
1547  for (; bufptr < buf + len - 3; dataptr++){
1548  if (*dataptr == '"') {
1549  *bufptr++ = '"';
1550  *bufptr++ = '"';
1551  } else if (*dataptr == '\0') {
1552  break;
1553  } else {
1554  *bufptr++ = *dataptr;
1555  }
1556  }
1557  *bufptr++ = '"';
1558  *bufptr='\0';
1559  return 0;
1560 }
1561 
1562 static struct ast_custom_function csv_quote_function = {
1563  .name = "CSV_QUOTE",
1564  .read = csv_quote,
1565 };
1566 
1567 static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
1568 {
1569  int length = 0;
1570 
1571  if (data)
1572  length = strlen(data);
1573 
1574  snprintf(buf, buflen, "%d", length);
1575 
1576  return 0;
1577 }
1578 
1579 static struct ast_custom_function len_function = {
1580  .name = "LEN",
1581  .read = len,
1582  .read_max = 12,
1583 };
1584 
1585 static int acf_strftime(struct ast_channel *chan, const char *cmd, char *parse,
1586  char *buf, size_t buflen)
1587 {
1588  AST_DECLARE_APP_ARGS(args,
1589  AST_APP_ARG(epoch);
1590  AST_APP_ARG(timezone);
1591  AST_APP_ARG(format);
1592  );
1593  struct timeval when;
1594  struct ast_tm tm;
1595 
1596  buf[0] = '\0';
1597 
1598  AST_STANDARD_APP_ARGS(args, parse);
1599 
1600  ast_get_timeval(args.epoch, &when, ast_tvnow(), NULL);
1601  ast_localtime(&when, &tm, args.timezone);
1602 
1603  if (!args.format)
1604  args.format = "%c";
1605 
1606  if (ast_strftime(buf, buflen, args.format, &tm) <= 0)
1607  ast_log(LOG_WARNING, "C function strftime() output nothing?!!\n");
1608 
1609  buf[buflen - 1] = '\0';
1610 
1611  return 0;
1612 }
1613 
1614 static struct ast_custom_function strftime_function = {
1615  .name = "STRFTIME",
1616  .read = acf_strftime,
1617 };
1618 
1619 static int acf_strptime(struct ast_channel *chan, const char *cmd, char *data,
1620  char *buf, size_t buflen)
1621 {
1622  AST_DECLARE_APP_ARGS(args,
1623  AST_APP_ARG(timestring);
1624  AST_APP_ARG(timezone);
1625  AST_APP_ARG(format);
1626  );
1627  struct ast_tm tm;
1628 
1629  buf[0] = '\0';
1630 
1631  if (!data) {
1632  ast_log(LOG_ERROR,
1633  "Asterisk function STRPTIME() requires an argument.\n");
1634  return -1;
1635  }
1636 
1637  AST_STANDARD_APP_ARGS(args, data);
1638 
1639  if (ast_strlen_zero(args.format)) {
1640  ast_log(LOG_ERROR,
1641  "No format supplied to STRPTIME(<timestring>,<timezone>,<format>)");
1642  return -1;
1643  }
1644 
1645  if (!ast_strptime(args.timestring, args.format, &tm)) {
1646  ast_log(LOG_WARNING, "STRPTIME() found no time specified within the string\n");
1647  } else {
1648  struct timeval when;
1649  when = ast_mktime(&tm, args.timezone);
1650  snprintf(buf, buflen, "%d", (int) when.tv_sec);
1651  }
1652 
1653  return 0;
1654 }
1655 
1656 static struct ast_custom_function strptime_function = {
1657  .name = "STRPTIME",
1658  .read = acf_strptime,
1659 };
1660 
1661 static int function_eval(struct ast_channel *chan, const char *cmd, char *data,
1662  char *buf, size_t buflen)
1663 {
1664  if (ast_strlen_zero(data)) {
1665  ast_log(LOG_WARNING, "EVAL requires an argument: EVAL(<string>)\n");
1666  return -1;
1667  }
1668 
1669  pbx_substitute_variables_helper(chan, data, buf, buflen - 1);
1670 
1671  return 0;
1672 }
1673 
1674 static int function_eval2(struct ast_channel *chan, const char *cmd, char *data,
1675  struct ast_str **buf, ssize_t buflen)
1676 {
1677  if (ast_strlen_zero(data)) {
1678  ast_log(LOG_WARNING, "EVAL requires an argument: EVAL(<string>)\n");
1679  return -1;
1680  }
1681 
1682  ast_str_substitute_variables(buf, buflen, chan, data);
1683 
1684  return 0;
1685 }
1686 
1687 static struct ast_custom_function eval_function = {
1688  .name = "EVAL",
1689  .read = function_eval,
1690  .read2 = function_eval2,
1691 };
1692 
1693 static int keypadhash(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
1694 {
1695  char *bufptr, *dataptr;
1696 
1697  for (bufptr = buf, dataptr = data; bufptr < buf + buflen - 1; dataptr++) {
1698  if (*dataptr == '\0') {
1699  *bufptr++ = '\0';
1700  break;
1701  } else if (*dataptr == '1') {
1702  *bufptr++ = '1';
1703  } else if (strchr("AaBbCc2", *dataptr)) {
1704  *bufptr++ = '2';
1705  } else if (strchr("DdEeFf3", *dataptr)) {
1706  *bufptr++ = '3';
1707  } else if (strchr("GgHhIi4", *dataptr)) {
1708  *bufptr++ = '4';
1709  } else if (strchr("JjKkLl5", *dataptr)) {
1710  *bufptr++ = '5';
1711  } else if (strchr("MmNnOo6", *dataptr)) {
1712  *bufptr++ = '6';
1713  } else if (strchr("PpQqRrSs7", *dataptr)) {
1714  *bufptr++ = '7';
1715  } else if (strchr("TtUuVv8", *dataptr)) {
1716  *bufptr++ = '8';
1717  } else if (strchr("WwXxYyZz9", *dataptr)) {
1718  *bufptr++ = '9';
1719  } else if (*dataptr == '0') {
1720  *bufptr++ = '0';
1721  }
1722  }
1723  buf[buflen - 1] = '\0';
1724 
1725  return 0;
1726 }
1727 
1728 static struct ast_custom_function keypadhash_function = {
1729  .name = "KEYPADHASH",
1730  .read = keypadhash,
1731 };
1732 
1733 static int string_toupper(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
1734 {
1735  char *bufptr = buf, *dataptr = data;
1736 
1737  while ((bufptr < buf + buflen - 1) && (*bufptr++ = toupper(*dataptr++)));
1738 
1739  return 0;
1740 }
1741 
1742 static int string_toupper2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t buflen)
1743 {
1744  char *bufptr, *dataptr = data;
1745 
1746  if (buflen > -1) {
1747  ast_str_make_space(buf, buflen > 0 ? buflen : strlen(data) + 1);
1748  }
1749  bufptr = ast_str_buffer(*buf);
1750  while ((bufptr < ast_str_buffer(*buf) + ast_str_size(*buf) - 1) && (*bufptr++ = toupper(*dataptr++)));
1751  ast_str_update(*buf);
1752 
1753  return 0;
1754 }
1755 
1756 static struct ast_custom_function toupper_function = {
1757  .name = "TOUPPER",
1758  .read = string_toupper,
1759  .read2 = string_toupper2,
1760 };
1761 
1762 static int string_tolower(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
1763 {
1764  char *bufptr = buf, *dataptr = data;
1765 
1766  while ((bufptr < buf + buflen - 1) && (*bufptr++ = tolower(*dataptr++)));
1767 
1768  return 0;
1769 }
1770 
1771 static int string_tolower2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t buflen)
1772 {
1773  char *bufptr, *dataptr = data;
1774 
1775  if (buflen > -1) {
1776  ast_str_make_space(buf, buflen > 0 ? buflen : strlen(data) + 1);
1777  }
1778  bufptr = ast_str_buffer(*buf);
1779  while ((bufptr < ast_str_buffer(*buf) + ast_str_size(*buf) - 1) && (*bufptr++ = tolower(*dataptr++)));
1780  ast_str_update(*buf);
1781 
1782  return 0;
1783 }
1784 
1785 static struct ast_custom_function tolower_function = {
1786  .name = "TOLOWER",
1787  .read = string_tolower,
1788  .read2 = string_tolower2,
1789 };
1790 
1791 static int shift_pop(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
1792 {
1793 #define beginning (cmd[0] == 'S') /* SHIFT */
1794  char *after, delimiter[2] = ",", *varsubst;
1795  size_t unused;
1796  struct ast_str *before = ast_str_thread_get(&result_buf, 16);
1797  char *(*search_func)(const char *s, int c) = (beginning ? strchr : strrchr);
1798  AST_DECLARE_APP_ARGS(args,
1799  AST_APP_ARG(var);
1800  AST_APP_ARG(delimiter);
1801  );
1802 
1803  if (!before) {
1804  return -1;
1805  }
1806 
1807  AST_STANDARD_APP_ARGS(args, data);
1808 
1809  if (ast_strlen_zero(args.var)) {
1810  ast_log(LOG_WARNING, "%s requires a variable name\n", cmd);
1811  return -1;
1812  }
1813 
1814  varsubst = ast_alloca(strlen(args.var) + 4);
1815  sprintf(varsubst, "${%s}", args.var);
1816  ast_str_substitute_variables(&before, 0, chan, varsubst);
1817 
1818  if (args.argc > 1 && !ast_strlen_zero(args.delimiter)) {
1819  ast_get_encoded_char(args.delimiter, delimiter, &unused);
1820  }
1821 
1822  if (!ast_str_strlen(before)) {
1823  /* Nothing to pop */
1824  return -1;
1825  }
1826 
1827  if (!(after = search_func(ast_str_buffer(before), delimiter[0]))) {
1828  /* Only one entry in array */
1829  ast_str_set(buf, len, "%s", ast_str_buffer(before));
1830  pbx_builtin_setvar_helper(chan, args.var, "");
1831  } else {
1832  *after++ = '\0';
1833  ast_str_set(buf, len, "%s", beginning ? ast_str_buffer(before) : after);
1834  pbx_builtin_setvar_helper(chan, args.var, beginning ? after : ast_str_buffer(before));
1835  }
1836 
1837  return 0;
1838 #undef beginning
1839 }
1840 
1841 static struct ast_custom_function shift_function = {
1842  .name = "SHIFT",
1843  .read2 = shift_pop,
1844 };
1845 
1846 static struct ast_custom_function pop_function = {
1847  .name = "POP",
1848  .read2 = shift_pop,
1849 };
1850 
1851 static int unshift_push(struct ast_channel *chan, const char *cmd, char *data, const char *new_value)
1852 {
1853 #define beginning (cmd[0] == 'U') /* UNSHIFT */
1854  char delimiter[2] = ",", *varsubst;
1855  size_t unused;
1856  struct ast_str *buf, *previous_value;
1857  AST_DECLARE_APP_ARGS(args,
1858  AST_APP_ARG(var);
1859  AST_APP_ARG(delimiter);
1860  );
1861  const char *stripped_var;
1862 
1863  if (!(buf = ast_str_thread_get(&result_buf, 16)) ||
1864  !(previous_value = ast_str_thread_get(&tmp_buf, 16))) {
1865  return -1;
1866  }
1867 
1868  AST_STANDARD_APP_ARGS(args, data);
1869 
1870  if (ast_strlen_zero(args.var)) {
1871  ast_log(LOG_WARNING, "%s requires a variable name\n", cmd);
1872  return -1;
1873  }
1874 
1875  if (args.argc > 1 && !ast_strlen_zero(args.delimiter)) {
1876  ast_get_encoded_char(args.delimiter, delimiter, &unused);
1877  }
1878 
1879  /* UNSHIFT and PUSH act as ways of setting a variable, so we need to be
1880  * sure to skip leading underscores if they appear. However, we only want
1881  * to skip up to two since that is the maximum number that can be used to
1882  * indicate variable inheritance. Any further underscores are part of the
1883  * variable name.
1884  */
1885  stripped_var = args.var + MIN(strspn(args.var, "_"), 2);
1886  varsubst = ast_alloca(strlen(stripped_var) + 4);
1887  sprintf(varsubst, "${%s}", stripped_var);
1888  ast_str_substitute_variables(&previous_value, 0, chan, varsubst);
1889 
1890  if (!ast_str_strlen(previous_value)) {
1891  ast_str_set(&buf, 0, "%s", new_value);
1892  } else {
1893  ast_str_set(&buf, 0, "%s%c%s",
1894  beginning ? new_value : ast_str_buffer(previous_value),
1895  delimiter[0],
1896  beginning ? ast_str_buffer(previous_value) : new_value);
1897  }
1898 
1899  pbx_builtin_setvar_helper(chan, args.var, ast_str_buffer(buf));
1900 
1901  return 0;
1902 #undef beginning
1903 }
1904 
1905 static struct ast_custom_function push_function = {
1906  .name = "PUSH",
1907  .write = unshift_push,
1908 };
1909 
1910 static struct ast_custom_function unshift_function = {
1911  .name = "UNSHIFT",
1912  .write = unshift_push,
1913 };
1914 
1915 static int passthru(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
1916 {
1917  ast_str_set(buf, len, "%s", data);
1918  return 0;
1919 }
1920 
1921 static struct ast_custom_function passthru_function = {
1922  .name = "PASSTHRU",
1923  .read2 = passthru,
1924 };
1925 
1926 #ifdef TEST_FRAMEWORK
1927 AST_TEST_DEFINE(test_FIELDNUM)
1928 {
1929  int i, res = AST_TEST_PASS;
1930  struct ast_channel *chan;
1931  struct ast_str *str;
1932  char expression[256];
1933  struct {
1934  const char *fields;
1935  const char *delim;
1936  const char *field;
1937  const char *expected;
1938  } test_args[] = {
1939  {"abc,def,ghi,jkl", "\\,", "ghi", "3"},
1940  {"abc def ghi jkl", " ", "abc", "1"},
1941  {"abc/def/ghi/jkl", "\\\\x2f", "def", "2"},
1942  {"abc$def$ghi$jkl", "", "ghi", "0"},
1943  {"abc,def,ghi,jkl", "-", "", "0"},
1944  {"abc-def-ghi-jkl", "-", "mno", "0"}
1945  };
1946 
1947  switch (cmd) {
1948  case TEST_INIT:
1949  info->name = "func_FIELDNUM_test";
1950  info->category = "/funcs/func_strings/";
1951  info->summary = "Test FIELDNUM function";
1952  info->description = "Verify FIELDNUM behavior";
1953  return AST_TEST_NOT_RUN;
1954  case TEST_EXECUTE:
1955  break;
1956  }
1957 
1958  if (!(chan = ast_dummy_channel_alloc())) {
1959  ast_test_status_update(test, "Unable to allocate dummy channel\n");
1960  return AST_TEST_FAIL;
1961  }
1962 
1963  if (!(str = ast_str_create(16))) {
1964  ast_test_status_update(test, "Unable to allocate dynamic string buffer\n");
1965  ast_channel_release(chan);
1966  return AST_TEST_FAIL;
1967  }
1968 
1969  for (i = 0; i < ARRAY_LEN(test_args); i++) {
1970  struct ast_var_t *var = ast_var_assign("FIELDS", test_args[i].fields);
1971  if (!var) {
1972  ast_test_status_update(test, "Out of memory\n");
1973  res = AST_TEST_FAIL;
1974  break;
1975  }
1976 
1977  AST_LIST_INSERT_HEAD(ast_channel_varshead(chan), var, entries);
1978 
1979  snprintf(expression, sizeof(expression), "${FIELDNUM(%s,%s,%s)}", var->name, test_args[i].delim, test_args[i].field);
1980  ast_str_substitute_variables(&str, 0, chan, expression);
1981 
1982  AST_LIST_REMOVE(ast_channel_varshead(chan), var, entries);
1983  ast_var_delete(var);
1984 
1985  if (strcasecmp(ast_str_buffer(str), test_args[i].expected)) {
1986  ast_test_status_update(test, "Evaluation of '%s' returned '%s' instead of the expected value '%s'\n",
1987  expression, ast_str_buffer(str), test_args[i].expected);
1988  res = AST_TEST_FAIL;
1989  break;
1990  }
1991  }
1992 
1993  ast_free(str);
1994  ast_channel_release(chan);
1995 
1996  return res;
1997 }
1998 
1999 AST_TEST_DEFINE(test_REPLACE)
2000 {
2001  int i, res = AST_TEST_PASS;
2002  struct ast_channel *chan;
2003  struct ast_str *str;
2004  char expression[256];
2005  struct {
2006  const char *test_string;
2007  const char *find_chars;
2008  const char *replace_char;
2009  const char *expected;
2010  } test_args[] = {
2011  {"abc,def", "\\,", "-", "abc-def"},
2012  {"abc,abc", "bc", "a", "aaa,aaa"},
2013  {"abc,def", "x", "?", "abc,def"},
2014  {"abc,def", "\\,", "", "abcdef"}
2015  };
2016 
2017  switch (cmd) {
2018  case TEST_INIT:
2019  info->name = "func_REPLACE_test";
2020  info->category = "/funcs/func_strings/";
2021  info->summary = "Test REPLACE function";
2022  info->description = "Verify REPLACE behavior";
2023  return AST_TEST_NOT_RUN;
2024  case TEST_EXECUTE:
2025  break;
2026  }
2027 
2028  if (!(chan = ast_dummy_channel_alloc())) {
2029  ast_test_status_update(test, "Unable to allocate dummy channel\n");
2030  return AST_TEST_FAIL;
2031  }
2032 
2033  if (!(str = ast_str_create(16))) {
2034  ast_test_status_update(test, "Unable to allocate dynamic string buffer\n");
2035  ast_channel_release(chan);
2036  return AST_TEST_FAIL;
2037  }
2038 
2039  for (i = 0; i < ARRAY_LEN(test_args); i++) {
2040  struct ast_var_t *var = ast_var_assign("TEST_STRING", test_args[i].test_string);
2041  if (!var) {
2042  ast_test_status_update(test, "Out of memory\n");
2043  res = AST_TEST_FAIL;
2044  break;
2045  }
2046 
2047  AST_LIST_INSERT_HEAD(ast_channel_varshead(chan), var, entries);
2048 
2049  snprintf(expression, sizeof(expression), "${REPLACE(%s,%s,%s)}", var->name, test_args[i].find_chars, test_args[i].replace_char);
2050  ast_str_substitute_variables(&str, 0, chan, expression);
2051 
2052  AST_LIST_REMOVE(ast_channel_varshead(chan), var, entries);
2053  ast_var_delete(var);
2054 
2055  if (strcasecmp(ast_str_buffer(str), test_args[i].expected)) {
2056  ast_test_status_update(test, "Evaluation of '%s' returned '%s' instead of the expected value '%s'\n",
2057  expression, ast_str_buffer(str), test_args[i].expected);
2058  res = AST_TEST_FAIL;
2059  break;
2060  }
2061  }
2062 
2063  ast_free(str);
2064  ast_channel_release(chan);
2065 
2066  return res;
2067 }
2068 
2069 AST_TEST_DEFINE(test_FILTER)
2070 {
2071  int i, res = AST_TEST_PASS;
2072  const char *test_strings[][2] = {
2073  {"A-R", "DAHDI"},
2074  {"A\\-R", "A"},
2075  {"\\x41-R", "DAHDI"},
2076  {"0-9A-Ca-c", "0042133333A12212"},
2077  {"0-9a-cA-C_+\\-", "0042133333A12212"},
2078  {NULL, NULL},
2079  };
2080 
2081  switch (cmd) {
2082  case TEST_INIT:
2083  info->name = "func_FILTER_test";
2084  info->category = "/funcs/func_strings/";
2085  info->summary = "Test FILTER function";
2086  info->description = "Verify FILTER behavior";
2087  return AST_TEST_NOT_RUN;
2088  case TEST_EXECUTE:
2089  break;
2090  }
2091 
2092  for (i = 0; test_strings[i][0]; i++) {
2093  char tmp[256], tmp2[256] = "";
2094  snprintf(tmp, sizeof(tmp), "${FILTER(%s,0042133333&DAHDI/g1/2212)}", test_strings[i][0]);
2095  pbx_substitute_variables_helper(NULL, tmp, tmp2, sizeof(tmp2) - 1);
2096  if (strcmp(test_strings[i][1], tmp2)) {
2097  ast_test_status_update(test, "Format string '%s' substituted to '%s'. Expected '%s'.\n", test_strings[i][0], tmp2, test_strings[i][1]);
2098  res = AST_TEST_FAIL;
2099  }
2100  }
2101  return res;
2102 }
2103 
2104 AST_TEST_DEFINE(test_STRREPLACE)
2105 {
2106  int i, res = AST_TEST_PASS;
2107  struct ast_channel *chan; /* dummy channel */
2108  struct ast_str *str; /* fancy string for holding comparing value */
2109 
2110  const char *test_strings[][5] = {
2111  {"Weasels have eaten my telephone system", "have eaten my", "are eating our", "", "Weasels are eating our telephone system"}, /*Test normal conditions */
2112  {"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 */
2113  {"foofoofoofoofoofoofoo", "foofoo", "bar", NULL, "barbarbarfoo"}, /* Found string within previous match */
2114  {"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."},
2115  {"One and one and one is three", "and", "plus", "1", "One plus one and one is three"}, /* Test <max-replacements> = 1*/
2116  {"", "fhqwagads", "spelunker", NULL, ""}, /* Empty primary string */
2117  {"Part of this string is missing.", "missing", NULL, NULL, "Part of this string is ."}, /* Empty replace string */
2118  {"'Accidentally' left off a bunch of stuff.", NULL, NULL, NULL, ""}, /* Deliberate error test from too few args */
2119  {"This test will also error.", "", "", "", ""}, /* Deliberate error test from blank find string */
2120  {"This is an \"escape character\" test.", "\\\"escape character\\\"", "evil", NULL, "This is an evil test."}
2121  };
2122 
2123  switch (cmd) {
2124  case TEST_INIT:
2125  info->name = "func_STRREPLACE_test";
2126  info->category = "/funcs/func_strings/";
2127  info->summary = "Test STRREPLACE function";
2128  info->description = "Verify STRREPLACE behavior";
2129  return AST_TEST_NOT_RUN;
2130  case TEST_EXECUTE:
2131  break;
2132  }
2133 
2134  if (!(chan = ast_dummy_channel_alloc())) {
2135  ast_test_status_update(test, "Unable to allocate dummy channel\n");
2136  return AST_TEST_FAIL;
2137  }
2138 
2139  if (!(str = ast_str_create(64))) {
2140  ast_test_status_update(test, "Unable to allocate dynamic string buffer\n");
2141  ast_channel_release(chan);
2142  return AST_TEST_FAIL;
2143  }
2144 
2145  for (i = 0; i < ARRAY_LEN(test_strings); i++) {
2146  char tmp[512], tmp2[512] = "";
2147 
2148  struct ast_var_t *var = ast_var_assign("test_string", test_strings[i][0]);
2149  if (!var) {
2150  ast_test_status_update(test, "Unable to allocate variable\n");
2151  ast_free(str);
2152  ast_channel_release(chan);
2153  return AST_TEST_FAIL;
2154  }
2155 
2156  AST_LIST_INSERT_HEAD(ast_channel_varshead(chan), var, entries);
2157 
2158  if (test_strings[i][3]) {
2159  snprintf(tmp, sizeof(tmp), "${STRREPLACE(%s,%s,%s,%s)}", "test_string", test_strings[i][1], test_strings[i][2], test_strings[i][3]);
2160  } else if (test_strings[i][2]) {
2161  snprintf(tmp, sizeof(tmp), "${STRREPLACE(%s,%s,%s)}", "test_string", test_strings[i][1], test_strings[i][2]);
2162  } else if (test_strings[i][1]) {
2163  snprintf(tmp, sizeof(tmp), "${STRREPLACE(%s,%s)}", "test_string", test_strings[i][1]);
2164  } else {
2165  snprintf(tmp, sizeof(tmp), "${STRREPLACE(%s)}", "test_string");
2166  }
2167  ast_str_substitute_variables(&str, 0, chan, tmp);
2168  if (strcmp(test_strings[i][4], ast_str_buffer(str))) {
2169  ast_test_status_update(test, "Format string '%s' substituted to '%s'. Expected '%s'.\n", test_strings[i][0], tmp2, test_strings[i][4]);
2170  res = AST_TEST_FAIL;
2171  }
2172  }
2173 
2174  ast_free(str);
2175  ast_channel_release(chan);
2176 
2177  return res;
2178 }
2179 
2180 AST_TEST_DEFINE(test_STRBETWEEN)
2181 {
2182  int i, res = AST_TEST_PASS;
2183  struct ast_channel *chan; /* dummy channel */
2184  struct ast_str *str; /* fancy string for holding comparing value */
2185 
2186  const char *test_strings[][5] = {
2187  {"0", "w", "0"},
2188  {"30", "w", "3w0"},
2189  {"212", "w", "2w1w2"},
2190  {"212", "55", "2551552"},
2191  {"212", " ", "2 1 2"},
2192  {"", "w", ""},
2193  {"555", "", "555"},
2194  {"abcdefg", "_", "a_b_c_d_e_f_g"},
2195  {"A", "A", "A"},
2196  {"AA", "B", "ABA"},
2197  {"AAA", "B", "ABABA"},
2198  };
2199 
2200  switch (cmd) {
2201  case TEST_INIT:
2202  info->name = "func_STRBETWEEN";
2203  info->category = "/funcs/func_strings/";
2204  info->summary = "Test STRBETWEEN function";
2205  info->description = "Verify STRBETWEEN behavior";
2206  return AST_TEST_NOT_RUN;
2207  case TEST_EXECUTE:
2208  break;
2209  }
2210 
2211  if (!(chan = ast_dummy_channel_alloc())) {
2212  ast_test_status_update(test, "Unable to allocate dummy channel\n");
2213  return AST_TEST_FAIL;
2214  }
2215 
2216  if (!(str = ast_str_create(64))) {
2217  ast_test_status_update(test, "Unable to allocate dynamic string buffer\n");
2218  ast_channel_release(chan);
2219  return AST_TEST_FAIL;
2220  }
2221 
2222  for (i = 0; i < ARRAY_LEN(test_strings); i++) {
2223  char tmp[512], tmp2[512] = "";
2224 
2225  struct ast_var_t *var = ast_var_assign("test_string", test_strings[i][0]);
2226  if (!var) {
2227  ast_test_status_update(test, "Unable to allocate variable\n");
2228  ast_free(str);
2229  ast_channel_release(chan);
2230  return AST_TEST_FAIL;
2231  }
2232 
2233  AST_LIST_INSERT_HEAD(ast_channel_varshead(chan), var, entries);
2234 
2235  if (test_strings[i][1]) {
2236  snprintf(tmp, sizeof(tmp), "${STRBETWEEN(%s,%s)}", "test_string", test_strings[i][1]);
2237  } else {
2238  snprintf(tmp, sizeof(tmp), "${STRBETWEEN(%s)}", "test_string");
2239  }
2240  ast_str_substitute_variables(&str, 0, chan, tmp);
2241  if (strcmp(test_strings[i][2], ast_str_buffer(str))) {
2242  ast_test_status_update(test, "Format string '%s' substituted to '%s'. Expected '%s'.\n", test_strings[i][0], tmp2, test_strings[i][2]);
2243  res = AST_TEST_FAIL;
2244  }
2245  }
2246 
2247  ast_free(str);
2248  ast_channel_release(chan);
2249 
2250  return res;
2251 }
2252 
2253 AST_TEST_DEFINE(test_TRIM)
2254 {
2255  int i, res = AST_TEST_PASS;
2256  struct ast_channel *chan; /* dummy channel */
2257  struct ast_str *str; /* fancy string for holding comparing value */
2258 
2259  const char *test_strings[][5] = {
2260  {"TRIM", " abcd ", "abcd"},
2261  {"LTRIM", " abcd ", "abcd "},
2262  {"RTRIM", " abcd ", " abcd"},
2263  {"TRIM", "abcd", "abcd"},
2264  {"TRIM", " a b c d ", "a b c d"},
2265  };
2266 
2267  switch (cmd) {
2268  case TEST_INIT:
2269  info->name = "func_TRIM";
2270  info->category = "/funcs/func_strings/";
2271  info->summary = "Test TRIM functions";
2272  info->description = "Verify TRIM behavior";
2273  return AST_TEST_NOT_RUN;
2274  case TEST_EXECUTE:
2275  break;
2276  }
2277 
2278  if (!(chan = ast_dummy_channel_alloc())) {
2279  ast_test_status_update(test, "Unable to allocate dummy channel\n");
2280  return AST_TEST_FAIL;
2281  }
2282 
2283  if (!(str = ast_str_create(64))) {
2284  ast_test_status_update(test, "Unable to allocate dynamic string buffer\n");
2285  ast_channel_release(chan);
2286  return AST_TEST_FAIL;
2287  }
2288 
2289  for (i = 0; i < ARRAY_LEN(test_strings); i++) {
2290  char tmp[512], tmp2[512] = "";
2291 
2292  snprintf(tmp, sizeof(tmp), "${%s(%s)}", test_strings[i][0], test_strings[i][1]);
2293  ast_str_substitute_variables(&str, 0, chan, tmp);
2294  if (strcmp(test_strings[i][2], ast_str_buffer(str))) {
2295  ast_test_status_update(test, "Format string '%s' substituted to '%s'. Expected '%s'.\n", test_strings[i][0], tmp2, test_strings[i][2]);
2296  res = AST_TEST_FAIL;
2297  }
2298  }
2299 
2300  ast_free(str);
2301  ast_channel_release(chan);
2302 
2303  return res;
2304 }
2305 #endif
2306 
2307 static int unload_module(void)
2308 {
2309  int res = 0;
2310 
2311  AST_TEST_UNREGISTER(test_FIELDNUM);
2312  AST_TEST_UNREGISTER(test_REPLACE);
2313  AST_TEST_UNREGISTER(test_FILTER);
2314  AST_TEST_UNREGISTER(test_STRREPLACE);
2315  AST_TEST_UNREGISTER(test_STRBETWEEN);
2316  AST_TEST_UNREGISTER(test_TRIM);
2317  res |= ast_custom_function_unregister(&fieldqty_function);
2318  res |= ast_custom_function_unregister(&fieldnum_function);
2319  res |= ast_custom_function_unregister(&filter_function);
2320  res |= ast_custom_function_unregister(&replace_function);
2321  res |= ast_custom_function_unregister(&strreplace_function);
2322  res |= ast_custom_function_unregister(&strbetween_function);
2323  res |= ast_custom_function_unregister(&listfilter_function);
2324  res |= ast_custom_function_unregister(&regex_function);
2325  res |= ast_custom_function_unregister(&array_function);
2326  res |= ast_custom_function_unregister(&quote_function);
2327  res |= ast_custom_function_unregister(&csv_quote_function);
2328  res |= ast_custom_function_unregister(&len_function);
2329  res |= ast_custom_function_unregister(&strftime_function);
2330  res |= ast_custom_function_unregister(&strptime_function);
2331  res |= ast_custom_function_unregister(&eval_function);
2332  res |= ast_custom_function_unregister(&keypadhash_function);
2333  res |= ast_custom_function_unregister(&hashkeys_function);
2334  res |= ast_custom_function_unregister(&hash_function);
2335  res |= ast_unregister_application(app_clearhash);
2336  res |= ast_custom_function_unregister(&toupper_function);
2337  res |= ast_custom_function_unregister(&tolower_function);
2338  res |= ast_custom_function_unregister(&shift_function);
2339  res |= ast_custom_function_unregister(&pop_function);
2340  res |= ast_custom_function_unregister(&push_function);
2341  res |= ast_custom_function_unregister(&unshift_function);
2342  res |= ast_custom_function_unregister(&passthru_function);
2343  res |= ast_custom_function_unregister(&trim_function);
2344  res |= ast_custom_function_unregister(&ltrim_function);
2345  res |= ast_custom_function_unregister(&rtrim_function);
2346 
2347  return res;
2348 }
2349 
2350 static int load_module(void)
2351 {
2352  int res = 0;
2353 
2354  AST_TEST_REGISTER(test_FIELDNUM);
2355  AST_TEST_REGISTER(test_REPLACE);
2356  AST_TEST_REGISTER(test_FILTER);
2357  AST_TEST_REGISTER(test_STRREPLACE);
2358  AST_TEST_REGISTER(test_STRBETWEEN);
2359  AST_TEST_REGISTER(test_TRIM);
2360  res |= ast_custom_function_register(&fieldqty_function);
2361  res |= ast_custom_function_register(&fieldnum_function);
2362  res |= ast_custom_function_register(&filter_function);
2363  res |= ast_custom_function_register(&replace_function);
2364  res |= ast_custom_function_register(&strreplace_function);
2365  res |= ast_custom_function_register(&strbetween_function);
2366  res |= ast_custom_function_register(&listfilter_function);
2367  res |= ast_custom_function_register(&regex_function);
2368  res |= ast_custom_function_register(&array_function);
2369  res |= ast_custom_function_register(&quote_function);
2370  res |= ast_custom_function_register(&csv_quote_function);
2371  res |= ast_custom_function_register(&len_function);
2372  res |= ast_custom_function_register(&strftime_function);
2373  res |= ast_custom_function_register(&strptime_function);
2374  res |= ast_custom_function_register(&eval_function);
2375  res |= ast_custom_function_register(&keypadhash_function);
2376  res |= ast_custom_function_register(&hashkeys_function);
2377  res |= ast_custom_function_register(&hash_function);
2378  res |= ast_register_application_xml(app_clearhash, exec_clearhash);
2379  res |= ast_custom_function_register(&toupper_function);
2380  res |= ast_custom_function_register(&tolower_function);
2381  res |= ast_custom_function_register(&shift_function);
2382  res |= ast_custom_function_register(&pop_function);
2383  res |= ast_custom_function_register(&push_function);
2384  res |= ast_custom_function_register(&unshift_function);
2385  res |= ast_custom_function_register(&passthru_function);
2386  res |= ast_custom_function_register(&trim_function);
2387  res |= ast_custom_function_register(&ltrim_function);
2388  res |= ast_custom_function_register(&rtrim_function);
2389 
2390  return res;
2391 }
2392 
2393 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "String handling dialplan functions");
const char * name
Definition: pbx.h:119
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
Definition: threadstorage.h:86
Main Channel structure associated with a channel.
Asterisk main include file. File version handling, generic pbx functions.
char * ast_get_encoded_str(const char *stream, char *result, size_t result_len)
Decode a stream of encoded control or extended ASCII characters.
Definition: main/app.c:3162
size_t ast_str_size(const struct ast_str *buf)
Returns the current maximum length (without reallocation) of the current buffer.
Definition: strings.h:742
struct ast_channel * ast_channel_release(struct ast_channel *chan)
Unlink and release reference to a channel.
Definition: channel.c:1584
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
struct ast_tm * ast_localtime(const struct timeval *timep, struct ast_tm *p_tm, const char *zone)
Timezone-independent version of localtime_r(3).
Definition: localtime.c:1739
void ast_str_substitute_variables(struct ast_str **buf, ssize_t maxlen, struct ast_channel *chan, const char *templ)
Test Framework API.
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1139
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
#define AST_LIST_REMOVE(head, elm, field)
Removes a specific entry from a list.
Definition: linkedlists.h:856
int ast_get_timeval(const char *src, struct timeval *tv, struct timeval _default, int *consumed)
Parse a time (float) string.
Definition: utils.c:2419
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:615
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
Utility functions.
char * ast_str_truncate(struct ast_str *buf, ssize_t len)
Truncates the enclosed string to the given length.
Definition: strings.h:786
Custom localtime functions for multiple timezones.
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1113
General Asterisk PBX channel definitions.
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_dummy_channel_alloc()
Create a fake channel structure.
Definition: channel.h:1282
char * ast_str_append_substr(struct ast_str **buf, ssize_t maxlen, const char *src, size_t maxsrc)
Append a non-NULL terminated substring to the end of a dynamic string.
Definition: strings.h:1062
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:557
#define ast_debug(level,...)
Log a DEBUG message.
int ast_get_encoded_char(const char *stream, char *result, size_t *consumed)
Decode an encoded control or extended ASCII character.
Definition: main/app.c:3087
Core PBX routines and definitions.
char * ast_strptime(const char *s, const char *format, struct ast_tm *tm)
Special version of strptime(3) which places the answer in the common structure ast_tm. Also, unlike strptime(3), ast_strptime() initializes its memory prior to use.
Definition: localtime.c:2550
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
Definition: autoservice.c:266
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:288
Support for dynamic strings.
Definition: strings.h:623
#define AST_NONSTANDARD_APP_ARGS(args, parse, sep)
Performs the 'nonstandard' argument separation process for an application.
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
Definition: linkedlists.h:711
int ast_strftime(char *buf, size_t len, const char *format, const struct ast_tm *tm)
Special version of strftime(3) that handles fractions of a second. Takes the same arguments as strfti...
Definition: localtime.c:2524
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name...
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
Definition: strings.h:693
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:730
struct timeval ast_mktime(struct ast_tm *const tmp, const char *zone)
Timezone-independent version of mktime(3).
Definition: localtime.c:2357
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
void ast_str_update(struct ast_str *buf)
Update the length of the buffer, after using ast_str merely as a buffer.
Definition: strings.h:703
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:80
#define AST_TEST_DEFINE(hdr)
Definition: test.h:126
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:529
struct ast_str * ast_str_thread_get(struct ast_threadstorage *ts, size_t init_len)
Retrieve a thread locally stored dynamic string.
Definition: strings.h:909
static int filter(struct ast_channel *chan, const char *cmd, char *parse, char *buf, size_t len)
Definition: func_strings.c:807
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1558
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:640
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
#define AST_APP_ARG(name)
Define an application argument.