2 * Asterisk -- An open source telephony toolkit.
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
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief String manipulation dialplan functions
23 * \author Tilghman Lesher
24 * \author Anothony Minessale II
30 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
35 #include <sys/types.h>
38 #include "asterisk/module.h"
39 #include "asterisk/options.h"
40 #include "asterisk/channel.h"
41 #include "asterisk/pbx.h"
42 #include "asterisk/logger.h"
43 #include "asterisk/utils.h"
44 #include "asterisk/app.h"
45 #include "asterisk/localtime.h"
46 #include "asterisk/options.h"
48 static int function_fieldqty(struct ast_channel *chan, const char *cmd,
49 char *parse, char *buf, size_t len)
51 char *varsubst, varval[8192] = "", *varval2 = varval;
53 AST_DECLARE_APP_ARGS(args,
60 AST_STANDARD_APP_ARGS(args, parse);
62 ast_get_encoded_char(args.delim, delim, &delim_used);
64 varsubst = alloca(strlen(args.varname) + 4);
66 sprintf(varsubst, "${%s}", args.varname);
67 pbx_substitute_variables_helper(chan, varsubst, varval, sizeof(varval) - 1);
68 if (ast_strlen_zero(varval2))
71 while (strsep(&varval2, delim))
77 snprintf(buf, len, "%d", fieldcount);
82 static struct ast_custom_function fieldqty_function = {
84 .synopsis = "Count the fields, with an arbitrary delimiter",
85 .syntax = "FIELDQTY(<varname>,<delim>)",
86 .read = function_fieldqty,
89 static int filter(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
92 AST_DECLARE_APP_ARGS(args,
96 char *outbuf = buf, ac;
97 char allowed[256] = "";
98 size_t allowedlen = 0;
100 AST_STANDARD_APP_ARGS(args, parse);
103 ast_log(LOG_ERROR, "Usage: FILTER(<allowed-chars>,<string>)\n");
108 for (; *(args.allowed) && allowedlen < sizeof(allowed); (args.allowed)++) {
112 if (ast_get_encoded_char(args.allowed, &c1, &consumed))
114 args.allowed += consumed;
116 if (*(args.allowed) == '-') {
117 if (ast_get_encoded_char(args.allowed + 1, &c2, &consumed))
119 args.allowed += consumed + 1;
122 * Looks a little strange, until you realize that we can overflow
123 * the size of a char.
125 for (ac = c1; ac != c2 && allowedlen < sizeof(allowed) - 1; ac++)
126 allowed[allowedlen++] = ac;
127 allowed[allowedlen++] = ac;
129 ast_debug(4, "c1=%d, c2=%d\n", c1, c2);
131 /* Decrement before the loop increment */
134 allowed[allowedlen++] = c1;
137 ast_debug(1, "Allowed: %s\n", allowed);
139 for (; *(args.string) && (buf + len - 1 > outbuf); (args.string)++) {
140 if (strchr(allowed, *(args.string)))
141 *outbuf++ = *(args.string);
148 static struct ast_custom_function filter_function = {
150 .synopsis = "Filter the string to include only the allowed characters",
151 .syntax = "FILTER(<allowed-chars>,<string>)",
154 "Permits all characters listed in <allowed-chars>, filtering all others out.\n"
155 "In addition to literally listing the characters, you may also use ranges of\n"
156 "characters (delimited by a '-'), as well as hexadecimal characters started\n"
157 "with a \\x (i.e. \\x20) and octal characters started with \\0 (i.e. \\040).\n"
158 "Also, \\t, \\n, and \\r are recognized. If you want a literal '-' character,\n"
159 "simply prefix it with a '\\'\n",
162 static int regex(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
165 AST_DECLARE_APP_ARGS(args,
175 AST_NONSTANDARD_APP_ARGS(args, parse, '"');
177 if (args.argc != 3) {
178 ast_log(LOG_ERROR, "Unexpected arguments: should have been in the form '\"<regex>\" <string>'\n");
181 if ((*args.str == ' ') || (*args.str == '\t'))
184 ast_debug(1, "FUNCTION REGEX (%s)(%s)\n", args.reg, args.str);
186 if ((errcode = regcomp(®exbuf, args.reg, REG_EXTENDED | REG_NOSUB))) {
187 regerror(errcode, ®exbuf, buf, len);
188 ast_log(LOG_WARNING, "Malformed input %s(%s): %s\n", cmd, parse, buf);
192 strcpy(buf, regexec(®exbuf, args.str, 0, NULL, 0) ? "0" : "1");
199 static struct ast_custom_function regex_function = {
201 .synopsis = "Regular Expression",
203 "Returns 1 if data matches regular expression, or 0 otherwise.\n"
204 "Please note that the space following the double quotes separating the regex from the data\n"
205 "is optional and if present, is skipped. If a space is desired at the beginning of the data,\n"
206 "then put two spaces there; the second will not be skipped.\n",
207 .syntax = "REGEX(\"<regular expression>\" <data>)",
211 #define HASH_PREFIX "~HASH~%s~"
212 #define HASH_FORMAT HASH_PREFIX "%s~"
214 static char *app_clearhash = "ClearHash";
215 static char *syn_clearhash = "Clear the keys from a specified hashname";
216 static char *desc_clearhash =
217 "ClearHash(<hashname>)\n"
218 " Clears all keys out of the specified hashname\n";
220 /* This function probably should migrate to main/pbx.c, as pbx_builtin_clearvar_prefix() */
221 static void clearvar_prefix(struct ast_channel *chan, const char *prefix)
223 struct ast_var_t *var;
224 int len = strlen(prefix);
225 AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->varshead, var, entries) {
226 if (strncasecmp(prefix, ast_var_name(var), len) == 0) {
227 AST_LIST_REMOVE_CURRENT(&chan->varshead, entries);
231 AST_LIST_TRAVERSE_SAFE_END
234 static int exec_clearhash(struct ast_channel *chan, void *data)
237 snprintf(prefix, sizeof(prefix), HASH_PREFIX, data ? (char *)data : "null");
238 clearvar_prefix(chan, prefix);
242 static int array(struct ast_channel *chan, const char *cmd, char *var,
245 AST_DECLARE_APP_ARGS(arg1,
246 AST_APP_ARG(var)[100];
248 AST_DECLARE_APP_ARGS(arg2,
249 AST_APP_ARG(val)[100];
251 char *origvar = "", *value2, varname[256];
254 value2 = ast_strdupa(value);
258 if (!strcmp(cmd, "HASH")) {
259 const char *var2 = pbx_builtin_getvar_helper(chan, "~ODBCFIELDS~");
262 var = ast_strdupa(var2);
268 /* The functions this will generally be used with are SORT and ODBC_*, which
269 * both return comma-delimited lists. However, if somebody uses literal lists,
270 * their commas will be translated to vertical bars by the load, and I don't
271 * want them to be surprised by the result. Hence, we prefer commas as the
272 * delimiter, but we'll fall back to vertical bars if commas aren't found.
274 ast_debug(1, "array (%s=%s)\n", var, value2);
275 AST_STANDARD_APP_ARGS(arg1, var);
277 AST_STANDARD_APP_ARGS(arg2, value2);
279 for (i = 0; i < arg1.argc; i++) {
280 ast_debug(1, "array set value (%s=%s)\n", arg1.var[i],
284 snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
285 pbx_builtin_setvar_helper(chan, varname, arg2.val[i]);
287 pbx_builtin_setvar_helper(chan, arg1.var[i], arg2.val[i]);
290 /* We could unset the variable, by passing a NULL, but due to
291 * pushvar semantics, that could create some undesired behavior. */
293 snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
294 pbx_builtin_setvar_helper(chan, varname, "");
296 pbx_builtin_setvar_helper(chan, arg1.var[i], "");
304 static int hashkeys_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
306 struct ast_var_t *newvar;
309 snprintf(prefix, sizeof(prefix), HASH_PREFIX, data);
310 plen = strlen(prefix);
313 AST_LIST_TRAVERSE(&chan->varshead, newvar, entries) {
314 if (strncasecmp(prefix, ast_var_name(newvar), plen) == 0) {
315 /* Copy everything after the prefix */
316 strncat(buf, ast_var_name(newvar) + plen, len);
317 /* Trim the trailing ~ */
318 buf[strlen(buf) - 1] = ',';
321 /* Trim the trailing comma */
322 buf[strlen(buf) - 1] = '\0';
326 static int hash_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
329 AST_DECLARE_APP_ARGS(arg,
330 AST_APP_ARG(hashname);
331 AST_APP_ARG(hashkey);
334 if (!strchr(var, ',')) {
335 /* Single argument version */
336 return array(chan, "HASH", var, value);
339 AST_STANDARD_APP_ARGS(arg, var);
340 snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
341 pbx_builtin_setvar_helper(chan, varname, value);
346 static int hash_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
349 const char *varvalue;
350 AST_DECLARE_APP_ARGS(arg,
351 AST_APP_ARG(hashname);
352 AST_APP_ARG(hashkey);
355 AST_STANDARD_APP_ARGS(arg, data);
357 snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
358 varvalue = pbx_builtin_getvar_helper(chan, varname);
360 ast_copy_string(buf, varvalue, len);
363 } else if (arg.argc == 1) {
366 AST_DECLARE_APP_ARGS(arg2,
367 AST_APP_ARG(col)[100];
370 /* Get column names, in no particular order */
371 hashkeys_read(chan, "HASHKEYS", arg.hashname, colnames, sizeof(colnames));
372 pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", colnames);
374 AST_NONSTANDARD_APP_ARGS(arg2, colnames, ',');
377 /* Now get the corresponding column values, in exactly the same order */
378 for (i = 0; i < arg2.argc; i++) {
379 snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg2.col[i]);
380 varvalue = pbx_builtin_getvar_helper(chan, varname);
381 strncat(buf, varvalue, len);
382 strncat(buf, ",", len);
385 /* Strip trailing comma */
386 buf[strlen(buf) - 1] = '\0';
392 static struct ast_custom_function hash_function = {
394 .synopsis = "Implementation of a dialplan associative array",
395 .syntax = "HASH(hashname[,hashkey])",
399 "In two argument mode, gets and sets values to corresponding keys within a named\n"
400 "associative array. The single-argument mode will only work when assigned to from\n"
401 "a function defined by func_odbc.so.\n",
404 static struct ast_custom_function hashkeys_function = {
406 .synopsis = "Retrieve the keys of a HASH()",
407 .syntax = "HASHKEYS(<hashname>)",
408 .read = hashkeys_read,
410 "Returns a comma-delimited list of the current keys of an associative array\n"
411 "defined by the HASH() function. Note that if you iterate over the keys of\n"
412 "the result, adding keys during iteration will cause the result of the HASHKEYS\n"
413 "function to change.\n",
416 static struct ast_custom_function array_function = {
418 .synopsis = "Allows setting multiple variables at once",
419 .syntax = "ARRAY(var1[,var2[...][,varN]])",
422 "The comma-separated list passed as a value to which the function is set will\n"
423 "be interpreted as a set of values to which the comma-separated list of\n"
424 "variable names in the argument should be set.\n"
425 "Hence, Set(ARRAY(var1,var2)=1,2) will set var1 to 1 and var2 to 2.\n",
428 static int acf_sprintf(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
430 #define SPRINTF_FLAG 0
431 #define SPRINTF_WIDTH 1
432 #define SPRINTF_PRECISION 2
433 #define SPRINTF_LENGTH 3
434 #define SPRINTF_CONVERSION 4
435 int i, state = -1, argcount = 0;
436 char *formatstart = NULL, *bufptr = buf;
437 char formatbuf[256] = "";
440 AST_DECLARE_APP_ARGS(arg,
442 AST_APP_ARG(var)[100];
445 AST_STANDARD_APP_ARGS(arg, data);
447 /* Scan the format, converting each argument into the requisite format type. */
448 for (i = 0; arg.format[i]; i++) {
451 if (strchr("#0- +'I", arg.format[i]))
453 state = SPRINTF_WIDTH;
455 if (arg.format[i] >= '0' && arg.format[i] <= '9')
458 /* Next character must be a period to go into a precision */
459 if (arg.format[i] == '.') {
460 state = SPRINTF_PRECISION;
462 state = SPRINTF_LENGTH;
466 case SPRINTF_PRECISION:
467 if (arg.format[i] >= '0' && arg.format[i] <= '9')
469 state = SPRINTF_LENGTH;
471 if (strchr("hl", arg.format[i])) {
472 if (arg.format[i + 1] == arg.format[i])
474 state = SPRINTF_CONVERSION;
476 } else if (strchr("Lqjzt", arg.format[i]))
477 state = SPRINTF_CONVERSION;
479 state = SPRINTF_CONVERSION;
480 case SPRINTF_CONVERSION:
481 if (strchr("diouxXc", arg.format[i])) {
484 /* Isolate this format alone */
485 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
486 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
488 /* Convert the argument into the required type */
489 if (sscanf(arg.var[argcount++], "%d", &tmpi) != 1) {
490 ast_log(LOG_ERROR, "Argument '%s' is not an integer number for format '%s'\n", arg.var[argcount - 1], formatbuf);
494 /* Format the argument */
495 snprintf(bufptr, buf + len - bufptr, formatbuf, tmpi);
497 /* Update the position of the next parameter to print */
498 bufptr = strchr(buf, '\0');
499 } else if (strchr("eEfFgGaA", arg.format[i])) {
502 /* Isolate this format alone */
503 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
504 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
506 /* Convert the argument into the required type */
507 if (sscanf(arg.var[argcount++], "%lf", &tmpd) != 1) {
508 ast_log(LOG_ERROR, "Argument '%s' is not a floating point number for format '%s'\n", arg.var[argcount - 1], formatbuf);
512 /* Format the argument */
513 snprintf(bufptr, buf + len - bufptr, formatbuf, tmpd);
515 /* Update the position of the next parameter to print */
516 bufptr = strchr(buf, '\0');
517 } else if (arg.format[i] == 's') {
520 /* Isolate this format alone */
521 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
522 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
524 /* Format the argument */
525 snprintf(bufptr, buf + len - bufptr, formatbuf, arg.var[argcount++]);
527 /* Update the position of the next parameter to print */
528 bufptr = strchr(buf, '\0');
529 } else if (arg.format[i] == '%') {
530 /* Literal data to copy */
531 *bufptr++ = arg.format[i];
535 /* Isolate this format alone */
536 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
537 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
539 ast_log(LOG_ERROR, "Format type not supported: '%s' with argument '%s'\n", formatbuf, arg.var[argcount++]);
545 if (arg.format[i] == '%') {
546 state = SPRINTF_FLAG;
547 formatstart = &arg.format[i];
550 /* Literal data to copy */
551 *bufptr++ = arg.format[i];
560 static struct ast_custom_function sprintf_function = {
562 .synopsis = "Format a variable according to a format string",
563 .syntax = "SPRINTF(<format>,<arg1>[,...<argN>])",
566 "Parses the format string specified and returns a string matching that format.\n"
567 "Supports most options supported by sprintf(3). Returns a shortened string if\n"
568 "a format specifier is not recognized.\n",
571 static int quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
573 char *bufptr = buf, *dataptr = data;
575 for (; bufptr < buf + len - 1; dataptr++) {
576 if (*dataptr == '\\') {
579 } else if (*dataptr == '"') {
582 } else if (*dataptr == '\0') {
585 *bufptr++ = *dataptr;
593 static struct ast_custom_function quote_function = {
595 .synopsis = "Quotes a given string, escaping embedded quotes as necessary",
596 .syntax = "QUOTE(<string>)",
601 static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf,
607 length = strlen(data);
609 snprintf(buf, len, "%d", length);
614 static struct ast_custom_function len_function = {
616 .synopsis = "Returns the length of the argument given",
617 .syntax = "LEN(<string>)",
621 static int acf_strftime(struct ast_channel *chan, const char *cmd, char *parse,
622 char *buf, size_t len)
624 AST_DECLARE_APP_ARGS(args,
626 AST_APP_ARG(timezone);
634 AST_STANDARD_APP_ARGS(args, parse);
636 ast_get_timeval(args.epoch, &tv, ast_tvnow(), NULL);
637 ast_localtime(&tv, &tm, args.timezone);
642 if (ast_strftime(buf, len, args.format, &tm) <= 0)
643 ast_log(LOG_WARNING, "C function strftime() output nothing?!!\n");
650 static struct ast_custom_function strftime_function = {
652 .synopsis = "Returns the current date/time in a specified format.",
653 .syntax = "STRFTIME([<epoch>][,[timezone][,format]])",
655 "STRFTIME sports all of the same formats as the underlying C function\n"
656 "strftime(3) - see the man page for details. It also supports the\n"
657 "following format:\n"
658 " %[n]q - fractions of a second, with leading zeroes. For example, %3q will\n"
659 " give milliseconds and %1q will give tenths of a second. The default\n"
660 " is to output milliseconds (n=3). The common case is to use it in\n"
661 " combination with %S, as in \"%S.%3q\".\n",
662 .read = acf_strftime,
665 static int acf_strptime(struct ast_channel *chan, const char *cmd, char *data,
666 char *buf, size_t len)
668 AST_DECLARE_APP_ARGS(args,
669 AST_APP_ARG(timestring);
670 AST_APP_ARG(timezone);
682 "Asterisk function STRPTIME() requires an argument.\n");
686 AST_STANDARD_APP_ARGS(args, data);
688 if (ast_strlen_zero(args.format)) {
690 "No format supplied to STRPTIME(<timestring>,<timezone>,<format>)");
694 if (!strptime(args.timestring, args.format, &t.time)) {
695 ast_log(LOG_WARNING, "C function strptime() output nothing?!!\n");
697 struct timeval tv = ast_mktime(&t.atm, args.timezone);
698 snprintf(buf, len, "%d", (int) tv.tv_sec);
704 static struct ast_custom_function strptime_function = {
707 "Returns the epoch of the arbitrary date/time string structured as described in the format.",
708 .syntax = "STRPTIME(<datetime>,<timezone>,<format>)",
710 "This is useful for converting a date into an EPOCH time, possibly to pass to\n"
711 "an application like SayUnixTime or to calculate the difference between two\n"
715 " ${STRPTIME(2006-03-01 07:30:35,America/Chicago,%Y-%m-%d %H:%M:%S)} returns 1141219835\n",
716 .read = acf_strptime,
719 static int function_eval(struct ast_channel *chan, const char *cmd, char *data,
720 char *buf, size_t len)
724 if (ast_strlen_zero(data)) {
725 ast_log(LOG_WARNING, "EVAL requires an argument: EVAL(<string>)\n");
729 pbx_substitute_variables_helper(chan, data, buf, len - 1);
734 static struct ast_custom_function eval_function = {
736 .synopsis = "Evaluate stored variables.",
737 .syntax = "EVAL(<variable>)",
738 .desc = "Using EVAL basically causes a string to be evaluated twice.\n"
739 "When a variable or expression is in the dialplan, it will be\n"
740 "evaluated at runtime. However, if the result of the evaluation\n"
741 "is in fact a variable or expression, using EVAL will have it\n"
742 "evaluated a second time. For example, if the variable ${MYVAR}\n"
743 "contains \"${OTHERVAR}\", then the result of putting ${EVAL(${MYVAR})}\n"
744 "in the dialplan will be the contents of the variable, OTHERVAR.\n"
745 "Normally, by just putting ${MYVAR} in the dialplan, you would be\n"
746 "left with \"${OTHERVAR}\".\n",
747 .read = function_eval,
750 static int keypadhash(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
752 char *bufptr, *dataptr;
754 for (bufptr = buf, dataptr = data; bufptr < buf + len - 1; dataptr++) {
755 if (*dataptr == '1') {
757 } else if (strchr("AaBbCc2", *dataptr)) {
759 } else if (strchr("DdEeFf3", *dataptr)) {
761 } else if (strchr("GgHhIi4", *dataptr)) {
763 } else if (strchr("JjKkLl5", *dataptr)) {
765 } else if (strchr("MmNnOo6", *dataptr)) {
767 } else if (strchr("PpQqRrSs7", *dataptr)) {
769 } else if (strchr("TtUuVv8", *dataptr)) {
771 } else if (strchr("WwXxYyZz9", *dataptr)) {
773 } else if (*dataptr == '0') {
775 } else if (*dataptr == '\0') {
785 static struct ast_custom_function keypadhash_function = {
786 .name = "KEYPADHASH",
787 .synopsis = "Hash the letters in the string into the equivalent keypad numbers.",
788 .syntax = "KEYPADHASH(<string>)",
790 .desc = "Example: ${KEYPADHASH(Les)} returns \"537\"\n",
793 static int unload_module(void)
797 res |= ast_custom_function_unregister(&fieldqty_function);
798 res |= ast_custom_function_unregister(&filter_function);
799 res |= ast_custom_function_unregister(®ex_function);
800 res |= ast_custom_function_unregister(&array_function);
801 res |= ast_custom_function_unregister("e_function);
802 res |= ast_custom_function_unregister(&len_function);
803 res |= ast_custom_function_unregister(&strftime_function);
804 res |= ast_custom_function_unregister(&strptime_function);
805 res |= ast_custom_function_unregister(&eval_function);
806 res |= ast_custom_function_unregister(&keypadhash_function);
807 res |= ast_custom_function_unregister(&sprintf_function);
808 res |= ast_custom_function_unregister(&hashkeys_function);
809 res |= ast_custom_function_unregister(&hash_function);
810 res |= ast_unregister_application(app_clearhash);
815 static int load_module(void)
819 res |= ast_custom_function_register(&fieldqty_function);
820 res |= ast_custom_function_register(&filter_function);
821 res |= ast_custom_function_register(®ex_function);
822 res |= ast_custom_function_register(&array_function);
823 res |= ast_custom_function_register("e_function);
824 res |= ast_custom_function_register(&len_function);
825 res |= ast_custom_function_register(&strftime_function);
826 res |= ast_custom_function_register(&strptime_function);
827 res |= ast_custom_function_register(&eval_function);
828 res |= ast_custom_function_register(&keypadhash_function);
829 res |= ast_custom_function_register(&sprintf_function);
830 res |= ast_custom_function_register(&hashkeys_function);
831 res |= ast_custom_function_register(&hash_function);
832 res |= ast_register_application(app_clearhash, exec_clearhash, syn_clearhash, desc_clearhash);
837 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "String handling dialplan functions");