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