Wed Aug 18 22:33:52 2010

Asterisk developer's documentation


func_strings.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2005-2006, Digium, Inc.
00005  * Portions Copyright (C) 2005, Tilghman Lesher.  All rights reserved.
00006  * Portions Copyright (C) 2005, Anthony Minessale II
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief String manipulation dialplan functions
00022  *
00023  * \author Tilghman Lesher
00024  * \author Anothony Minessale II 
00025  * \ingroup functions
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, }; /* 256 bits */
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    /* Expand ranges */
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          /*!\note
00126           * Looks a little strange, until you realize that we can overflow
00127           * the size of a char.
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(&regexbuf, args.reg, REG_EXTENDED | REG_NOSUB))) {
00198       regerror(errcode, &regexbuf, 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(&regexbuf, args.str, 0, NULL, 0) ? "0" : "1");
00204 
00205    regfree(&regexbuf);
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 /* This function probably should migrate to main/pbx.c, as pbx_builtin_clearvar_prefix() */
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    /* The functions this will generally be used with are SORT and ODBC_*, which
00283     * both return comma-delimited lists.  However, if somebody uses literal lists,
00284     * their commas will be translated to vertical bars by the load, and I don't
00285     * want them to be surprised by the result.  Hence, we prefer commas as the
00286     * delimiter, but we'll fall back to vertical bars if commas aren't found.
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          /* We could unset the variable, by passing a NULL, but due to
00305           * pushvar semantics, that could create some undesired behavior. */
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          /* Copy everything after the prefix */
00330          strncat(buf, ast_var_name(newvar) + plen, len - strlen(buf) - 1);
00331          /* Trim the trailing ~ */
00332          buf[strlen(buf) - 1] = ',';
00333       }
00334    }
00335    /* Trim the trailing comma */
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       /* Single argument version */
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       /* Get column names, in no particular order */
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       /* Now get the corresponding column values, in exactly the same order */
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       /* Strip trailing comma */
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    /* Scan the format, converting each argument into the requisite format type. */
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          /* Next character must be a period to go into a precision */
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             /* Integer */
00498 
00499             /* Isolate this format alone */
00500             ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
00501             formatbuf[&arg.format[i] - formatstart + 1] = '\0';
00502 
00503             /* Convert the argument into the required type */
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             /* Format the argument */
00515             snprintf(bufptr, buf + len - bufptr, formatbuf, tmpi);
00516 
00517             /* Update the position of the next parameter to print */
00518             bufptr = strchr(buf, '\0');
00519          } else if (strchr("eEfFgGaA", arg.format[i])) {
00520             /* Double */
00521 
00522             /* Isolate this format alone */
00523             ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
00524             formatbuf[&arg.format[i] - formatstart + 1] = '\0';
00525 
00526             /* Convert the argument into the required type */
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             /* Format the argument */
00538             snprintf(bufptr, buf + len - bufptr, formatbuf, tmpd);
00539 
00540             /* Update the position of the next parameter to print */
00541             bufptr = strchr(buf, '\0');
00542          } else if (arg.format[i] == 's') {
00543             /* String */
00544 
00545             /* Isolate this format alone */
00546             ast_copy_string(formatbuf, formatstart, sizeof(formatbuf));
00547             formatbuf[&arg.format[i] - formatstart + 1] = '\0';
00548 
00549             /* Format the argument */
00550             snprintf(bufptr, buf + len - bufptr, formatbuf, arg.var[argcount++]);
00551 
00552             /* Update the position of the next parameter to print */
00553             bufptr = strchr(buf, '\0');
00554          } else if (arg.format[i] == '%') {
00555             /* Literal data to copy */
00556             *bufptr++ = arg.format[i];
00557          } else {
00558             /* Not supported */
00559 
00560             /* Isolate this format alone */
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             /* Literal data to copy */
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){ /* at least two for quotes and one for binary zero */
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){ /* at least two for quotes and one for binary zero */
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       /* Since strptime(3) does not check DST, force ast_mktime() to calculate it. */
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(&regex_function);
00907    res |= ast_custom_function_unregister(&array_function);
00908    res |= ast_custom_function_unregister(&quote_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(&regex_function);
00932    res |= ast_custom_function_register(&array_function);
00933    res |= ast_custom_function_register(&quote_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");

Generated on Wed Aug 18 22:33:52 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7