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