Thu Apr 28 2011 17:15:22

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: 293158 $")
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 AST_THREADSTORAGE(result_buf);
00043 
00044 /*** DOCUMENTATION
00045    <function name="FIELDQTY" language="en_US">
00046       <synopsis>
00047          Count the fields with an arbitrary delimiter
00048       </synopsis>
00049       <syntax>
00050          <parameter name="varname" required="true" />
00051          <parameter name="delim" required="true" />
00052       </syntax>
00053       <description>
00054          <para>The delimiter may be specified as a special or extended ASCII character, by encoding it.  The characters
00055          <literal>\n</literal>, <literal>\r</literal>, and <literal>\t</literal> are all recognized as the newline,
00056          carriage return, and tab characters, respectively.  Also, octal and hexadecimal specifications are recognized
00057          by the patterns <literal>\0nnn</literal> and <literal>\xHH</literal>, respectively.  For example, if you wanted
00058          to encode a comma as the delimiter, you could use either <literal>\054</literal> or <literal>\x2C</literal>.</para>
00059          <para>Example: If ${example} contains <literal>ex-amp-le</literal>, then ${FIELDQTY(example,-)} returns 3.</para>
00060       </description>
00061    </function>
00062    <function name="LISTFILTER" language="en_US">
00063       <synopsis>Remove an item from a list, by name.</synopsis>
00064       <syntax>
00065          <parameter name="varname" required="true" />
00066          <parameter name="delim" required="true" default="," />
00067          <parameter name="value" required="true" />
00068       </syntax>
00069       <description>
00070          <para>Remove <replaceable>value</replaceable> from the list contained in the <replaceable>varname</replaceable>
00071          variable, where the list delimiter is specified by the <replaceable>delim</replaceable> parameter.  This is
00072          very useful for removing a single channel name from a list of channels, for example.</para>
00073       </description>
00074    </function>
00075    <function name="FILTER" language="en_US">
00076       <synopsis>
00077          Filter the string to include only the allowed characters
00078       </synopsis>
00079       <syntax>
00080          <parameter name="allowed-chars" required="true" />
00081          <parameter name="string" required="true" />
00082       </syntax>
00083       <description>
00084          <para>Permits all characters listed in <replaceable>allowed-chars</replaceable>, 
00085          filtering all others outs. In addition to literally listing the characters, 
00086          you may also use ranges of characters (delimited by a <literal>-</literal></para>
00087          <para>Hexadecimal characters started with a <literal>\x</literal>(i.e. \x20)</para>
00088          <para>Octal characters started with a <literal>\0</literal> (i.e. \040)</para>
00089          <para>Also <literal>\t</literal>,<literal>\n</literal> and <literal>\r</literal> are recognized.</para> 
00090          <note><para>If you want the <literal>-</literal> character it needs to be prefixed with a 
00091          <literal></literal></para></note>
00092       </description>
00093    </function>
00094    <function name="REGEX" language="en_US">
00095       <synopsis>
00096          Check string against a regular expression.
00097       </synopsis>
00098       <syntax argsep=" ">
00099          <parameter name="&quot;regular expression&quot;" required="true" />
00100          <parameter name="string" required="true" />
00101       </syntax>
00102       <description>
00103          <para>Return <literal>1</literal> on regular expression match or <literal>0</literal> otherwise</para>
00104          <para>Please note that the space following the double quotes separating the 
00105          regex from the data is optional and if present, is skipped. If a space is 
00106          desired at the beginning of the data, then put two spaces there; the second 
00107          will not be skipped.</para>
00108       </description>
00109    </function>
00110    <application name="ClearHash" language="en_US">
00111       <synopsis>
00112          Clear the keys from a specified hashname.
00113       </synopsis>
00114       <syntax>
00115          <parameter name="hashname" required="true" />
00116       </syntax>
00117       <description>
00118          <para>Clears all keys out of the specified <replaceable>hashname</replaceable>.</para>
00119       </description>
00120    </application>
00121    <function name="HASH" language="en_US">
00122       <synopsis>
00123          Implementation of a dialplan associative array
00124       </synopsis>
00125       <syntax>
00126          <parameter name="hashname" required="true" />
00127          <parameter name="hashkey" />
00128       </syntax>
00129       <description>
00130          <para>In two arguments mode, gets and sets values to corresponding keys within
00131          a named associative array. The single-argument mode will only work when assigned
00132          to from a function defined by func_odbc</para>
00133       </description>
00134    </function>
00135    <function name="HASHKEYS" language="en_US">
00136       <synopsis>
00137          Retrieve the keys of the HASH() function.
00138       </synopsis>
00139       <syntax>
00140          <parameter name="hashname" required="true" />
00141       </syntax>
00142       <description>
00143          <para>Returns a comma-delimited list of the current keys of the associative array 
00144          defined by the HASH() function. Note that if you iterate over the keys of 
00145          the result, adding keys during iteration will cause the result of the HASHKEYS()
00146          function to change.</para>
00147       </description>
00148    </function>
00149    <function name="KEYPADHASH" language="en_US">
00150       <synopsis>
00151          Hash the letters in string into equivalent keypad numbers.
00152       </synopsis>
00153       <syntax>
00154          <parameter name="string" required="true" />
00155       </syntax>
00156       <description>
00157          <para>Example: ${KEYPADHASH(Les)} returns "537"</para>
00158       </description>
00159    </function>
00160    <function name="ARRAY" language="en_US">
00161       <synopsis>
00162          Allows setting multiple variables at once.
00163       </synopsis>
00164       <syntax>
00165          <parameter name="var1" required="true" />
00166          <parameter name="var2" required="false" multiple="true" />
00167          <parameter name="varN" required="false" />
00168       </syntax>
00169       <description>
00170          <para>The comma-delimited list passed as a value to which the function is set will 
00171          be interpreted as a set of values to which the comma-delimited list of 
00172          variable names in the argument should be set.</para>
00173          <para>Example: Set(ARRAY(var1,var2)=1,2) will set var1 to 1 and var2 to 2</para>
00174       </description>
00175    </function>
00176    <function name="STRPTIME" language="en_US">
00177       <synopsis>
00178          Returns the epoch of the arbitrary date/time string structured as described by the format.
00179       </synopsis>
00180       <syntax>
00181          <parameter name="datetime" required="true" />
00182          <parameter name="timezone" required="true" />
00183          <parameter name="format" required="true" />
00184       </syntax>
00185       <description>
00186          <para>This is useful for converting a date into <literal>EPOCH</literal> time, 
00187          possibly to pass to an application like SayUnixTime or to calculate the difference
00188          between the two date strings</para>
00189          <para>Example: ${STRPTIME(2006-03-01 07:30:35,America/Chicago,%Y-%m-%d %H:%M:%S)} returns 1141219835</para>
00190       </description>
00191    </function>
00192    <function name="STRFTIME" language="en_US">
00193       <synopsis>
00194          Returns the current date/time in the specified format.
00195       </synopsis>
00196       <syntax>
00197          <parameter name="epoch" />
00198          <parameter name="timezone" />
00199          <parameter name="format" />
00200       </syntax>
00201       <description>
00202          <para>STRFTIME supports all of the same formats as the underlying C function
00203          <emphasis>strftime(3)</emphasis>.
00204          It also supports the following format: <literal>%[n]q</literal> - fractions of a second,
00205          with leading zeros.</para>
00206          <para>Example: <literal>%3q</literal> will give milliseconds and <literal>%1q</literal>
00207          will give tenths of a second. The default is set at milliseconds (n=3).
00208          The common case is to use it in combination with %S, as in <literal>%S.%3q</literal>.</para>
00209       </description>
00210       <see-also>
00211          <ref type="manpage">strftime(3)</ref>
00212       </see-also>
00213    </function>
00214    <function name="EVAL" language="en_US">
00215       <synopsis>
00216          Evaluate stored variables
00217       </synopsis>
00218       <syntax>
00219          <parameter name="variable" required="true" />
00220       </syntax>
00221       <description>
00222          <para>Using EVAL basically causes a string to be evaluated twice.
00223          When a variable or expression is in the dialplan, it will be
00224          evaluated at runtime. However, if the results of the evaluation
00225          is in fact another variable or expression, using EVAL will have it
00226          evaluated a second time.</para>
00227          <para>Example: If the <variable>MYVAR</variable> contains
00228          <variable>OTHERVAR</variable>, then the result of ${EVAL(
00229          <variable>MYVAR</variable>)} in the dialplan will be the
00230          contents of <variable>OTHERVAR</variable>. Normally just
00231          putting <variable>MYVAR</variable> in the dialplan the result
00232          would be <variable>OTHERVAR</variable>.</para>
00233       </description>
00234    </function>
00235    <function name="TOUPPER" language="en_US">
00236       <synopsis>
00237          Convert string to all uppercase letters.
00238       </synopsis>
00239       <syntax>
00240          <parameter name="string" required="true" />
00241       </syntax>
00242       <description>
00243          <para>Example: ${TOUPPER(Example)} returns "EXAMPLE"</para>
00244       </description>
00245    </function>
00246    <function name="TOLOWER" language="en_US">
00247       <synopsis>
00248          Convert string to all lowercase letters.
00249       </synopsis>
00250       <syntax>
00251          <parameter name="string" required="true" />
00252       </syntax>
00253       <description>
00254          <para>Example: ${TOLOWER(Example)} returns "example"</para>
00255       </description>
00256    </function>
00257    <function name="LEN" language="en_US">
00258       <synopsis>
00259          Return the length of the string given.
00260       </synopsis>
00261       <syntax>
00262          <parameter name="string" required="true" />
00263       </syntax>
00264       <description>
00265          <para>Example: ${LEN(example)} returns 7</para>
00266       </description>
00267    </function>
00268    <function name="QUOTE" language="en_US">
00269       <synopsis>
00270          Quotes a given string, escaping embedded quotes as necessary
00271       </synopsis>
00272       <syntax>
00273          <parameter name="string" required="true" />
00274       </syntax>
00275       <description>
00276          <para>Example: ${QUOTE(ab"c"de)} will return "abcde"</para>
00277       </description>
00278    </function>
00279    <function name="CSV_QUOTE" language="en_US">
00280       <synopsis>
00281          Quotes a given string for use in a CSV file, escaping embedded quotes as necessary
00282       </synopsis>
00283       <syntax>
00284          <parameter name="string" required="true" />
00285       </syntax>
00286       <description>
00287          <para>Example: ${CSV_QUOTE("a,b" 123)} will return """a,b"" 123"</para>
00288       </description>
00289    </function>
00290  ***/
00291 
00292 static int function_fieldqty(struct ast_channel *chan, const char *cmd,
00293               char *parse, char *buf, size_t len)
00294 {
00295    char *varsubst, varval[8192], *varval2 = varval;
00296    int fieldcount = 0;
00297    AST_DECLARE_APP_ARGS(args,
00298               AST_APP_ARG(varname);
00299               AST_APP_ARG(delim);
00300       );
00301    char delim[2] = "";
00302    size_t delim_used;
00303 
00304    AST_STANDARD_APP_ARGS(args, parse);
00305    if (args.delim) {
00306       ast_get_encoded_char(args.delim, delim, &delim_used);
00307 
00308       varsubst = alloca(strlen(args.varname) + 4);
00309 
00310       sprintf(varsubst, "${%s}", args.varname);
00311       pbx_substitute_variables_helper(chan, varsubst, varval, sizeof(varval) - 1);
00312       if (ast_strlen_zero(varval2))
00313          fieldcount = 0;
00314       else {
00315          while (strsep(&varval2, delim))
00316             fieldcount++;
00317       }
00318    } else {
00319       fieldcount = 1;
00320    }
00321    snprintf(buf, len, "%d", fieldcount);
00322 
00323    return 0;
00324 }
00325 
00326 static struct ast_custom_function fieldqty_function = {
00327    .name = "FIELDQTY",
00328    .read = function_fieldqty,
00329 };
00330 
00331 static int listfilter(struct ast_channel *chan, const char *cmd, char *parse, char *buf, size_t len)
00332 {
00333    AST_DECLARE_APP_ARGS(args,
00334       AST_APP_ARG(listname);
00335       AST_APP_ARG(delimiter);
00336       AST_APP_ARG(fieldvalue);
00337    );
00338    const char *orig_list, *ptr;
00339    const char *begin, *cur, *next;
00340    int dlen, flen, first = 1;
00341    struct ast_str *result = ast_str_thread_get(&result_buf, 16);
00342    char *delim;
00343 
00344    AST_STANDARD_APP_ARGS(args, parse);
00345 
00346    if (args.argc < 3) {
00347       ast_log(LOG_ERROR, "Usage: LISTFILTER(<listname>,<delimiter>,<fieldvalue>)\n");
00348       return -1;
00349    }
00350 
00351    /* If we don't lock the channel, the variable could disappear out from underneath us. */
00352    if (chan) {
00353       ast_channel_lock(chan);
00354    }
00355    if (!(orig_list = pbx_builtin_getvar_helper(chan, args.listname))) {
00356       ast_log(LOG_ERROR, "List variable '%s' not found\n", args.listname);
00357       if (chan) {
00358          ast_channel_unlock(chan);
00359       }
00360       return -1;
00361    }
00362 
00363    /* If the string isn't there, just copy out the string and be done with it. */
00364    if (!(ptr = strstr(orig_list, args.fieldvalue))) {
00365       ast_copy_string(buf, orig_list, len);
00366       if (chan) {
00367          ast_channel_unlock(chan);
00368       }
00369       return 0;
00370    }
00371 
00372    dlen = strlen(args.delimiter);
00373    delim = alloca(dlen + 1);
00374    ast_get_encoded_str(args.delimiter, delim, dlen + 1);
00375 
00376    if ((dlen = strlen(delim)) == 0) {
00377       delim = ",";
00378       dlen = 1;
00379    }
00380 
00381    flen = strlen(args.fieldvalue);
00382 
00383    ast_str_reset(result);
00384    /* Enough space for any result */
00385    ast_str_make_space(&result, strlen(orig_list) + 1);
00386 
00387    begin = orig_list;
00388    next = strstr(begin, delim);
00389 
00390    do {
00391       /* Find next boundary */
00392       if (next) {
00393          cur = next;
00394          next = strstr(cur + dlen, delim);
00395       } else {
00396          cur = strchr(begin + dlen, '\0');
00397       }
00398 
00399       if (flen == cur - begin && !strncmp(begin, args.fieldvalue, flen)) {
00400          /* Skip field */
00401          begin += flen + dlen;
00402       } else {
00403          /* Copy field to output */
00404          if (!first) {
00405             ast_str_append(&result, 0, "%s", delim);
00406          }
00407 
00408          ast_str_append_substr(&result, 0, begin, cur - begin);
00409          first = 0;
00410          begin = cur + dlen;
00411       }
00412    } while (*cur != '\0');
00413    if (chan) {
00414       ast_channel_unlock(chan);
00415    }
00416 
00417    ast_copy_string(buf, ast_str_buffer(result), len);
00418 
00419    return 0;
00420 }
00421 
00422 static struct ast_custom_function listfilter_function = {
00423    .name = "LISTFILTER",
00424    .read = listfilter,
00425 };
00426 
00427 static int filter(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
00428         size_t len)
00429 {
00430    AST_DECLARE_APP_ARGS(args,
00431               AST_APP_ARG(allowed);
00432               AST_APP_ARG(string);
00433    );
00434    char *outbuf = buf;
00435    unsigned char ac;
00436    char allowed[256] = "";
00437    size_t allowedlen = 0;
00438    int32_t bitfield[8] = { 0, }; /* 256 bits */
00439 
00440    AST_STANDARD_RAW_ARGS(args, parse);
00441 
00442    if (!args.string) {
00443       ast_log(LOG_ERROR, "Usage: FILTER(<allowed-chars>,<string>)\n");
00444       return -1;
00445    }
00446 
00447    if (args.allowed[0] == '"' && !ast_opt_dont_warn) {
00448       ast_log(LOG_WARNING, "FILTER allowed characters includes the quote (\") character.  This may not be what you want.\n");
00449    }
00450 
00451    /* Expand ranges */
00452    for (; *(args.allowed);) {
00453       char c1 = 0, c2 = 0;
00454       size_t consumed = 0;
00455 
00456       if (ast_get_encoded_char(args.allowed, &c1, &consumed))
00457          return -1;
00458       args.allowed += consumed;
00459 
00460       if (*(args.allowed) == '-') {
00461          if (ast_get_encoded_char(args.allowed + 1, &c2, &consumed))
00462             c2 = c1;
00463          args.allowed += consumed + 1;
00464 
00465          if ((unsigned char) c2 < (unsigned char) c1 && !ast_opt_dont_warn) {
00466             ast_log(LOG_WARNING, "Range wrapping in FILTER(%s,%s).  This may not be what you want.\n", parse, args.string);
00467          }
00468 
00469          /*!\note
00470           * Looks a little strange, until you realize that we can overflow
00471           * the size of a char.
00472           */
00473          for (ac = (unsigned char) c1; ac != (unsigned char) c2; ac++) {
00474             bitfield[ac / 32] |= 1 << (ac % 32);
00475          }
00476          bitfield[ac / 32] |= 1 << (ac % 32);
00477 
00478          ast_debug(4, "c1=%d, c2=%d\n", c1, c2);
00479       } else {
00480          ac = (unsigned char) c1;
00481          ast_debug(4, "c1=%d, consumed=%d, args.allowed=%s\n", c1, (int) consumed, args.allowed - consumed);
00482          bitfield[ac / 32] |= 1 << (ac % 32);
00483       }
00484    }
00485 
00486    for (ac = 1; ac != 0; ac++) {
00487       if (bitfield[ac / 32] & (1 << (ac % 32))) {
00488          allowed[allowedlen++] = ac;
00489       }
00490    }
00491 
00492    ast_debug(1, "Allowed: %s\n", allowed);
00493 
00494    for (; *(args.string) && (buf + len - 1 > outbuf); (args.string)++) {
00495       if (strchr(allowed, *(args.string)))
00496          *outbuf++ = *(args.string);
00497    }
00498    *outbuf = '\0';
00499 
00500    return 0;
00501 }
00502 
00503 static struct ast_custom_function filter_function = {
00504    .name = "FILTER",
00505    .read = filter,
00506 };
00507 
00508 static int regex(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
00509        size_t len)
00510 {
00511    AST_DECLARE_APP_ARGS(args,
00512               AST_APP_ARG(null);
00513               AST_APP_ARG(reg);
00514               AST_APP_ARG(str);
00515    );
00516    int errcode;
00517    regex_t regexbuf;
00518 
00519    buf[0] = '\0';
00520 
00521    AST_NONSTANDARD_APP_ARGS(args, parse, '"');
00522 
00523    if (args.argc != 3) {
00524       ast_log(LOG_ERROR, "Unexpected arguments: should have been in the form '\"<regex>\" <string>'\n");
00525       return -1;
00526    }
00527    if ((*args.str == ' ') || (*args.str == '\t'))
00528       args.str++;
00529 
00530    ast_debug(1, "FUNCTION REGEX (%s)(%s)\n", args.reg, args.str);
00531 
00532    if ((errcode = regcomp(&regexbuf, args.reg, REG_EXTENDED | REG_NOSUB))) {
00533       regerror(errcode, &regexbuf, buf, len);
00534       ast_log(LOG_WARNING, "Malformed input %s(%s): %s\n", cmd, parse, buf);
00535       return -1;
00536    }
00537    
00538    strcpy(buf, regexec(&regexbuf, args.str, 0, NULL, 0) ? "0" : "1");
00539 
00540    regfree(&regexbuf);
00541 
00542    return 0;
00543 }
00544 
00545 static struct ast_custom_function regex_function = {
00546    .name = "REGEX",
00547    .read = regex,
00548 };
00549 
00550 #define HASH_PREFIX  "~HASH~%s~"
00551 #define HASH_FORMAT  HASH_PREFIX "%s~"
00552 
00553 static char *app_clearhash = "ClearHash";
00554 
00555 /* This function probably should migrate to main/pbx.c, as pbx_builtin_clearvar_prefix() */
00556 static void clearvar_prefix(struct ast_channel *chan, const char *prefix)
00557 {
00558    struct ast_var_t *var;
00559    int len = strlen(prefix);
00560    AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->varshead, var, entries) {
00561       if (strncasecmp(prefix, ast_var_name(var), len) == 0) {
00562          AST_LIST_REMOVE_CURRENT(entries);
00563          ast_free(var);
00564       }
00565    }
00566    AST_LIST_TRAVERSE_SAFE_END
00567 }
00568 
00569 static int exec_clearhash(struct ast_channel *chan, void *data)
00570 {
00571    char prefix[80];
00572    snprintf(prefix, sizeof(prefix), HASH_PREFIX, data ? (char *)data : "null");
00573    clearvar_prefix(chan, prefix);
00574    return 0;
00575 }
00576 
00577 static int array(struct ast_channel *chan, const char *cmd, char *var,
00578        const char *value)
00579 {
00580    AST_DECLARE_APP_ARGS(arg1,
00581               AST_APP_ARG(var)[100];
00582    );
00583    AST_DECLARE_APP_ARGS(arg2,
00584               AST_APP_ARG(val)[100];
00585    );
00586    char *origvar = "", *value2, varname[256];
00587    int i, ishash = 0;
00588 
00589    value2 = ast_strdupa(value);
00590    if (!var || !value2)
00591       return -1;
00592 
00593    if (!strcmp(cmd, "HASH")) {
00594       const char *var2 = pbx_builtin_getvar_helper(chan, "~ODBCFIELDS~");
00595       origvar = var;
00596       if (var2)
00597          var = ast_strdupa(var2);
00598       else {
00599          if (chan)
00600             ast_autoservice_stop(chan);
00601          return -1;
00602       }
00603       ishash = 1;
00604    }
00605 
00606    /* The functions this will generally be used with are SORT and ODBC_*, which
00607     * both return comma-delimited lists.  However, if somebody uses literal lists,
00608     * their commas will be translated to vertical bars by the load, and I don't
00609     * want them to be surprised by the result.  Hence, we prefer commas as the
00610     * delimiter, but we'll fall back to vertical bars if commas aren't found.
00611     */
00612    ast_debug(1, "array (%s=%s)\n", var, S_OR(value2, ""));
00613    AST_STANDARD_APP_ARGS(arg1, var);
00614 
00615    AST_STANDARD_APP_ARGS(arg2, value2);
00616 
00617    for (i = 0; i < arg1.argc; i++) {
00618       ast_debug(1, "array set value (%s=%s)\n", arg1.var[i],
00619             S_OR(arg2.val[i], ""));
00620       if (i < arg2.argc) {
00621          if (ishash) {
00622             snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
00623             pbx_builtin_setvar_helper(chan, varname, arg2.val[i]);
00624          } else {
00625             pbx_builtin_setvar_helper(chan, arg1.var[i], arg2.val[i]);
00626          }
00627       } else {
00628          /* We could unset the variable, by passing a NULL, but due to
00629           * pushvar semantics, that could create some undesired behavior. */
00630          if (ishash) {
00631             snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
00632             pbx_builtin_setvar_helper(chan, varname, "");
00633          } else {
00634             pbx_builtin_setvar_helper(chan, arg1.var[i], "");
00635          }
00636       }
00637    }
00638 
00639    return 0;
00640 }
00641 
00642 static int hashkeys_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00643 {
00644    struct ast_var_t *newvar;
00645    int plen;
00646    char prefix[80];
00647    snprintf(prefix, sizeof(prefix), HASH_PREFIX, data);
00648    plen = strlen(prefix);
00649 
00650    memset(buf, 0, len);
00651    AST_LIST_TRAVERSE(&chan->varshead, newvar, entries) {
00652       if (strncasecmp(prefix, ast_var_name(newvar), plen) == 0) {
00653          /* Copy everything after the prefix */
00654          strncat(buf, ast_var_name(newvar) + plen, len - strlen(buf) - 1);
00655          /* Trim the trailing ~ */
00656          buf[strlen(buf) - 1] = ',';
00657       }
00658    }
00659    /* Trim the trailing comma */
00660    buf[strlen(buf) - 1] = '\0';
00661    return 0;
00662 }
00663 
00664 static int hash_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
00665 {
00666    char varname[256];
00667    AST_DECLARE_APP_ARGS(arg,
00668       AST_APP_ARG(hashname);
00669       AST_APP_ARG(hashkey);
00670    );
00671 
00672    if (!strchr(var, ',')) {
00673       /* Single argument version */
00674       return array(chan, "HASH", var, value);
00675    }
00676 
00677    AST_STANDARD_APP_ARGS(arg, var);
00678    snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
00679    pbx_builtin_setvar_helper(chan, varname, value);
00680 
00681    return 0;
00682 }
00683 
00684 static int hash_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00685 {
00686    char varname[256];
00687    const char *varvalue;
00688    AST_DECLARE_APP_ARGS(arg,
00689       AST_APP_ARG(hashname);
00690       AST_APP_ARG(hashkey);
00691    );
00692 
00693    AST_STANDARD_APP_ARGS(arg, data);
00694    if (arg.argc == 2) {
00695       snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
00696       varvalue = pbx_builtin_getvar_helper(chan, varname);
00697       if (varvalue)
00698          ast_copy_string(buf, varvalue, len);
00699       else
00700          *buf = '\0';
00701    } else if (arg.argc == 1) {
00702       char colnames[4096];
00703       int i;
00704       AST_DECLARE_APP_ARGS(arg2,
00705          AST_APP_ARG(col)[100];
00706       );
00707 
00708       /* Get column names, in no particular order */
00709       hashkeys_read(chan, "HASHKEYS", arg.hashname, colnames, sizeof(colnames));
00710       pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", colnames);
00711 
00712       AST_STANDARD_APP_ARGS(arg2, colnames);
00713       *buf = '\0';
00714 
00715       /* Now get the corresponding column values, in exactly the same order */
00716       for (i = 0; i < arg2.argc; i++) {
00717          snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg2.col[i]);
00718          varvalue = pbx_builtin_getvar_helper(chan, varname);
00719          strncat(buf, varvalue, len - strlen(buf) - 1);
00720          strncat(buf, ",", len - strlen(buf) - 1);
00721       }
00722 
00723       /* Strip trailing comma */
00724       buf[strlen(buf) - 1] = '\0';
00725    }
00726 
00727    return 0;
00728 }
00729 
00730 static struct ast_custom_function hash_function = {
00731    .name = "HASH",
00732    .write = hash_write,
00733    .read = hash_read,
00734 };
00735 
00736 static struct ast_custom_function hashkeys_function = {
00737    .name = "HASHKEYS",
00738    .read = hashkeys_read,
00739 };
00740 
00741 static struct ast_custom_function array_function = {
00742    .name = "ARRAY",
00743    .write = array,
00744 };
00745 
00746 static int quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00747 {
00748    char *bufptr = buf, *dataptr = data;
00749 
00750    if (len < 3){ /* at least two for quotes and one for binary zero */
00751       ast_log(LOG_ERROR, "Not enough buffer");
00752       return -1;
00753    }
00754 
00755    if (ast_strlen_zero(data)) {
00756       ast_log(LOG_WARNING, "No argument specified!\n");
00757       ast_copy_string(buf, "\"\"", len);
00758       return 0;
00759    }
00760 
00761    *bufptr++ = '"';
00762    for (; bufptr < buf + len - 3; dataptr++) {
00763       if (*dataptr == '\\') {
00764          *bufptr++ = '\\';
00765          *bufptr++ = '\\';
00766       } else if (*dataptr == '"') {
00767          *bufptr++ = '\\';
00768          *bufptr++ = '"';
00769       } else if (*dataptr == '\0') {
00770          break;
00771       } else {
00772          *bufptr++ = *dataptr;
00773       }
00774    }
00775    *bufptr++ = '"';
00776    *bufptr = '\0';
00777    return 0;
00778 }
00779 
00780 static struct ast_custom_function quote_function = {
00781    .name = "QUOTE",
00782    .read = quote,
00783 };
00784 
00785 static int csv_quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00786 {
00787    char *bufptr = buf, *dataptr = data;
00788 
00789    if (len < 3){ /* at least two for quotes and one for binary zero */
00790       ast_log(LOG_ERROR, "Not enough buffer");
00791       return -1;
00792    }
00793 
00794    if (ast_strlen_zero(data)) {
00795       ast_log(LOG_WARNING, "No argument specified!\n");
00796       ast_copy_string(buf,"\"\"",len);
00797       return 0;
00798    }
00799 
00800    *bufptr++ = '"';
00801    for (; bufptr < buf + len - 3; dataptr++){
00802       if (*dataptr == '"') {
00803          *bufptr++ = '"';
00804          *bufptr++ = '"';
00805       } else if (*dataptr == '\0') {
00806          break;
00807       } else {
00808          *bufptr++ = *dataptr;
00809       }
00810    }
00811    *bufptr++ = '"';
00812    *bufptr='\0';
00813    return 0;
00814 }
00815 
00816 static struct ast_custom_function csv_quote_function = {
00817    .name = "CSV_QUOTE",
00818    .read = csv_quote,
00819 };
00820 
00821 static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
00822 {
00823    int length = 0;
00824 
00825    if (data)
00826       length = strlen(data);
00827 
00828    snprintf(buf, buflen, "%d", length);
00829 
00830    return 0;
00831 }
00832 
00833 static struct ast_custom_function len_function = {
00834    .name = "LEN",
00835    .read = len,
00836 };
00837 
00838 static int acf_strftime(struct ast_channel *chan, const char *cmd, char *parse,
00839          char *buf, size_t buflen)
00840 {
00841    AST_DECLARE_APP_ARGS(args,
00842               AST_APP_ARG(epoch);
00843               AST_APP_ARG(timezone);
00844               AST_APP_ARG(format);
00845    );
00846    struct timeval when;
00847    struct ast_tm tm;
00848 
00849    buf[0] = '\0';
00850 
00851    AST_STANDARD_APP_ARGS(args, parse);
00852 
00853    ast_get_timeval(args.epoch, &when, ast_tvnow(), NULL);
00854    ast_localtime(&when, &tm, args.timezone);
00855 
00856    if (!args.format)
00857       args.format = "%c";
00858 
00859    if (ast_strftime(buf, buflen, args.format, &tm) <= 0)
00860       ast_log(LOG_WARNING, "C function strftime() output nothing?!!\n");
00861 
00862    buf[buflen - 1] = '\0';
00863 
00864    return 0;
00865 }
00866 
00867 static struct ast_custom_function strftime_function = {
00868    .name = "STRFTIME",
00869    .read = acf_strftime,
00870 };
00871 
00872 static int acf_strptime(struct ast_channel *chan, const char *cmd, char *data,
00873          char *buf, size_t buflen)
00874 {
00875    AST_DECLARE_APP_ARGS(args,
00876               AST_APP_ARG(timestring);
00877               AST_APP_ARG(timezone);
00878               AST_APP_ARG(format);
00879    );
00880    struct ast_tm tm;
00881 
00882    buf[0] = '\0';
00883 
00884    if (!data) {
00885       ast_log(LOG_ERROR,
00886             "Asterisk function STRPTIME() requires an argument.\n");
00887       return -1;
00888    }
00889 
00890    AST_STANDARD_APP_ARGS(args, data);
00891 
00892    if (ast_strlen_zero(args.format)) {
00893       ast_log(LOG_ERROR,
00894             "No format supplied to STRPTIME(<timestring>,<timezone>,<format>)");
00895       return -1;
00896    }
00897 
00898    if (!ast_strptime(args.timestring, args.format, &tm)) {
00899       ast_log(LOG_WARNING, "STRPTIME() found no time specified within the string\n");
00900    } else {
00901       struct timeval when;
00902       when = ast_mktime(&tm, args.timezone);
00903       snprintf(buf, buflen, "%d", (int) when.tv_sec);
00904    }
00905 
00906    return 0;
00907 }
00908 
00909 static struct ast_custom_function strptime_function = {
00910    .name = "STRPTIME",
00911    .read = acf_strptime,
00912 };
00913 
00914 static int function_eval(struct ast_channel *chan, const char *cmd, char *data,
00915           char *buf, size_t buflen)
00916 {
00917    if (ast_strlen_zero(data)) {
00918       ast_log(LOG_WARNING, "EVAL requires an argument: EVAL(<string>)\n");
00919       return -1;
00920    }
00921 
00922    pbx_substitute_variables_helper(chan, data, buf, buflen - 1);
00923 
00924    return 0;
00925 }
00926 
00927 static struct ast_custom_function eval_function = {
00928    .name = "EVAL",
00929    .read = function_eval,
00930 };
00931 
00932 static int keypadhash(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
00933 {
00934    char *bufptr, *dataptr;
00935 
00936    for (bufptr = buf, dataptr = data; bufptr < buf + buflen - 1; dataptr++) {
00937       if (*dataptr == '\0') {
00938          *bufptr++ = '\0';
00939          break;
00940       } else if (*dataptr == '1') {
00941          *bufptr++ = '1';
00942       } else if (strchr("AaBbCc2", *dataptr)) {
00943          *bufptr++ = '2';
00944       } else if (strchr("DdEeFf3", *dataptr)) {
00945          *bufptr++ = '3';
00946       } else if (strchr("GgHhIi4", *dataptr)) {
00947          *bufptr++ = '4';
00948       } else if (strchr("JjKkLl5", *dataptr)) {
00949          *bufptr++ = '5';
00950       } else if (strchr("MmNnOo6", *dataptr)) {
00951          *bufptr++ = '6';
00952       } else if (strchr("PpQqRrSs7", *dataptr)) {
00953          *bufptr++ = '7';
00954       } else if (strchr("TtUuVv8", *dataptr)) {
00955          *bufptr++ = '8';
00956       } else if (strchr("WwXxYyZz9", *dataptr)) {
00957          *bufptr++ = '9';
00958       } else if (*dataptr == '0') {
00959          *bufptr++ = '0';
00960       }
00961    }
00962    buf[buflen - 1] = '\0';
00963 
00964    return 0;
00965 }
00966 
00967 static struct ast_custom_function keypadhash_function = {
00968    .name = "KEYPADHASH",
00969    .read = keypadhash,
00970 };
00971 
00972 static int string_toupper(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
00973 {
00974    char *bufptr = buf, *dataptr = data;
00975 
00976    while ((bufptr < buf + buflen - 1) && (*bufptr++ = toupper(*dataptr++)));
00977 
00978    return 0;
00979 }
00980 
00981 static struct ast_custom_function toupper_function = {
00982    .name = "TOUPPER",
00983    .read = string_toupper,
00984 };
00985 
00986 static int string_tolower(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
00987 {
00988    char *bufptr = buf, *dataptr = data;
00989 
00990    while ((bufptr < buf + buflen - 1) && (*bufptr++ = tolower(*dataptr++)));
00991 
00992    return 0;
00993 }
00994 
00995 static struct ast_custom_function tolower_function = {
00996    .name = "TOLOWER",
00997    .read = string_tolower,
00998 };
00999 
01000 static int unload_module(void)
01001 {
01002    int res = 0;
01003 
01004    res |= ast_custom_function_unregister(&fieldqty_function);
01005    res |= ast_custom_function_unregister(&filter_function);
01006    res |= ast_custom_function_unregister(&listfilter_function);
01007    res |= ast_custom_function_unregister(&regex_function);
01008    res |= ast_custom_function_unregister(&array_function);
01009    res |= ast_custom_function_unregister(&quote_function);
01010    res |= ast_custom_function_unregister(&csv_quote_function);
01011    res |= ast_custom_function_unregister(&len_function);
01012    res |= ast_custom_function_unregister(&strftime_function);
01013    res |= ast_custom_function_unregister(&strptime_function);
01014    res |= ast_custom_function_unregister(&eval_function);
01015    res |= ast_custom_function_unregister(&keypadhash_function);
01016    res |= ast_custom_function_unregister(&hashkeys_function);
01017    res |= ast_custom_function_unregister(&hash_function);
01018    res |= ast_unregister_application(app_clearhash);
01019    res |= ast_custom_function_unregister(&toupper_function);
01020    res |= ast_custom_function_unregister(&tolower_function);
01021 
01022    return res;
01023 }
01024 
01025 static int load_module(void)
01026 {
01027    int res = 0;
01028 
01029    res |= ast_custom_function_register(&fieldqty_function);
01030    res |= ast_custom_function_register(&filter_function);
01031    res |= ast_custom_function_register(&listfilter_function);
01032    res |= ast_custom_function_register(&regex_function);
01033    res |= ast_custom_function_register(&array_function);
01034    res |= ast_custom_function_register(&quote_function);
01035    res |= ast_custom_function_register(&csv_quote_function);
01036    res |= ast_custom_function_register(&len_function);
01037    res |= ast_custom_function_register(&strftime_function);
01038    res |= ast_custom_function_register(&strptime_function);
01039    res |= ast_custom_function_register(&eval_function);
01040    res |= ast_custom_function_register(&keypadhash_function);
01041    res |= ast_custom_function_register(&hashkeys_function);
01042    res |= ast_custom_function_register(&hash_function);
01043    res |= ast_register_application_xml(app_clearhash, exec_clearhash);
01044    res |= ast_custom_function_register(&toupper_function);
01045    res |= ast_custom_function_register(&tolower_function);
01046 
01047    return res;
01048 }
01049 
01050 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "String handling dialplan functions");