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