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