00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #include "asterisk.h"
00029
00030 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 246206 $")
00031
00032 #include <regex.h>
00033 #include <ctype.h>
00034
00035 #include "asterisk/module.h"
00036 #include "asterisk/channel.h"
00037 #include "asterisk/pbx.h"
00038 #include "asterisk/utils.h"
00039 #include "asterisk/app.h"
00040 #include "asterisk/localtime.h"
00041
00042 static int function_fieldqty(struct ast_channel *chan, const char *cmd,
00043 char *parse, char *buf, size_t len)
00044 {
00045 char *varsubst, varval[8192], *varval2 = varval;
00046 int fieldcount = 0;
00047 AST_DECLARE_APP_ARGS(args,
00048 AST_APP_ARG(varname);
00049 AST_APP_ARG(delim);
00050 );
00051 char delim[2] = "";
00052 size_t delim_used;
00053
00054 AST_STANDARD_APP_ARGS(args, parse);
00055 if (args.delim) {
00056 ast_get_encoded_char(args.delim, delim, &delim_used);
00057
00058 varsubst = alloca(strlen(args.varname) + 4);
00059
00060 sprintf(varsubst, "${%s}", args.varname);
00061 pbx_substitute_variables_helper(chan, varsubst, varval, sizeof(varval) - 1);
00062 if (ast_strlen_zero(varval2))
00063 fieldcount = 0;
00064 else {
00065 while (strsep(&varval2, delim))
00066 fieldcount++;
00067 }
00068 } else {
00069 fieldcount = 1;
00070 }
00071 snprintf(buf, len, "%d", fieldcount);
00072
00073 return 0;
00074 }
00075
00076 static struct ast_custom_function fieldqty_function = {
00077 .name = "FIELDQTY",
00078 .synopsis = "Count the fields, with an arbitrary delimiter",
00079 .syntax = "FIELDQTY(<varname>,<delim>)",
00080 .read = function_fieldqty,
00081 };
00082
00083 static int filter(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
00084 size_t len)
00085 {
00086 AST_DECLARE_APP_ARGS(args,
00087 AST_APP_ARG(allowed);
00088 AST_APP_ARG(string);
00089 );
00090 char *outbuf = buf;
00091 unsigned char ac;
00092 char allowed[256] = "";
00093 size_t allowedlen = 0;
00094 int32_t bitfield[8] = { 0, };
00095
00096 AST_STANDARD_RAW_ARGS(args, parse);
00097
00098 if (!args.string) {
00099 ast_log(LOG_ERROR, "Usage: FILTER(<allowed-chars>,<string>)\n");
00100 return -1;
00101 }
00102
00103 if (args.allowed[0] == '"' && !ast_opt_dont_warn) {
00104 ast_log(LOG_WARNING, "FILTER allowed characters includes the quote (\") character. This may not be what you want.\n");
00105 }
00106
00107
00108 for (; *(args.allowed);) {
00109 char c1 = 0, c2 = 0;
00110 size_t consumed = 0;
00111
00112 if (ast_get_encoded_char(args.allowed, &c1, &consumed))
00113 return -1;
00114 args.allowed += consumed;
00115
00116 if (*(args.allowed) == '-') {
00117 if (ast_get_encoded_char(args.allowed + 1, &c2, &consumed))
00118 c2 = -1;
00119 args.allowed += consumed + 1;
00120
00121 if ((c2 < c1 || c2 == -1) && !ast_opt_dont_warn) {
00122 ast_log(LOG_WARNING, "Range wrapping in FILTER(%s,%s). This may not be what you want.\n", parse, args.string);
00123 }
00124
00125
00126
00127
00128
00129 for (ac = c1; ac != c2; ac++) {
00130 bitfield[ac / 32] |= 1 << (ac % 32);
00131 }
00132 bitfield[ac / 32] |= 1 << (ac % 32);
00133
00134 ast_debug(4, "c1=%d, c2=%d\n", c1, c2);
00135 } else {
00136 ac = (unsigned char) c1;
00137 ast_debug(4, "c1=%d, consumed=%d, args.allowed=%s\n", c1, (int) consumed, args.allowed - consumed);
00138 bitfield[ac / 32] |= 1 << (ac % 32);
00139 }
00140 }
00141
00142 for (ac = 1; ac != 0; ac++) {
00143 if (bitfield[ac / 32] & (1 << (ac % 32))) {
00144 allowed[allowedlen++] = ac;
00145 }
00146 }
00147
00148 ast_debug(1, "Allowed: %s\n", allowed);
00149
00150 for (; *(args.string) && (buf + len - 1 > outbuf); (args.string)++) {
00151 if (strchr(allowed, *(args.string)))
00152 *outbuf++ = *(args.string);
00153 }
00154 *outbuf = '\0';
00155
00156 return 0;
00157 }
00158
00159 static struct ast_custom_function filter_function = {
00160 .name = "FILTER",
00161 .synopsis = "Filter the string to include only the allowed characters",
00162 .syntax = "FILTER(<allowed-chars>,<string>)",
00163 .read = filter,
00164 .desc =
00165 "Permits all characters listed in <allowed-chars>, filtering all others out.\n"
00166 "In addition to literally listing the characters, you may also use ranges of\n"
00167 "characters (delimited by a '-'), as well as hexadecimal characters started\n"
00168 "with a \\x (i.e. \\x20) and octal characters started with \\0 (i.e. \\040).\n"
00169 "Also, \\t, \\n, and \\r are recognized. If you want a literal '-' character,\n"
00170 "simply prefix it with a '\\'\n",
00171 };
00172
00173 static int regex(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
00174 size_t len)
00175 {
00176 AST_DECLARE_APP_ARGS(args,
00177 AST_APP_ARG(null);
00178 AST_APP_ARG(reg);
00179 AST_APP_ARG(str);
00180 );
00181 int errcode;
00182 regex_t regexbuf;
00183
00184 buf[0] = '\0';
00185
00186 AST_NONSTANDARD_APP_ARGS(args, parse, '"');
00187
00188 if (args.argc != 3) {
00189 ast_log(LOG_ERROR, "Unexpected arguments: should have been in the form '\"<regex>\" <string>'\n");
00190 return -1;
00191 }
00192 if ((*args.str == ' ') || (*args.str == '\t'))
00193 args.str++;
00194
00195 ast_debug(1, "FUNCTION REGEX (%s)(%s)\n", args.reg, args.str);
00196
00197 if ((errcode = regcomp(®exbuf, args.reg, REG_EXTENDED | REG_NOSUB))) {
00198 regerror(errcode, ®exbuf, buf, len);
00199 ast_log(LOG_WARNING, "Malformed input %s(%s): %s\n", cmd, parse, buf);
00200 return -1;
00201 }
00202
00203 strcpy(buf, regexec(®exbuf, args.str, 0, NULL, 0) ? "0" : "1");
00204
00205 regfree(®exbuf);
00206
00207 return 0;
00208 }
00209
00210 static struct ast_custom_function regex_function = {
00211 .name = "REGEX",
00212 .synopsis = "Regular Expression",
00213 .desc =
00214 "Returns 1 if data matches regular expression, or 0 otherwise.\n"
00215 "Please note that the space following the double quotes separating the regex from the data\n"
00216 "is optional and if present, is skipped. If a space is desired at the beginning of the data,\n"
00217 "then put two spaces there; the second will not be skipped.\n",
00218 .syntax = "REGEX(\"<regular expression>\" <data>)",
00219 .read = regex,
00220 };
00221
00222 #define HASH_PREFIX "~HASH~%s~"
00223 #define HASH_FORMAT HASH_PREFIX "%s~"
00224
00225 static char *app_clearhash = "ClearHash";
00226 static char *syn_clearhash = "Clear the keys from a specified hashname";
00227 static char *desc_clearhash =
00228 "ClearHash(<hashname>)\n"
00229 " Clears all keys out of the specified hashname\n";
00230
00231
00232 static void clearvar_prefix(struct ast_channel *chan, const char *prefix)
00233 {
00234 struct ast_var_t *var;
00235 int len = strlen(prefix);
00236 AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->varshead, var, entries) {
00237 if (strncasecmp(prefix, ast_var_name(var), len) == 0) {
00238 AST_LIST_REMOVE_CURRENT(entries);
00239 ast_free(var);
00240 }
00241 }
00242 AST_LIST_TRAVERSE_SAFE_END
00243 }
00244
00245 static int exec_clearhash(struct ast_channel *chan, void *data)
00246 {
00247 char prefix[80];
00248 snprintf(prefix, sizeof(prefix), HASH_PREFIX, data ? (char *)data : "null");
00249 clearvar_prefix(chan, prefix);
00250 return 0;
00251 }
00252
00253 static int array(struct ast_channel *chan, const char *cmd, char *var,
00254 const char *value)
00255 {
00256 AST_DECLARE_APP_ARGS(arg1,
00257 AST_APP_ARG(var)[100];
00258 );
00259 AST_DECLARE_APP_ARGS(arg2,
00260 AST_APP_ARG(val)[100];
00261 );
00262 char *origvar = "", *value2, varname[256];
00263 int i, ishash = 0;
00264
00265 value2 = ast_strdupa(value);
00266 if (!var || !value2)
00267 return -1;
00268
00269 if (!strcmp(cmd, "HASH")) {
00270 const char *var2 = pbx_builtin_getvar_helper(chan, "~ODBCFIELDS~");
00271 origvar = var;
00272 if (var2)
00273 var = ast_strdupa(var2);
00274 else {
00275 if (chan)
00276 ast_autoservice_stop(chan);
00277 return -1;
00278 }
00279 ishash = 1;
00280 }
00281
00282
00283
00284
00285
00286
00287
00288 ast_debug(1, "array (%s=%s)\n", var, value2);
00289 AST_STANDARD_APP_ARGS(arg1, var);
00290
00291 AST_STANDARD_APP_ARGS(arg2, value2);
00292
00293 for (i = 0; i < arg1.argc; i++) {
00294 ast_debug(1, "array set value (%s=%s)\n", arg1.var[i],
00295 arg2.val[i]);
00296 if (i < arg2.argc) {
00297 if (ishash) {
00298 snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
00299 pbx_builtin_setvar_helper(chan, varname, arg2.val[i]);
00300 } else {
00301 pbx_builtin_setvar_helper(chan, arg1.var[i], arg2.val[i]);
00302 }
00303 } else {
00304
00305
00306 if (ishash) {
00307 snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
00308 pbx_builtin_setvar_helper(chan, varname, "");
00309 } else {
00310 pbx_builtin_setvar_helper(chan, arg1.var[i], "");
00311 }
00312 }
00313 }
00314
00315 return 0;
00316 }
00317
00318 static int hashkeys_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00319 {
00320 struct ast_var_t *newvar;
00321 int plen;
00322 char prefix[80];
00323 snprintf(prefix, sizeof(prefix), HASH_PREFIX, data);
00324 plen = strlen(prefix);
00325
00326 memset(buf, 0, len);
00327 AST_LIST_TRAVERSE(&chan->varshead, newvar, entries) {
00328 if (strncasecmp(prefix, ast_var_name(newvar), plen) == 0) {
00329
00330 strncat(buf, ast_var_name(newvar) + plen, len - strlen(buf) - 1);
00331
00332 buf[strlen(buf) - 1] = ',';
00333 }
00334 }
00335
00336 buf[strlen(buf) - 1] = '\0';
00337 return 0;
00338 }
00339
00340 static int hash_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
00341 {
00342 char varname[256];
00343 AST_DECLARE_APP_ARGS(arg,
00344 AST_APP_ARG(hashname);
00345 AST_APP_ARG(hashkey);
00346 );
00347
00348 if (!strchr(var, ',')) {
00349
00350 return array(chan, "HASH", var, value);
00351 }
00352
00353 AST_STANDARD_APP_ARGS(arg, var);
00354 snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
00355 pbx_builtin_setvar_helper(chan, varname, value);
00356
00357 return 0;
00358 }
00359
00360 static int hash_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00361 {
00362 char varname[256];
00363 const char *varvalue;
00364 AST_DECLARE_APP_ARGS(arg,
00365 AST_APP_ARG(hashname);
00366 AST_APP_ARG(hashkey);
00367 );
00368
00369 AST_STANDARD_APP_ARGS(arg, data);
00370 if (arg.argc == 2) {
00371 snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
00372 varvalue = pbx_builtin_getvar_helper(chan, varname);
00373 if (varvalue)
00374 ast_copy_string(buf, varvalue, len);
00375 else
00376 *buf = '\0';
00377 } else if (arg.argc == 1) {
00378 char colnames[4096];
00379 int i;
00380 AST_DECLARE_APP_ARGS(arg2,
00381 AST_APP_ARG(col)[100];
00382 );
00383
00384
00385 hashkeys_read(chan, "HASHKEYS", arg.hashname, colnames, sizeof(colnames));
00386 pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", colnames);
00387
00388 AST_STANDARD_APP_ARGS(arg2, colnames);
00389 *buf = '\0';
00390
00391
00392 for (i = 0; i < arg2.argc; i++) {
00393 snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg2.col[i]);
00394 varvalue = pbx_builtin_getvar_helper(chan, varname);
00395 strncat(buf, varvalue, len - strlen(buf) - 1);
00396 strncat(buf, ",", len - strlen(buf) - 1);
00397 }
00398
00399
00400 buf[strlen(buf) - 1] = '\0';
00401 }
00402
00403 return 0;
00404 }
00405
00406 static struct ast_custom_function hash_function = {
00407 .name = "HASH",
00408 .synopsis = "Implementation of a dialplan associative array",
00409 .syntax = "HASH(hashname[,hashkey])",
00410 .write = hash_write,
00411 .read = hash_read,
00412 .desc =
00413 "In two argument mode, gets and sets values to corresponding keys within a named\n"
00414 "associative array. The single-argument mode will only work when assigned to from\n"
00415 "a function defined by func_odbc.so.\n",
00416 };
00417
00418 static struct ast_custom_function hashkeys_function = {
00419 .name = "HASHKEYS",
00420 .synopsis = "Retrieve the keys of a HASH()",
00421 .syntax = "HASHKEYS(<hashname>)",
00422 .read = hashkeys_read,
00423 .desc =
00424 "Returns a comma-delimited list of the current keys of an associative array\n"
00425 "defined by the HASH() function. Note that if you iterate over the keys of\n"
00426 "the result, adding keys during iteration will cause the result of the HASHKEYS\n"
00427 "function to change.\n",
00428 };
00429
00430 static struct ast_custom_function array_function = {
00431 .name = "ARRAY",
00432 .synopsis = "Allows setting multiple variables at once",
00433 .syntax = "ARRAY(var1[,var2[...][,varN]])",
00434 .write = array,
00435 .desc =
00436 "The comma-separated list passed as a value to which the function is set will\n"
00437 "be interpreted as a set of values to which the comma-separated list of\n"
00438 "variable names in the argument should be set.\n"
00439 "Hence, Set(ARRAY(var1,var2)=1,2) will set var1 to 1 and var2 to 2.\n",
00440 };
00441
00442 static int acf_sprintf(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00443 {
00444 #define SPRINTF_FLAG 0
00445 #define SPRINTF_WIDTH 1
00446 #define SPRINTF_PRECISION 2
00447 #define SPRINTF_LENGTH 3
00448 #define SPRINTF_CONVERSION 4
00449 int i, state = -1, argcount = 0;
00450 char *formatstart = NULL, *bufptr = buf;
00451 char formatbuf[256] = "";
00452 int tmpi;
00453 double tmpd;
00454 AST_DECLARE_APP_ARGS(arg,
00455 AST_APP_ARG(format);
00456 AST_APP_ARG(var)[100];
00457 );
00458
00459 AST_STANDARD_APP_ARGS(arg, data);
00460
00461
00462 for (i = 0; arg.format[i]; i++) {
00463 switch (state) {
00464 case SPRINTF_FLAG:
00465 if (strchr("#0- +'I", arg.format[i]))
00466 break;
00467 state = SPRINTF_WIDTH;
00468 case SPRINTF_WIDTH:
00469 if (arg.format[i] >= '0' && arg.format[i] <= '9')
00470 break;
00471
00472
00473 if (arg.format[i] == '.') {
00474 state = SPRINTF_PRECISION;
00475 } else {
00476 state = SPRINTF_LENGTH;
00477 i--;
00478 }
00479 break;
00480 case SPRINTF_PRECISION:
00481 if (arg.format[i] >= '0' && arg.format[i] <= '9')
00482 break;
00483 state = SPRINTF_LENGTH;
00484 case SPRINTF_LENGTH:
00485 if (strchr("hl", arg.format[i])) {
00486 if (arg.format[i + 1] == arg.format[i])
00487 i++;
00488 state = SPRINTF_CONVERSION;
00489 break;
00490 } else if (strchr("Lqjzt", arg.format[i])) {
00491 state = SPRINTF_CONVERSION;
00492 break;
00493 }
00494 state = SPRINTF_CONVERSION;
00495 case SPRINTF_CONVERSION:
00496 if (strchr("diouxXc", arg.format[i])) {
00497
00498
00499
00500 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
00501 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
00502
00503
00504 if (arg.var[argcount]) {
00505 if (sscanf(arg.var[argcount++], "%30d", &tmpi) != 1) {
00506 ast_log(LOG_ERROR, "Argument '%s' is not an integer number for format '%s'\n", arg.var[argcount - 1], formatbuf);
00507 goto sprintf_fail;
00508 }
00509 } else {
00510 ast_log(LOG_ERROR, "SPRINTF() has more format specifiers than arguments!\n");
00511 goto sprintf_fail;
00512 }
00513
00514
00515 snprintf(bufptr, buf + len - bufptr, formatbuf, tmpi);
00516
00517
00518 bufptr = strchr(buf, '\0');
00519 } else if (strchr("eEfFgGaA", arg.format[i])) {
00520
00521
00522
00523 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
00524 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
00525
00526
00527 if (arg.var[argcount]) {
00528 if (sscanf(arg.var[argcount++], "%30lf", &tmpd) != 1) {
00529 ast_log(LOG_ERROR, "Argument '%s' is not a floating point number for format '%s'\n", arg.var[argcount - 1], formatbuf);
00530 goto sprintf_fail;
00531 }
00532 } else {
00533 ast_log(LOG_ERROR, "SPRINTF() has more format specifiers than arguments!\n");
00534 goto sprintf_fail;
00535 }
00536
00537
00538 snprintf(bufptr, buf + len - bufptr, formatbuf, tmpd);
00539
00540
00541 bufptr = strchr(buf, '\0');
00542 } else if (arg.format[i] == 's') {
00543
00544
00545
00546 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
00547 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
00548
00549
00550 snprintf(bufptr, buf + len - bufptr, formatbuf, arg.var[argcount++]);
00551
00552
00553 bufptr = strchr(buf, '\0');
00554 } else if (arg.format[i] == '%') {
00555
00556 *bufptr++ = arg.format[i];
00557 } else {
00558
00559
00560
00561 ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
00562 formatbuf[&arg.format[i] - formatstart + 1] = '\0';
00563
00564 ast_log(LOG_ERROR, "Format type not supported: '%s' with argument '%s'\n", formatbuf, arg.var[argcount++]);
00565 goto sprintf_fail;
00566 }
00567 state = -1;
00568 break;
00569 default:
00570 if (arg.format[i] == '%') {
00571 state = SPRINTF_FLAG;
00572 formatstart = &arg.format[i];
00573 break;
00574 } else {
00575
00576 *bufptr++ = arg.format[i];
00577 }
00578 }
00579 }
00580 *bufptr = '\0';
00581 return 0;
00582 sprintf_fail:
00583 return -1;
00584 }
00585
00586 static struct ast_custom_function sprintf_function = {
00587 .name = "SPRINTF",
00588 .synopsis = "Format a variable according to a format string",
00589 .syntax = "SPRINTF(<format>,<arg1>[,...<argN>])",
00590 .read = acf_sprintf,
00591 .desc =
00592 "Parses the format string specified and returns a string matching that format.\n"
00593 "Supports most options supported by sprintf(3). Returns a shortened string if\n"
00594 "a format specifier is not recognized.\n",
00595 };
00596
00597 static int quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00598 {
00599 char *bufptr = buf, *dataptr = data;
00600
00601 if (len < 3){
00602 ast_log(LOG_ERROR, "Not enough buffer");
00603 return -1;
00604 }
00605
00606 if (ast_strlen_zero(data)) {
00607 ast_log(LOG_WARNING, "No argument specified!\n");
00608 ast_copy_string(buf, "\"\"", len);
00609 return 0;
00610 }
00611
00612 *bufptr++ = '"';
00613 for (; bufptr < buf + len - 3; dataptr++) {
00614 if (*dataptr == '\\') {
00615 *bufptr++ = '\\';
00616 *bufptr++ = '\\';
00617 } else if (*dataptr == '"') {
00618 *bufptr++ = '\\';
00619 *bufptr++ = '"';
00620 } else if (*dataptr == '\0') {
00621 break;
00622 } else {
00623 *bufptr++ = *dataptr;
00624 }
00625 }
00626 *bufptr++ = '"';
00627 *bufptr = '\0';
00628 return 0;
00629 }
00630
00631 static struct ast_custom_function quote_function = {
00632 .name = "QUOTE",
00633 .synopsis = "Quotes a given string, escaping embedded quotes as necessary",
00634 .syntax = "QUOTE(<string>)",
00635 .read = quote,
00636 };
00637
00638 static int csv_quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00639 {
00640 char *bufptr = buf, *dataptr = data;
00641
00642 if (len < 3){
00643 ast_log(LOG_ERROR, "Not enough buffer");
00644 return -1;
00645 }
00646
00647 if (ast_strlen_zero(data)) {
00648 ast_log(LOG_WARNING, "No argument specified!\n");
00649 ast_copy_string(buf,"\"\"",len);
00650 return 0;
00651 }
00652
00653 *bufptr++ = '"';
00654 for (; bufptr < buf + len - 3; dataptr++){
00655 if (*dataptr == '"') {
00656 *bufptr++ = '"';
00657 *bufptr++ = '"';
00658 } else if (*dataptr == '\0') {
00659 break;
00660 } else {
00661 *bufptr++ = *dataptr;
00662 }
00663 }
00664 *bufptr++ = '"';
00665 *bufptr='\0';
00666 return 0;
00667 }
00668
00669 static struct ast_custom_function csv_quote_function = {
00670 .name = "CSV_QUOTE",
00671 .read = csv_quote,
00672 };
00673
00674 static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
00675 {
00676 int length = 0;
00677
00678 if (data)
00679 length = strlen(data);
00680
00681 snprintf(buf, buflen, "%d", length);
00682
00683 return 0;
00684 }
00685
00686 static struct ast_custom_function len_function = {
00687 .name = "LEN",
00688 .synopsis = "Returns the length of the argument given",
00689 .syntax = "LEN(<string>)",
00690 .read = len,
00691 };
00692
00693 static int acf_strftime(struct ast_channel *chan, const char *cmd, char *parse,
00694 char *buf, size_t buflen)
00695 {
00696 AST_DECLARE_APP_ARGS(args,
00697 AST_APP_ARG(epoch);
00698 AST_APP_ARG(timezone);
00699 AST_APP_ARG(format);
00700 );
00701 struct timeval when;
00702 struct ast_tm tm;
00703
00704 buf[0] = '\0';
00705
00706 AST_STANDARD_APP_ARGS(args, parse);
00707
00708 ast_get_timeval(args.epoch, &when, ast_tvnow(), NULL);
00709 ast_localtime(&when, &tm, args.timezone);
00710
00711 if (!args.format)
00712 args.format = "%c";
00713
00714 if (ast_strftime(buf, buflen, args.format, &tm) <= 0)
00715 ast_log(LOG_WARNING, "C function strftime() output nothing?!!\n");
00716
00717 buf[buflen - 1] = '\0';
00718
00719 return 0;
00720 }
00721
00722 static struct ast_custom_function strftime_function = {
00723 .name = "STRFTIME",
00724 .synopsis = "Returns the current date/time in a specified format.",
00725 .syntax = "STRFTIME([<epoch>][,[timezone][,format]])",
00726 .desc =
00727 "STRFTIME sports all of the same formats as the underlying C function\n"
00728 "strftime(3) - see the man page for details. It also supports the\n"
00729 "following format:\n"
00730 " %[n]q - fractions of a second, with leading zeroes. For example, %3q will\n"
00731 " give milliseconds and %1q will give tenths of a second. The default\n"
00732 " is to output milliseconds (n=3). The common case is to use it in\n"
00733 " combination with %S, as in \"%S.%3q\".\n",
00734 .read = acf_strftime,
00735 };
00736
00737 static int acf_strptime(struct ast_channel *chan, const char *cmd, char *data,
00738 char *buf, size_t buflen)
00739 {
00740 AST_DECLARE_APP_ARGS(args,
00741 AST_APP_ARG(timestring);
00742 AST_APP_ARG(timezone);
00743 AST_APP_ARG(format);
00744 );
00745 union {
00746 struct ast_tm atm;
00747 struct tm time;
00748 } t = { { 0, }, };
00749
00750 buf[0] = '\0';
00751
00752 if (!data) {
00753 ast_log(LOG_ERROR,
00754 "Asterisk function STRPTIME() requires an argument.\n");
00755 return -1;
00756 }
00757
00758 AST_STANDARD_APP_ARGS(args, data);
00759
00760 if (ast_strlen_zero(args.format)) {
00761 ast_log(LOG_ERROR,
00762 "No format supplied to STRPTIME(<timestring>,<timezone>,<format>)");
00763 return -1;
00764 }
00765
00766 if (!strptime(args.timestring, args.format, &t.time)) {
00767 ast_log(LOG_WARNING, "C function strptime() output nothing?!!\n");
00768 } else {
00769 struct timeval when;
00770
00771 t.atm.tm_isdst = -1;
00772 when = ast_mktime(&t.atm, args.timezone);
00773 snprintf(buf, buflen, "%d", (int) when.tv_sec);
00774 }
00775
00776 return 0;
00777 }
00778
00779 static struct ast_custom_function strptime_function = {
00780 .name = "STRPTIME",
00781 .synopsis =
00782 "Returns the epoch of the arbitrary date/time string structured as described in the format.",
00783 .syntax = "STRPTIME(<datetime>,<timezone>,<format>)",
00784 .desc =
00785 "This is useful for converting a date into an EPOCH time, possibly to pass to\n"
00786 "an application like SayUnixTime or to calculate the difference between two\n"
00787 "date strings.\n"
00788 "\n"
00789 "Example:\n"
00790 " ${STRPTIME(2006-03-01 07:30:35,America/Chicago,%Y-%m-%d %H:%M:%S)} returns 1141219835\n",
00791 .read = acf_strptime,
00792 };
00793
00794 static int function_eval(struct ast_channel *chan, const char *cmd, char *data,
00795 char *buf, size_t buflen)
00796 {
00797 if (ast_strlen_zero(data)) {
00798 ast_log(LOG_WARNING, "EVAL requires an argument: EVAL(<string>)\n");
00799 return -1;
00800 }
00801
00802 pbx_substitute_variables_helper(chan, data, buf, buflen - 1);
00803
00804 return 0;
00805 }
00806
00807 static struct ast_custom_function eval_function = {
00808 .name = "EVAL",
00809 .synopsis = "Evaluate stored variables.",
00810 .syntax = "EVAL(<variable>)",
00811 .desc = "Using EVAL basically causes a string to be evaluated twice.\n"
00812 "When a variable or expression is in the dialplan, it will be\n"
00813 "evaluated at runtime. However, if the result of the evaluation\n"
00814 "is in fact a variable or expression, using EVAL will have it\n"
00815 "evaluated a second time. For example, if the variable ${MYVAR}\n"
00816 "contains \"${OTHERVAR}\", then the result of putting ${EVAL(${MYVAR})}\n"
00817 "in the dialplan will be the contents of the variable, OTHERVAR.\n"
00818 "Normally, by just putting ${MYVAR} in the dialplan, you would be\n"
00819 "left with \"${OTHERVAR}\".\n",
00820 .read = function_eval,
00821 };
00822
00823 static int keypadhash(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
00824 {
00825 char *bufptr, *dataptr;
00826
00827 for (bufptr = buf, dataptr = data; bufptr < buf + buflen - 1; dataptr++) {
00828 if (*dataptr == '\0') {
00829 *bufptr++ = '\0';
00830 break;
00831 } else if (*dataptr == '1') {
00832 *bufptr++ = '1';
00833 } else if (strchr("AaBbCc2", *dataptr)) {
00834 *bufptr++ = '2';
00835 } else if (strchr("DdEeFf3", *dataptr)) {
00836 *bufptr++ = '3';
00837 } else if (strchr("GgHhIi4", *dataptr)) {
00838 *bufptr++ = '4';
00839 } else if (strchr("JjKkLl5", *dataptr)) {
00840 *bufptr++ = '5';
00841 } else if (strchr("MmNnOo6", *dataptr)) {
00842 *bufptr++ = '6';
00843 } else if (strchr("PpQqRrSs7", *dataptr)) {
00844 *bufptr++ = '7';
00845 } else if (strchr("TtUuVv8", *dataptr)) {
00846 *bufptr++ = '8';
00847 } else if (strchr("WwXxYyZz9", *dataptr)) {
00848 *bufptr++ = '9';
00849 } else if (*dataptr == '0') {
00850 *bufptr++ = '0';
00851 }
00852 }
00853 buf[buflen - 1] = '\0';
00854
00855 return 0;
00856 }
00857
00858 static struct ast_custom_function keypadhash_function = {
00859 .name = "KEYPADHASH",
00860 .synopsis = "Hash the letters in the string into the equivalent keypad numbers.",
00861 .syntax = "KEYPADHASH(<string>)",
00862 .read = keypadhash,
00863 .desc = "Example: ${KEYPADHASH(Les)} returns \"537\"\n",
00864 };
00865
00866 static int string_toupper(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
00867 {
00868 char *bufptr = buf, *dataptr = data;
00869
00870 while ((bufptr < buf + buflen - 1) && (*bufptr++ = toupper(*dataptr++)));
00871
00872 return 0;
00873 }
00874
00875 static struct ast_custom_function toupper_function = {
00876 .name = "TOUPPER",
00877 .synopsis = "Convert the string to upper case.",
00878 .syntax = "TOUPPER(<string>)",
00879 .read = string_toupper,
00880 .desc = "Example: ${TOUPPER(Example)} returns \"EXAMPLE\"\n",
00881 };
00882
00883 static int string_tolower(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
00884 {
00885 char *bufptr = buf, *dataptr = data;
00886
00887 while ((bufptr < buf + buflen - 1) && (*bufptr++ = tolower(*dataptr++)));
00888
00889 return 0;
00890 }
00891
00892 static struct ast_custom_function tolower_function = {
00893 .name = "TOLOWER",
00894 .synopsis = "Convert the string to lower case.",
00895 .syntax = "TOLOWER(<string>)",
00896 .read = string_tolower,
00897 .desc = "Example: ${TOLOWER(Example)} returns \"example\"\n",
00898 };
00899
00900 static int unload_module(void)
00901 {
00902 int res = 0;
00903
00904 res |= ast_custom_function_unregister(&fieldqty_function);
00905 res |= ast_custom_function_unregister(&filter_function);
00906 res |= ast_custom_function_unregister(®ex_function);
00907 res |= ast_custom_function_unregister(&array_function);
00908 res |= ast_custom_function_unregister("e_function);
00909 res |= ast_custom_function_unregister(&csv_quote_function);
00910 res |= ast_custom_function_unregister(&len_function);
00911 res |= ast_custom_function_unregister(&strftime_function);
00912 res |= ast_custom_function_unregister(&strptime_function);
00913 res |= ast_custom_function_unregister(&eval_function);
00914 res |= ast_custom_function_unregister(&keypadhash_function);
00915 res |= ast_custom_function_unregister(&sprintf_function);
00916 res |= ast_custom_function_unregister(&hashkeys_function);
00917 res |= ast_custom_function_unregister(&hash_function);
00918 res |= ast_unregister_application(app_clearhash);
00919 res |= ast_custom_function_unregister(&toupper_function);
00920 res |= ast_custom_function_unregister(&tolower_function);
00921
00922 return res;
00923 }
00924
00925 static int load_module(void)
00926 {
00927 int res = 0;
00928
00929 res |= ast_custom_function_register(&fieldqty_function);
00930 res |= ast_custom_function_register(&filter_function);
00931 res |= ast_custom_function_register(®ex_function);
00932 res |= ast_custom_function_register(&array_function);
00933 res |= ast_custom_function_register("e_function);
00934 res |= ast_custom_function_register(&csv_quote_function);
00935 res |= ast_custom_function_register(&len_function);
00936 res |= ast_custom_function_register(&strftime_function);
00937 res |= ast_custom_function_register(&strptime_function);
00938 res |= ast_custom_function_register(&eval_function);
00939 res |= ast_custom_function_register(&keypadhash_function);
00940 res |= ast_custom_function_register(&sprintf_function);
00941 res |= ast_custom_function_register(&hashkeys_function);
00942 res |= ast_custom_function_register(&hash_function);
00943 res |= ast_register_application(app_clearhash, exec_clearhash, syn_clearhash, desc_clearhash);
00944 res |= ast_custom_function_register(&toupper_function);
00945 res |= ast_custom_function_register(&tolower_function);
00946
00947 return res;
00948 }
00949
00950 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "String handling dialplan functions");