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
29 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
34 #include <sys/types.h>
37 #include "asterisk/module.h"
38 #include "asterisk/channel.h"
39 #include "asterisk/pbx.h"
40 #include "asterisk/logger.h"
41 #include "asterisk/utils.h"
42 #include "asterisk/app.h"
43 #include "asterisk/localtime.h"
45 static int function_fieldqty(struct ast_channel *chan, char *cmd,
46 char *parse, char *buf, size_t len)
50 AST_DECLARE_APP_ARGS(args,
55 AST_STANDARD_APP_ARGS(args, parse);
57 pbx_retrieve_variable(chan, args.varname, &varval, buf, len, NULL);
58 while (strsep(&varval, args.delim))
63 snprintf(buf, len, "%d", fieldcount);
68 static struct ast_custom_function fieldqty_function = {
70 .synopsis = "Count the fields, with an arbitrary delimiter",
71 .syntax = "FIELDQTY(<varname>|<delim>)",
72 .read = function_fieldqty,
75 static int filter(struct ast_channel *chan, char *cmd, char *parse, char *buf,
78 AST_DECLARE_APP_ARGS(args,
84 AST_STANDARD_APP_ARGS(args, parse);
87 ast_log(LOG_ERROR, "Usage: FILTER(<allowed-chars>|<string>)\n");
91 for (; *(args.string) && (buf + len - 1 > outbuf); (args.string)++) {
92 if (strchr(args.allowed, *(args.string)))
93 *outbuf++ = *(args.string);
100 static struct ast_custom_function filter_function = {
102 .synopsis = "Filter the string to include only the allowed characters",
103 .syntax = "FILTER(<allowed-chars>|<string>)",
107 static int regex(struct ast_channel *chan, char *cmd, char *parse, char *buf,
110 AST_DECLARE_APP_ARGS(args,
120 AST_NONSTANDARD_APP_ARGS(args, parse, '"');
122 if (args.argc != 3) {
123 ast_log(LOG_ERROR, "Unexpected arguments: should have been in the form '\"<regex>\" <string>'\n");
127 ast_log(LOG_DEBUG, "FUNCTION REGEX (%s)(%s)\n", args.reg, args.str);
129 if ((errcode = regcomp(®exbuf, args.reg, REG_EXTENDED | REG_NOSUB))) {
130 regerror(errcode, ®exbuf, buf, len);
131 ast_log(LOG_WARNING, "Malformed input %s(%s): %s\n", cmd, parse, buf);
135 strcpy(buf, regexec(®exbuf, args.str, 0, NULL, 0) ? "0" : "1");
142 static struct ast_custom_function regex_function = {
144 .synopsis = "Regular Expression",
146 "Returns 1 if data matches regular expression, or 0 otherwise.\n"
147 "Please note that the double quotes separating the expression from the data\n"
148 "should not have any neighboring spaces, either before or after, unless you\n"
149 "intend them to be in either the expression or the data!\n",
150 .syntax = "REGEX(\"<regular expression>\"<data>)",
154 static int array(struct ast_channel *chan, char *cmd, char *var,
157 AST_DECLARE_APP_ARGS(arg1,
158 AST_APP_ARG(var)[100];
160 AST_DECLARE_APP_ARGS(arg2,
161 AST_APP_ARG(val)[100];
166 value2 = ast_strdupa(value);
170 /* The functions this will generally be used with are SORT and ODBC_*, which
171 * both return comma-delimited lists. However, if somebody uses literal lists,
172 * their commas will be translated to vertical bars by the load, and I don't
173 * want them to be surprised by the result. Hence, we prefer commas as the
174 * delimiter, but we'll fall back to vertical bars if commas aren't found.
176 ast_log(LOG_DEBUG, "array (%s=%s)\n", var, value2);
177 if (strchr(var, ','))
178 AST_NONSTANDARD_APP_ARGS(arg1, var, ',');
180 AST_STANDARD_APP_ARGS(arg1, var);
182 if (strchr(value2, ','))
183 AST_NONSTANDARD_APP_ARGS(arg2, value2, ',');
185 AST_STANDARD_APP_ARGS(arg2, value2);
187 for (i = 0; i < arg1.argc; i++) {
188 ast_log(LOG_DEBUG, "array set value (%s=%s)\n", arg1.var[i],
191 pbx_builtin_setvar_helper(chan, arg1.var[i], arg2.val[i]);
193 /* We could unset the variable, by passing a NULL, but due to
194 * pushvar semantics, that could create some undesired behavior. */
195 pbx_builtin_setvar_helper(chan, arg1.var[i], "");
202 static struct ast_custom_function array_function = {
204 .synopsis = "Allows setting multiple variables at once",
205 .syntax = "ARRAY(var1[|var2[...][|varN]])",
208 "The comma-separated list passed as a value to which the function is set will\n"
209 "be interpreted as a set of values to which the comma-separated list of\n"
210 "variable names in the argument should be set.\n"
211 "Hence, Set(ARRAY(var1|var2)=1\\,2) will set var1 to 1 and var2 to 2\n"
212 "Note: remember to either backslash your commas in extensions.conf or quote the\n"
213 "entire argument, since Set can take multiple arguments itself.\n",
216 static int acf_sprintf(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
218 #define SPRINTF_FLAG 0
219 #define SPRINTF_WIDTH 1
220 #define SPRINTF_PRECISION 2
221 #define SPRINTF_LENGTH 3
222 #define SPRINTF_CONVERSION 4
223 int i, state = -1, argcount = 0;
224 char *formatstart = NULL, *bufptr = buf;
225 char formatbuf[256] = "";
228 AST_DECLARE_APP_ARGS(arg,
230 AST_APP_ARG(var)[100];
233 AST_STANDARD_APP_ARGS(arg, data);
235 /* Scan the format, converting each argument into the requisite format type. */
236 for (i = 0; arg.format[i]; i++) {
239 if (strchr("#0- +'I", arg.format[i]))
241 state = SPRINTF_WIDTH;
243 if (arg.format[i] >= '0' && arg.format[i] <= '9')
246 /* Next character must be a period to go into a precision */
247 if (arg.format[i] == '.') {
248 state = SPRINTF_PRECISION;
250 state = SPRINTF_LENGTH;
254 case SPRINTF_PRECISION:
255 if (arg.format[i] >= '0' && arg.format[i] <= '9')
257 state = SPRINTF_LENGTH;
259 if (strchr("hl", arg.format[i])) {
260 if (arg.format[i + 1] == arg.format[i])
262 state = SPRINTF_CONVERSION;
264 } else if (strchr("Lqjzt", arg.format[i]))
265 state = SPRINTF_CONVERSION;
267 state = SPRINTF_CONVERSION;
268 case SPRINTF_CONVERSION:
269 if (strchr("diouxXc", arg.format[i])) {
272 /* Isolate this format alone */
273 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
274 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
276 /* Convert the argument into the required type */
277 if (sscanf(arg.var[argcount++], "%i", &tmpi) != 1) {
278 ast_log(LOG_ERROR, "Argument '%s' is not an integer number for format '%s'\n", arg.var[argcount - 1], formatbuf);
282 /* Format the argument */
283 snprintf(bufptr, buf + len - bufptr, formatbuf, tmpi);
285 /* Update the position of the next parameter to print */
286 bufptr = strchr(buf, '\0');
287 } else if (strchr("eEfFgGaA", arg.format[i])) {
290 /* Isolate this format alone */
291 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
292 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
294 /* Convert the argument into the required type */
295 if (sscanf(arg.var[argcount++], "%lf", &tmpd) != 1) {
296 ast_log(LOG_ERROR, "Argument '%s' is not a floating point number for format '%s'\n", arg.var[argcount - 1], formatbuf);
300 /* Format the argument */
301 snprintf(bufptr, buf + len - bufptr, formatbuf, tmpd);
303 /* Update the position of the next parameter to print */
304 bufptr = strchr(buf, '\0');
305 } else if (arg.format[i] == 's') {
308 /* Isolate this format alone */
309 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
310 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
312 /* Format the argument */
313 snprintf(bufptr, buf + len - bufptr, formatbuf, arg.var[argcount++]);
315 /* Update the position of the next parameter to print */
316 bufptr = strchr(buf, '\0');
317 } else if (arg.format[i] == '%') {
318 /* Literal data to copy */
319 *bufptr++ = arg.format[i];
323 /* Isolate this format alone */
324 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
325 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
327 ast_log(LOG_ERROR, "Format type not supported: '%s' with argument '%s'\n", formatbuf, arg.var[argcount++]);
333 if (arg.format[i] == '%') {
334 state = SPRINTF_FLAG;
335 formatstart = &arg.format[i];
338 /* Literal data to copy */
339 *bufptr++ = arg.format[i];
348 static struct ast_custom_function sprintf_function = {
350 .synopsis = "Format a variable according to a format string",
351 .syntax = "SPRINTF(<format>|<arg1>[|...<argN>])",
354 "Parses the format string specified and returns a string matching that format.\n"
355 "Supports most options supported by sprintf(3). Returns a shortened string if\n"
356 "a format specifier is not recognized.\n",
359 static int quote(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
361 char *bufptr = buf, *dataptr = data;
363 for (; bufptr < buf + len - 1; dataptr++) {
364 if (*dataptr == '\\') {
367 } else if (*dataptr == '"') {
370 } else if (*dataptr == '\0') {
373 *bufptr++ = *dataptr;
381 static struct ast_custom_function quote_function = {
383 .synopsis = "Quotes a given string, escaping embedded quotes as necessary",
384 .syntax = "QUOTE(<string>)",
389 static int len(struct ast_channel *chan, char *cmd, char *data, char *buf,
395 length = strlen(data);
397 snprintf(buf, len, "%d", length);
402 static struct ast_custom_function len_function = {
404 .synopsis = "Returns the length of the argument given",
405 .syntax = "LEN(<string>)",
409 static int acf_strftime(struct ast_channel *chan, char *cmd, char *parse,
410 char *buf, size_t len)
412 AST_DECLARE_APP_ARGS(args,
414 AST_APP_ARG(timezone);
422 if (ast_strlen_zero(parse)) {
424 "Asterisk function STRFTIME() requires an argument.\n");
428 AST_STANDARD_APP_ARGS(args, parse);
430 ast_get_time_t(args.epoch, &epochi, time(NULL), NULL);
431 ast_localtime(&epochi, &tm, args.timezone);
436 if (!strftime(buf, len, args.format, &tm))
437 ast_log(LOG_WARNING, "C function strftime() output nothing?!!\n");
444 static struct ast_custom_function strftime_function = {
446 .synopsis = "Returns the current date/time in a specified format.",
447 .syntax = "STRFTIME([<epoch>][|[timezone][|format]])",
448 .read = acf_strftime,
451 static int acf_strptime(struct ast_channel *chan, char *cmd, char *data,
452 char *buf, size_t len)
454 AST_DECLARE_APP_ARGS(args,
455 AST_APP_ARG(timestring);
456 AST_APP_ARG(timezone);
461 memset(&time, 0, sizeof(struct tm));
467 "Asterisk function STRPTIME() requires an argument.\n");
471 AST_STANDARD_APP_ARGS(args, data);
473 if (ast_strlen_zero(args.format)) {
475 "No format supplied to STRPTIME(<timestring>|<timezone>|<format>)");
479 if (!strptime(args.timestring, args.format, &time)) {
480 ast_log(LOG_WARNING, "C function strptime() output nothing?!!\n");
482 snprintf(buf, len, "%d", (int) ast_mktime(&time, args.timezone));
488 static struct ast_custom_function strptime_function = {
491 "Returns the epoch of the arbitrary date/time string structured as described in the format.",
492 .syntax = "STRPTIME(<datetime>|<timezone>|<format>)",
494 "This is useful for converting a date into an EPOCH time, possibly to pass to\n"
495 "an application like SayUnixTime or to calculate the difference between two\n"
499 " ${STRPTIME(2006-03-01 07:30:35|America/Chicago|%Y-%m-%d %H:%M:%S)} returns 1141219835\n",
500 .read = acf_strptime,
503 static int function_eval(struct ast_channel *chan, char *cmd, char *data,
504 char *buf, size_t len)
508 if (ast_strlen_zero(data)) {
509 ast_log(LOG_WARNING, "EVAL requires an argument: EVAL(<string>)\n");
513 pbx_substitute_variables_helper(chan, data, buf, len - 1);
518 static struct ast_custom_function eval_function = {
520 .synopsis = "Evaluate stored variables.",
521 .syntax = "EVAL(<variable>)",
522 .desc = "Using EVAL basically causes a string to be evaluated twice.\n"
523 "When a variable or expression is in the dialplan, it will be\n"
524 "evaluated at runtime. However, if the result of the evaluation\n"
525 "is in fact a variable or expression, using EVAL will have it\n"
526 "evaluated a second time. For example, if the variable ${MYVAR}\n"
527 "contains \"${OTHERVAR}\", then the result of putting ${EVAL(${MYVAR})}\n"
528 "in the dialplan will be the contents of the variable, OTHERVAR.\n"
529 "Normally, by just putting ${MYVAR} in the dialplan, you would be\n"
530 "left with \"${OTHERVAR}\".\n",
531 .read = function_eval,
534 static int keypadhash(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
536 char *bufptr, *dataptr;
538 for (bufptr = buf, dataptr = data; bufptr < buf + len - 1; dataptr++) {
539 if (*dataptr == '1') {
541 } else if (strchr("AaBbCc2", *dataptr)) {
543 } else if (strchr("DdEeFf3", *dataptr)) {
545 } else if (strchr("GgHhIi4", *dataptr)) {
547 } else if (strchr("JjKkLl5", *dataptr)) {
549 } else if (strchr("MmNnOo6", *dataptr)) {
551 } else if (strchr("PpQqRrSs7", *dataptr)) {
553 } else if (strchr("TtUuVv8", *dataptr)) {
555 } else if (strchr("WwXxYyZz9", *dataptr)) {
557 } else if (*dataptr == '0') {
559 } else if (*dataptr == '\0') {
569 static struct ast_custom_function keypadhash_function = {
570 .name = "KEYPADHASH",
571 .synopsis = "Hash the letters in the string into the equivalent keypad numbers.",
572 .syntax = "KEYPADHASH(<string>)",
574 .desc = "Example: ${KEYPADHASH(Les)} returns \"537\"\n",
577 static int unload_module(void)
581 res |= ast_custom_function_unregister(&fieldqty_function);
582 res |= ast_custom_function_unregister(&filter_function);
583 res |= ast_custom_function_unregister(®ex_function);
584 res |= ast_custom_function_unregister(&array_function);
585 res |= ast_custom_function_unregister("e_function);
586 res |= ast_custom_function_unregister(&len_function);
587 res |= ast_custom_function_unregister(&strftime_function);
588 res |= ast_custom_function_unregister(&strptime_function);
589 res |= ast_custom_function_unregister(&eval_function);
590 res |= ast_custom_function_unregister(&keypadhash_function);
591 res |= ast_custom_function_unregister(&sprintf_function);
596 static int load_module(void)
600 res |= ast_custom_function_register(&fieldqty_function);
601 res |= ast_custom_function_register(&filter_function);
602 res |= ast_custom_function_register(®ex_function);
603 res |= ast_custom_function_register(&array_function);
604 res |= ast_custom_function_register("e_function);
605 res |= ast_custom_function_register(&len_function);
606 res |= ast_custom_function_register(&strftime_function);
607 res |= ast_custom_function_register(&strptime_function);
608 res |= ast_custom_function_register(&eval_function);
609 res |= ast_custom_function_register(&keypadhash_function);
610 res |= ast_custom_function_register(&sprintf_function);
615 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "String handling dialplan functions");