merge new_loader_completion branch, including (at least):
[asterisk/asterisk.git] / funcs / func_strings.c
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  *
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.
13  *
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.
17  */
18
19 /*! \file
20  *
21  * \brief String manipulation dialplan functions
22  *
23  * \author Tilghman Lesher
24  * \author Anothony Minessale II 
25  */
26
27 #include "asterisk.h"
28
29 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
30
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <sys/types.h>
35 #include <regex.h>
36
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"
44
45 static int function_fieldqty(struct ast_channel *chan, char *cmd,
46                              char *parse, char *buf, size_t len)
47 {
48         char *varval;
49         int fieldcount = 0;
50         AST_DECLARE_APP_ARGS(args,
51                              AST_APP_ARG(varname);
52                              AST_APP_ARG(delim);
53                 );
54
55         AST_STANDARD_APP_ARGS(args, parse);
56         if (args.delim) {
57                 pbx_retrieve_variable(chan, args.varname, &varval, buf, len, NULL);
58                 while (strsep(&varval, args.delim))
59                         fieldcount++;
60         } else {
61                 fieldcount = 1;
62         }
63         snprintf(buf, len, "%d", fieldcount);
64
65         return 0;
66 }
67
68 static struct ast_custom_function fieldqty_function = {
69         .name = "FIELDQTY",
70         .synopsis = "Count the fields, with an arbitrary delimiter",
71         .syntax = "FIELDQTY(<varname>|<delim>)",
72         .read = function_fieldqty,
73 };
74
75 static int filter(struct ast_channel *chan, char *cmd, char *parse, char *buf,
76                   size_t len)
77 {
78         AST_DECLARE_APP_ARGS(args,
79                              AST_APP_ARG(allowed);
80                              AST_APP_ARG(string);
81         );
82         char *outbuf = buf;
83
84         AST_STANDARD_APP_ARGS(args, parse);
85
86         if (!args.string) {
87                 ast_log(LOG_ERROR, "Usage: FILTER(<allowed-chars>|<string>)\n");
88                 return -1;
89         }
90
91         for (; *(args.string) && (buf + len - 1 > outbuf); (args.string)++) {
92                 if (strchr(args.allowed, *(args.string)))
93                         *outbuf++ = *(args.string);
94         }
95         *outbuf = '\0';
96
97         return 0;
98 }
99
100 static struct ast_custom_function filter_function = {
101         .name = "FILTER",
102         .synopsis = "Filter the string to include only the allowed characters",
103         .syntax = "FILTER(<allowed-chars>|<string>)",
104         .read = filter,
105 };
106
107 static int regex(struct ast_channel *chan, char *cmd, char *parse, char *buf,
108                  size_t len)
109 {
110         AST_DECLARE_APP_ARGS(args,
111                              AST_APP_ARG(null);
112                              AST_APP_ARG(reg);
113                              AST_APP_ARG(str);
114         );
115         int errcode;
116         regex_t regexbuf;
117
118         buf[0] = '\0';
119
120         AST_NONSTANDARD_APP_ARGS(args, parse, '"');
121
122         if (args.argc != 3) {
123                 ast_log(LOG_ERROR, "Unexpected arguments: should have been in the form '\"<regex>\" <string>'\n");
124                 return -1;
125         }
126
127         ast_log(LOG_DEBUG, "FUNCTION REGEX (%s)(%s)\n", args.reg, args.str);
128
129         if ((errcode = regcomp(&regexbuf, args.reg, REG_EXTENDED | REG_NOSUB))) {
130                 regerror(errcode, &regexbuf, buf, len);
131                 ast_log(LOG_WARNING, "Malformed input %s(%s): %s\n", cmd, parse, buf);
132                 return -1;
133         } else {
134                 if (!regexec(&regexbuf, args.str, 0, NULL, 0))
135                         strcpy(buf, "1");
136         }
137         regfree(&regexbuf);
138
139         return 0;
140 }
141
142 static struct ast_custom_function regex_function = {
143         .name = "REGEX",
144         .synopsis =
145                 "Regular Expression: Returns 1 if data matches regular expression.",
146         .syntax = "REGEX(\"<regular expression>\" <data>)",
147         .read = regex,
148 };
149
150 static int array(struct ast_channel *chan, char *cmd, char *var,
151                  const char *value)
152 {
153         AST_DECLARE_APP_ARGS(arg1,
154                              AST_APP_ARG(var)[100];
155         );
156         AST_DECLARE_APP_ARGS(arg2,
157                              AST_APP_ARG(val)[100];
158         );
159         char *value2;
160         int i;
161
162         value2 = ast_strdupa(value);
163         if (!var || !value2)
164                 return -1;
165
166         /* The functions this will generally be used with are SORT and ODBC_*, which
167          * both return comma-delimited lists.  However, if somebody uses literal lists,
168          * their commas will be translated to vertical bars by the load, and I don't
169          * want them to be surprised by the result.  Hence, we prefer commas as the
170          * delimiter, but we'll fall back to vertical bars if commas aren't found.
171          */
172         ast_log(LOG_DEBUG, "array (%s=%s)\n", var, value2);
173         if (strchr(var, ','))
174                 AST_NONSTANDARD_APP_ARGS(arg1, var, ',');
175         else
176                 AST_STANDARD_APP_ARGS(arg1, var);
177
178         if (strchr(value2, ','))
179                 AST_NONSTANDARD_APP_ARGS(arg2, value2, ',');
180         else
181                 AST_STANDARD_APP_ARGS(arg2, value2);
182
183         for (i = 0; i < arg1.argc; i++) {
184                 ast_log(LOG_DEBUG, "array set value (%s=%s)\n", arg1.var[i],
185                         arg2.val[i]);
186                 if (i < arg2.argc) {
187                         pbx_builtin_setvar_helper(chan, arg1.var[i], arg2.val[i]);
188                 } else {
189                         /* We could unset the variable, by passing a NULL, but due to
190                          * pushvar semantics, that could create some undesired behavior. */
191                         pbx_builtin_setvar_helper(chan, arg1.var[i], "");
192                 }
193         }
194
195         return 0;
196 }
197
198 static struct ast_custom_function array_function = {
199         .name = "ARRAY",
200         .synopsis = "Allows setting multiple variables at once",
201         .syntax = "ARRAY(var1[|var2[...][|varN]])",
202         .write = array,
203         .desc =
204                 "The comma-separated list passed as a value to which the function is set will\n"
205                 "be interpreted as a set of values to which the comma-separated list of\n"
206                 "variable names in the argument should be set.\n"
207                 "Hence, Set(ARRAY(var1|var2)=1\\,2) will set var1 to 1 and var2 to 2\n"
208                 "Note: remember to either backslash your commas in extensions.conf or quote the\n"
209                 "entire argument, since Set can take multiple arguments itself.\n",
210 };
211
212 static int acf_sprintf(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
213 {
214 #define SPRINTF_FLAG    0
215 #define SPRINTF_WIDTH   1
216 #define SPRINTF_PRECISION       2
217 #define SPRINTF_LENGTH  3
218 #define SPRINTF_CONVERSION      4
219         int i, state = -1, argcount = 0;
220         char *formatstart = NULL, *bufptr = buf;
221         char formatbuf[256] = "";
222         int tmpi;
223         double tmpd;
224         AST_DECLARE_APP_ARGS(arg,
225                                 AST_APP_ARG(format);
226                                 AST_APP_ARG(var)[100];
227         );
228
229         AST_STANDARD_APP_ARGS(arg, data);
230
231         /* Scan the format, converting each argument into the requisite format type. */
232         for (i = 0; arg.format[i]; i++) {
233                 switch (state) {
234                 case SPRINTF_FLAG:
235                         if (strchr("#0- +'I", arg.format[i]))
236                                 break;
237                         state = SPRINTF_WIDTH;
238                 case SPRINTF_WIDTH:
239                         if (arg.format[i] >= '0' && arg.format[i] <= '9')
240                                 break;
241
242                         /* Next character must be a period to go into a precision */
243                         if (arg.format[i] == '.') {
244                                 state = SPRINTF_PRECISION;
245                         } else {
246                                 state = SPRINTF_LENGTH;
247                                 i--;
248                         }
249                         break;
250                 case SPRINTF_PRECISION:
251                         if (arg.format[i] >= '0' && arg.format[i] <= '9')
252                                 break;
253                         state = SPRINTF_LENGTH;
254                 case SPRINTF_LENGTH:
255                         if (strchr("hl", arg.format[i])) {
256                                 if (arg.format[i + 1] == arg.format[i])
257                                         i++;
258                                 state = SPRINTF_CONVERSION;
259                                 break;
260                         } else if (strchr("Lqjzt", arg.format[i]))
261                                 state = SPRINTF_CONVERSION;
262                                 break;
263                         state = SPRINTF_CONVERSION;
264                 case SPRINTF_CONVERSION:
265                         if (strchr("diouxXc", arg.format[i])) {
266                                 /* Integer */
267
268                                 /* Isolate this format alone */
269                                 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
270                                 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
271
272                                 /* Convert the argument into the required type */
273                                 if (sscanf(arg.var[argcount++], "%i", &tmpi) != 1) {
274                                         ast_log(LOG_ERROR, "Argument '%s' is not an integer number for format '%s'\n", arg.var[argcount - 1], formatbuf);
275                                         goto sprintf_fail;
276                                 }
277
278                                 /* Format the argument */
279                                 snprintf(bufptr, buf + len - bufptr, formatbuf, tmpi);
280
281                                 /* Update the position of the next parameter to print */
282                                 bufptr = strchr(buf, '\0');
283                         } else if (strchr("eEfFgGaA", arg.format[i])) {
284                                 /* Double */
285
286                                 /* Isolate this format alone */
287                                 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
288                                 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
289
290                                 /* Convert the argument into the required type */
291                                 if (sscanf(arg.var[argcount++], "%lf", &tmpd) != 1) {
292                                         ast_log(LOG_ERROR, "Argument '%s' is not a floating point number for format '%s'\n", arg.var[argcount - 1], formatbuf);
293                                         goto sprintf_fail;
294                                 }
295
296                                 /* Format the argument */
297                                 snprintf(bufptr, buf + len - bufptr, formatbuf, tmpd);
298
299                                 /* Update the position of the next parameter to print */
300                                 bufptr = strchr(buf, '\0');
301                         } else if (arg.format[i] == 's') {
302                                 /* String */
303
304                                 /* Isolate this format alone */
305                                 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
306                                 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
307
308                                 /* Format the argument */
309                                 snprintf(bufptr, buf + len - bufptr, formatbuf, arg.var[argcount++]);
310
311                                 /* Update the position of the next parameter to print */
312                                 bufptr = strchr(buf, '\0');
313                         } else if (arg.format[i] == '%') {
314                                 /* Literal data to copy */
315                                 *bufptr++ = arg.format[i];
316                         } else {
317                                 /* Not supported */
318
319                                 /* Isolate this format alone */
320                                 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
321                                 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
322
323                                 ast_log(LOG_ERROR, "Format type not supported: '%s' with argument '%s'\n", formatbuf, arg.var[argcount++]);
324                                 goto sprintf_fail;
325                         }
326                         state = -1;
327                         break;
328                 default:
329                         if (arg.format[i] == '%') {
330                                 state = SPRINTF_FLAG;
331                                 formatstart = &arg.format[i];
332                                 break;
333                         } else {
334                                 /* Literal data to copy */
335                                 *bufptr++ = arg.format[i];
336                         }
337                 }
338         }
339         return 0;
340 sprintf_fail:
341         return -1;
342 }
343
344 static struct ast_custom_function sprintf_function = {
345         .name = "SPRINTF",
346         .synopsis = "Format a variable according to a format string",
347         .syntax = "SPRINTF(<format>|<arg1>[|...<argN>])",
348         .read = acf_sprintf,
349         .desc =
350 "Parses the format string specified and returns a string matching that format.\n"
351 "Supports most options supported by sprintf(3).  Returns a shortened string if\n"
352 "a format specifier is not recognized.\n",
353 };
354
355 static int quote(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
356 {
357         char *bufptr = buf, *dataptr = data;
358         *bufptr++ = '"';
359         for (; bufptr < buf + len - 1; dataptr++) {
360                 if (*dataptr == '\\') {
361                         *bufptr++ = '\\';
362                         *bufptr++ = '\\';
363                 } else if (*dataptr == '"') {
364                         *bufptr++ = '\\';
365                         *bufptr++ = '"';
366                 } else if (*dataptr == '\0') {
367                         break;
368                 } else {
369                         *bufptr++ = *dataptr;
370                 }
371         }
372         *bufptr++ = '"';
373         *bufptr = '\0';
374         return 0;
375 }
376
377 static struct ast_custom_function quote_function = {
378         .name = "QUOTE",
379         .synopsis = "Quotes a given string, escaping embedded quotes as necessary",
380         .syntax = "QUOTE(<string>)",
381         .read = quote,
382 };
383
384
385 static int len(struct ast_channel *chan, char *cmd, char *data, char *buf,
386                size_t len)
387 {
388         int length = 0;
389
390         if (data)
391                 length = strlen(data);
392
393         snprintf(buf, len, "%d", length);
394
395         return 0;
396 }
397
398 static struct ast_custom_function len_function = {
399         .name = "LEN",
400         .synopsis = "Returns the length of the argument given",
401         .syntax = "LEN(<string>)",
402         .read = len,
403 };
404
405 static int acf_strftime(struct ast_channel *chan, char *cmd, char *parse,
406                         char *buf, size_t len)
407 {
408         AST_DECLARE_APP_ARGS(args,
409                              AST_APP_ARG(epoch);
410                              AST_APP_ARG(timezone);
411                              AST_APP_ARG(format);
412         );
413         time_t epochi;
414         struct tm tm;
415
416         buf[0] = '\0';
417
418         if (ast_strlen_zero(parse)) {
419                 ast_log(LOG_ERROR,
420                                 "Asterisk function STRFTIME() requires an argument.\n");
421                 return -1;
422         }
423
424         AST_STANDARD_APP_ARGS(args, parse);
425
426         ast_get_time_t(args.epoch, &epochi, time(NULL), NULL);
427         ast_localtime(&epochi, &tm, args.timezone);
428
429         if (!args.format)
430                 args.format = "%c";
431
432         if (!strftime(buf, len, args.format, &tm))
433                 ast_log(LOG_WARNING, "C function strftime() output nothing?!!\n");
434
435         buf[len - 1] = '\0';
436
437         return 0;
438 }
439
440 static struct ast_custom_function strftime_function = {
441         .name = "STRFTIME",
442         .synopsis = "Returns the current date/time in a specified format.",
443         .syntax = "STRFTIME([<epoch>][|[timezone][|format]])",
444         .read = acf_strftime,
445 };
446
447 static int acf_strptime(struct ast_channel *chan, char *cmd, char *data,
448                         char *buf, size_t len)
449 {
450         AST_DECLARE_APP_ARGS(args,
451                              AST_APP_ARG(timestring);
452                              AST_APP_ARG(timezone);
453                              AST_APP_ARG(format);
454         );
455         struct tm time;
456
457         memset(&time, 0, sizeof(struct tm));
458
459         buf[0] = '\0';
460
461         if (!data) {
462                 ast_log(LOG_ERROR,
463                                 "Asterisk function STRPTIME() requires an argument.\n");
464                 return -1;
465         }
466
467         AST_STANDARD_APP_ARGS(args, data);
468
469         if (ast_strlen_zero(args.format)) {
470                 ast_log(LOG_ERROR,
471                                 "No format supplied to STRPTIME(<timestring>|<timezone>|<format>)");
472                 return -1;
473         }
474
475         if (!strptime(args.timestring, args.format, &time)) {
476                 ast_log(LOG_WARNING, "C function strptime() output nothing?!!\n");
477         } else {
478                 snprintf(buf, len, "%d", (int) ast_mktime(&time, args.timezone));
479         }
480
481         return 0;
482 }
483
484 static struct ast_custom_function strptime_function = {
485         .name = "STRPTIME",
486         .synopsis =
487                 "Returns the epoch of the arbitrary date/time string structured as described in the format.",
488         .syntax = "STRPTIME(<datetime>|<timezone>|<format>)",
489         .desc =
490                 "This is useful for converting a date into an EPOCH time, possibly to pass to\n"
491                 "an application like SayUnixTime or to calculate the difference between two\n"
492                 "date strings.\n"
493                 "\n"
494                 "Example:\n"
495                 "  ${STRPTIME(2006-03-01 07:30:35|America/Chicago|%Y-%m-%d %H:%M:%S)} returns 1141219835\n",
496         .read = acf_strptime,
497 };
498
499 static int function_eval(struct ast_channel *chan, char *cmd, char *data,
500                          char *buf, size_t len)
501 {
502         buf[0] = '\0';
503
504         if (ast_strlen_zero(data)) {
505                 ast_log(LOG_WARNING, "EVAL requires an argument: EVAL(<string>)\n");
506                 return -1;
507         }
508
509         pbx_substitute_variables_helper(chan, data, buf, len - 1);
510
511         return 0;
512 }
513
514 static struct ast_custom_function eval_function = {
515         .name = "EVAL",
516         .synopsis = "Evaluate stored variables.",
517         .syntax = "EVAL(<variable>)",
518         .desc = "Using EVAL basically causes a string to be evaluated twice.\n"
519                 "When a variable or expression is in the dialplan, it will be\n"
520                 "evaluated at runtime. However, if the result of the evaluation\n"
521                 "is in fact a variable or expression, using EVAL will have it\n"
522                 "evaluated a second time. For example, if the variable ${MYVAR}\n"
523                 "contains \"${OTHERVAR}\", then the result of putting ${EVAL(${MYVAR})}\n"
524                 "in the dialplan will be the contents of the variable, OTHERVAR.\n"
525                 "Normally, by just putting ${MYVAR} in the dialplan, you would be\n"
526                 "left with \"${OTHERVAR}\".\n",
527         .read = function_eval,
528 };
529
530 static int keypadhash(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
531 {
532         char *bufptr, *dataptr;
533
534         for (bufptr = buf, dataptr = data; bufptr < buf + len - 1; dataptr++) {
535                 if (*dataptr == '1') {
536                         *bufptr++ = '1';
537                 } else if (strchr("AaBbCc2", *dataptr)) {
538                         *bufptr++ = '2';
539                 } else if (strchr("DdEeFf3", *dataptr)) {
540                         *bufptr++ = '3';
541                 } else if (strchr("GgHhIi4", *dataptr)) {
542                         *bufptr++ = '4';
543                 } else if (strchr("JjKkLl5", *dataptr)) {
544                         *bufptr++ = '5';
545                 } else if (strchr("MmNnOo6", *dataptr)) {
546                         *bufptr++ = '6';
547                 } else if (strchr("PpQqRrSs7", *dataptr)) {
548                         *bufptr++ = '7';
549                 } else if (strchr("TtUuVv8", *dataptr)) {
550                         *bufptr++ = '8';
551                 } else if (strchr("WwXxYyZz9", *dataptr)) {
552                         *bufptr++ = '9';
553                 } else if (*dataptr == '0') {
554                         *bufptr++ = '0';
555                 } else if (*dataptr == '\0') {
556                         *bufptr++ = '\0';
557                         break;
558                 }
559         }
560         buf[len - 1] = '\0';
561
562         return 0;
563 }
564
565 static struct ast_custom_function keypadhash_function = {
566         .name = "KEYPADHASH",
567         .synopsis = "Hash the letters in the string into the equivalent keypad numbers.",
568         .syntax = "KEYPADHASH(<string>)",
569         .read = keypadhash,
570         .desc = "Example:  ${KEYPADHASH(Les)} returns \"537\"\n",
571 };
572
573 static int unload_module(void)
574 {
575         int res = 0;
576
577         res |= ast_custom_function_unregister(&fieldqty_function);
578         res |= ast_custom_function_unregister(&filter_function);
579         res |= ast_custom_function_unregister(&regex_function);
580         res |= ast_custom_function_unregister(&array_function);
581         res |= ast_custom_function_unregister(&quote_function);
582         res |= ast_custom_function_unregister(&len_function);
583         res |= ast_custom_function_unregister(&strftime_function);
584         res |= ast_custom_function_unregister(&strptime_function);
585         res |= ast_custom_function_unregister(&eval_function);
586         res |= ast_custom_function_unregister(&keypadhash_function);
587         res |= ast_custom_function_unregister(&sprintf_function);
588
589         return res;
590 }
591
592 static int load_module(void)
593 {
594         int res = 0;
595
596         res |= ast_custom_function_register(&fieldqty_function);
597         res |= ast_custom_function_register(&filter_function);
598         res |= ast_custom_function_register(&regex_function);
599         res |= ast_custom_function_register(&array_function);
600         res |= ast_custom_function_register(&quote_function);
601         res |= ast_custom_function_register(&len_function);
602         res |= ast_custom_function_register(&strftime_function);
603         res |= ast_custom_function_register(&strptime_function);
604         res |= ast_custom_function_register(&eval_function);
605         res |= ast_custom_function_register(&keypadhash_function);
606         res |= ast_custom_function_register(&sprintf_function);
607
608         return res;
609 }
610
611 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "String handling dialplan functions");