Mon Oct 8 12:39:02 2012

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 /*** MODULEINFO
00029    <support_level>core</support_level>
00030  ***/
00031 
00032 #include "asterisk.h"
00033 
00034 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 368738 $")
00035 
00036 #include <regex.h>
00037 #include <ctype.h>
00038 
00039 #include "asterisk/module.h"
00040 #include "asterisk/channel.h"
00041 #include "asterisk/pbx.h"
00042 #include "asterisk/utils.h"
00043 #include "asterisk/app.h"
00044 #include "asterisk/localtime.h"
00045 #include "asterisk/test.h"
00046 
00047 AST_THREADSTORAGE(result_buf);
00048 AST_THREADSTORAGE(tmp_buf);
00049 
00050 /*** DOCUMENTATION
00051    <function name="FIELDQTY" language="en_US">
00052       <synopsis>
00053          Count the fields with an arbitrary delimiter
00054       </synopsis>
00055       <syntax>
00056          <parameter name="varname" required="true" />
00057          <parameter name="delim" required="true" />
00058       </syntax>
00059       <description>
00060          <para>The delimiter may be specified as a special or extended ASCII character, by encoding it.  The characters
00061          <literal>\n</literal>, <literal>\r</literal>, and <literal>\t</literal> are all recognized as the newline,
00062          carriage return, and tab characters, respectively.  Also, octal and hexadecimal specifications are recognized
00063          by the patterns <literal>\0nnn</literal> and <literal>\xHH</literal>, respectively.  For example, if you wanted
00064          to encode a comma as the delimiter, you could use either <literal>\054</literal> or <literal>\x2C</literal>.</para>
00065          <para>Example: If ${example} contains <literal>ex-amp-le</literal>, then ${FIELDQTY(example,-)} returns 3.</para>
00066       </description>
00067    </function>
00068    <function name="FIELDNUM" language="en_US">
00069       <synopsis>
00070          Return the 1-based offset of a field in a list
00071       </synopsis>
00072       <syntax>
00073          <parameter name="varname" required="true" />
00074          <parameter name="delim" required="true" />
00075          <parameter name="value" required="true" />
00076       </syntax>
00077       <description>
00078          <para>Search the variable named <replaceable>varname</replaceable> for the string <replaceable>value</replaceable>
00079          delimited by <replaceable>delim</replaceable> and return a 1-based offset as to its location. If not found
00080          or an error occured, return <literal>0</literal>.</para>
00081          <para>The delimiter may be specified as a special or extended ASCII character, by encoding it.  The characters
00082          <literal>\n</literal>, <literal>\r</literal>, and <literal>\t</literal> are all recognized as the newline,
00083          carriage return, and tab characters, respectively.  Also, octal and hexadecimal specifications are recognized
00084          by the patterns <literal>\0nnn</literal> and <literal>\xHH</literal>, respectively.  For example, if you wanted
00085          to encode a comma as the delimiter, you could use either <literal>\054</literal> or <literal>\x2C</literal>.</para>
00086               <para>Example: If ${example} contains <literal>ex-amp-le</literal>, then ${FIELDNUM(example,-,amp)} returns 2.</para>
00087       </description>
00088    </function>
00089    <function name="LISTFILTER" language="en_US">
00090       <synopsis>Remove an item from a list, by name.</synopsis>
00091       <syntax>
00092          <parameter name="varname" required="true" />
00093          <parameter name="delim" required="true" default="," />
00094          <parameter name="value" required="true" />
00095       </syntax>
00096       <description>
00097          <para>Remove <replaceable>value</replaceable> from the list contained in the <replaceable>varname</replaceable>
00098          variable, where the list delimiter is specified by the <replaceable>delim</replaceable> parameter.  This is
00099          very useful for removing a single channel name from a list of channels, for example.</para>
00100       </description>
00101    </function>
00102    <function name="FILTER" language="en_US">
00103       <synopsis>
00104          Filter the string to include only the allowed characters
00105       </synopsis>
00106       <syntax>
00107          <parameter name="allowed-chars" required="true" />
00108          <parameter name="string" required="true" />
00109       </syntax>
00110       <description>
00111          <para>Permits all characters listed in <replaceable>allowed-chars</replaceable>, 
00112          filtering all others outs. In addition to literally listing the characters, 
00113          you may also use ranges of characters (delimited by a <literal>-</literal></para>
00114          <para>Hexadecimal characters started with a <literal>\x</literal>(i.e. \x20)</para>
00115          <para>Octal characters started with a <literal>\0</literal> (i.e. \040)</para>
00116          <para>Also <literal>\t</literal>,<literal>\n</literal> and <literal>\r</literal> are recognized.</para> 
00117          <note><para>If you want the <literal>-</literal> character it needs to be prefixed with a 
00118          <literal></literal></para></note>
00119       </description>
00120    </function>
00121    <function name="REPLACE" language="en_US">
00122       <synopsis>
00123          Replace a set of characters in a given string with another character.
00124       </synopsis>
00125       <syntax>
00126          <parameter name="varname" required="true" />
00127          <parameter name="find-chars" required="true" />
00128          <parameter name="replace-char" required="false" />
00129       </syntax>
00130       <description>
00131          <para>Iterates through a string replacing all the <replaceable>find-chars</replaceable> with
00132          <replaceable>replace-char</replaceable>.  <replaceable>replace-char</replaceable> may be either
00133          empty or contain one character.  If empty, all <replaceable>find-chars</replaceable> will be
00134          deleted from the output.</para>
00135          <note><para>The replacement only occurs in the output.  The original variable is not
00136          altered.</para></note>
00137       </description>
00138    </function>
00139    <function name="PASSTHRU" language="en_US">
00140       <synopsis>
00141          Pass the given argument back as a value.
00142       </synopsis>
00143       <syntax>
00144          <parameter name="string" required="false" />
00145       </syntax>
00146       <description>
00147          <para>Literally returns the given <replaceable>string</replaceable>.  The intent is to permit
00148          other dialplan functions which take a variable name as an argument to be able to take a literal
00149          string, instead.</para>
00150       </description>
00151    </function>
00152    <function name="REGEX" language="en_US">
00153       <synopsis>
00154          Check string against a regular expression.
00155       </synopsis>
00156       <syntax argsep=" ">
00157          <parameter name="&quot;regular expression&quot;" required="true" />
00158          <parameter name="string" required="true" />
00159       </syntax>
00160       <description>
00161          <para>Return <literal>1</literal> on regular expression match or <literal>0</literal> otherwise</para>
00162          <para>Please note that the space following the double quotes separating the 
00163          regex from the data is optional and if present, is skipped. If a space is 
00164          desired at the beginning of the data, then put two spaces there; the second 
00165          will not be skipped.</para>
00166       </description>
00167    </function>
00168    <application name="ClearHash" language="en_US">
00169       <synopsis>
00170          Clear the keys from a specified hashname.
00171       </synopsis>
00172       <syntax>
00173          <parameter name="hashname" required="true" />
00174       </syntax>
00175       <description>
00176          <para>Clears all keys out of the specified <replaceable>hashname</replaceable>.</para>
00177       </description>
00178    </application>
00179    <function name="HASH" language="en_US">
00180       <synopsis>
00181          Implementation of a dialplan associative array
00182       </synopsis>
00183       <syntax>
00184          <parameter name="hashname" required="true" />
00185          <parameter name="hashkey" />
00186       </syntax>
00187       <description>
00188          <para>In two arguments mode, gets and sets values to corresponding keys within
00189          a named associative array. The single-argument mode will only work when assigned
00190          to from a function defined by func_odbc</para>
00191       </description>
00192    </function>
00193    <function name="HASHKEYS" language="en_US">
00194       <synopsis>
00195          Retrieve the keys of the HASH() function.
00196       </synopsis>
00197       <syntax>
00198          <parameter name="hashname" required="true" />
00199       </syntax>
00200       <description>
00201          <para>Returns a comma-delimited list of the current keys of the associative array 
00202          defined by the HASH() function. Note that if you iterate over the keys of 
00203          the result, adding keys during iteration will cause the result of the HASHKEYS()
00204          function to change.</para>
00205       </description>
00206    </function>
00207    <function name="KEYPADHASH" language="en_US">
00208       <synopsis>
00209          Hash the letters in string into equivalent keypad numbers.
00210       </synopsis>
00211       <syntax>
00212          <parameter name="string" required="true" />
00213       </syntax>
00214       <description>
00215          <para>Example: ${KEYPADHASH(Les)} returns "537"</para>
00216       </description>
00217    </function>
00218    <function name="ARRAY" language="en_US">
00219       <synopsis>
00220          Allows setting multiple variables at once.
00221       </synopsis>
00222       <syntax>
00223          <parameter name="var1" required="true" />
00224          <parameter name="var2" required="false" multiple="true" />
00225          <parameter name="varN" required="false" />
00226       </syntax>
00227       <description>
00228          <para>The comma-delimited list passed as a value to which the function is set will 
00229          be interpreted as a set of values to which the comma-delimited list of 
00230          variable names in the argument should be set.</para>
00231          <para>Example: Set(ARRAY(var1,var2)=1,2) will set var1 to 1 and var2 to 2</para>
00232       </description>
00233    </function>
00234    <function name="STRPTIME" language="en_US">
00235       <synopsis>
00236          Returns the epoch of the arbitrary date/time string structured as described by the format.
00237       </synopsis>
00238       <syntax>
00239          <parameter name="datetime" required="true" />
00240          <parameter name="timezone" required="true" />
00241          <parameter name="format" required="true" />
00242       </syntax>
00243       <description>
00244          <para>This is useful for converting a date into <literal>EPOCH</literal> time, 
00245          possibly to pass to an application like SayUnixTime or to calculate the difference
00246          between the two date strings</para>
00247          <para>Example: ${STRPTIME(2006-03-01 07:30:35,America/Chicago,%Y-%m-%d %H:%M:%S)} returns 1141219835</para>
00248       </description>
00249    </function>
00250    <function name="STRFTIME" language="en_US">
00251       <synopsis>
00252          Returns the current date/time in the specified format.
00253       </synopsis>
00254       <syntax>
00255          <parameter name="epoch" />
00256          <parameter name="timezone" />
00257          <parameter name="format" />
00258       </syntax>
00259       <description>
00260          <para>STRFTIME supports all of the same formats as the underlying C function
00261          <emphasis>strftime(3)</emphasis>.
00262          It also supports the following format: <literal>%[n]q</literal> - fractions of a second,
00263          with leading zeros.</para>
00264          <para>Example: <literal>%3q</literal> will give milliseconds and <literal>%1q</literal>
00265          will give tenths of a second. The default is set at milliseconds (n=3).
00266          The common case is to use it in combination with %S, as in <literal>%S.%3q</literal>.</para>
00267       </description>
00268       <see-also>
00269          <ref type="manpage">strftime(3)</ref>
00270       </see-also>
00271    </function>
00272    <function name="EVAL" language="en_US">
00273       <synopsis>
00274          Evaluate stored variables
00275       </synopsis>
00276       <syntax>
00277          <parameter name="variable" required="true" />
00278       </syntax>
00279       <description>
00280          <para>Using EVAL basically causes a string to be evaluated twice.
00281          When a variable or expression is in the dialplan, it will be
00282          evaluated at runtime. However, if the results of the evaluation
00283          is in fact another variable or expression, using EVAL will have it
00284          evaluated a second time.</para>
00285          <para>Example: If the <variable>MYVAR</variable> contains
00286          <variable>OTHERVAR</variable>, then the result of ${EVAL(
00287          <variable>MYVAR</variable>)} in the dialplan will be the
00288          contents of <variable>OTHERVAR</variable>. Normally just
00289          putting <variable>MYVAR</variable> in the dialplan the result
00290          would be <variable>OTHERVAR</variable>.</para>
00291       </description>
00292    </function>
00293    <function name="TOUPPER" language="en_US">
00294       <synopsis>
00295          Convert string to all uppercase letters.
00296       </synopsis>
00297       <syntax>
00298          <parameter name="string" required="true" />
00299       </syntax>
00300       <description>
00301          <para>Example: ${TOUPPER(Example)} returns "EXAMPLE"</para>
00302       </description>
00303    </function>
00304    <function name="TOLOWER" language="en_US">
00305       <synopsis>
00306          Convert string to all lowercase letters.
00307       </synopsis>
00308       <syntax>
00309          <parameter name="string" required="true" />
00310       </syntax>
00311       <description>
00312          <para>Example: ${TOLOWER(Example)} returns "example"</para>
00313       </description>
00314    </function>
00315    <function name="LEN" language="en_US">
00316       <synopsis>
00317          Return the length of the string given.
00318       </synopsis>
00319       <syntax>
00320          <parameter name="string" required="true" />
00321       </syntax>
00322       <description>
00323          <para>Example: ${LEN(example)} returns 7</para>
00324       </description>
00325    </function>
00326    <function name="QUOTE" language="en_US">
00327       <synopsis>
00328          Quotes a given string, escaping embedded quotes as necessary
00329       </synopsis>
00330       <syntax>
00331          <parameter name="string" required="true" />
00332       </syntax>
00333       <description>
00334          <para>Example: ${QUOTE(ab"c"de)} will return "abcde"</para>
00335       </description>
00336    </function>
00337    <function name="CSV_QUOTE" language="en_US">
00338       <synopsis>
00339          Quotes a given string for use in a CSV file, escaping embedded quotes as necessary
00340       </synopsis>
00341       <syntax>
00342          <parameter name="string" required="true" />
00343       </syntax>
00344       <description>
00345          <para>Example: ${CSV_QUOTE("a,b" 123)} will return """a,b"" 123"</para>
00346       </description>
00347    </function>
00348    <function name="SHIFT" language="en_US">
00349       <synopsis>
00350          Removes and returns the first item off of a variable containing delimited text
00351       </synopsis>
00352       <syntax>
00353          <parameter name="varname" required="true" />
00354          <parameter name="delimiter" required="false" default="," />
00355       </syntax>
00356       <description>
00357          <para>Example:</para>
00358          <para>exten => s,1,Set(array=one,two,three)</para>
00359          <para>exten => s,n,While($["${SET(var=${SHIFT(array)})}" != ""])</para>
00360          <para>exten => s,n,NoOp(var is ${var})</para>
00361          <para>exten => s,n,EndWhile</para>
00362          <para>This would iterate over each value in array, left to right, and
00363             would result in NoOp(var is one), NoOp(var is two), and
00364             NoOp(var is three) being executed.
00365          </para>
00366       </description>
00367    </function> 
00368    <function name="POP" language="en_US">
00369       <synopsis>
00370          Removes and returns the last item off of a variable containing delimited text
00371       </synopsis>
00372       <syntax>
00373          <parameter name="varname" required="true" />
00374          <parameter name="delimiter" required="false" default="," />
00375       </syntax>
00376       <description>
00377          <para>Example:</para>
00378          <para>exten => s,1,Set(array=one,two,three)</para>
00379          <para>exten => s,n,While($["${SET(var=${POP(array)})}" != ""])</para>
00380          <para>exten => s,n,NoOp(var is ${var})</para>
00381          <para>exten => s,n,EndWhile</para>
00382          <para>This would iterate over each value in array, right to left, and
00383             would result in NoOp(var is three), NoOp(var is two), and
00384             NoOp(var is one) being executed.
00385          </para>
00386       </description>
00387    </function> 
00388    <function name="PUSH" language="en_US">
00389       <synopsis>
00390          Appends one or more values to the end of a variable containing delimited text
00391       </synopsis>
00392       <syntax>
00393          <parameter name="varname" required="true" />
00394          <parameter name="delimiter" required="false" default="," />
00395       </syntax>
00396       <description>
00397          <para>Example: Set(PUSH(array)=one,two,three) would append one,
00398             two, and three to the end of the values stored in the variable
00399             "array".
00400          </para>
00401       </description>
00402    </function>
00403    <function name="UNSHIFT" language="en_US">
00404       <synopsis>
00405          Inserts one or more values to the beginning of a variable containing delimited text
00406       </synopsis>
00407       <syntax>
00408          <parameter name="varname" required="true" />
00409          <parameter name="delimiter" required="false" default="," />
00410       </syntax>
00411       <description>
00412          <para>Example: Set(UNSHIFT(array)=one,two,three) would insert one,
00413             two, and three before the values stored in the variable
00414             "array".
00415          </para>
00416       </description>
00417    </function>
00418  ***/
00419 
00420 static int function_fieldqty_helper(struct ast_channel *chan, const char *cmd,
00421               char *parse, char *buf, struct ast_str **sbuf, ssize_t len)
00422 {
00423    char *varsubst;
00424    struct ast_str *str = ast_str_thread_get(&result_buf, 16);
00425    int fieldcount = 0;
00426    AST_DECLARE_APP_ARGS(args,
00427               AST_APP_ARG(varname);
00428               AST_APP_ARG(delim);
00429       );
00430    char delim[2] = "";
00431    size_t delim_used;
00432 
00433    if (!str) {
00434       return -1;
00435    }
00436 
00437    AST_STANDARD_APP_ARGS(args, parse);
00438    if (args.delim) {
00439       ast_get_encoded_char(args.delim, delim, &delim_used);
00440 
00441       varsubst = alloca(strlen(args.varname) + 4);
00442 
00443       sprintf(varsubst, "${%s}", args.varname);
00444       ast_str_substitute_variables(&str, 0, chan, varsubst);
00445       if (ast_str_strlen(str) == 0) {
00446          fieldcount = 0;
00447       } else {
00448          char *varval = ast_str_buffer(str);
00449          while (strsep(&varval, delim)) {
00450             fieldcount++;
00451          }
00452       }
00453    } else {
00454       fieldcount = 1;
00455    }
00456    if (sbuf) {
00457       ast_str_set(sbuf, len, "%d", fieldcount);
00458    } else {
00459       snprintf(buf, len, "%d", fieldcount);
00460    }
00461 
00462    return 0;
00463 }
00464 
00465 static int function_fieldqty(struct ast_channel *chan, const char *cmd,
00466               char *parse, char *buf, size_t len)
00467 {
00468    return function_fieldqty_helper(chan, cmd, parse, buf, NULL, len);
00469 }
00470 
00471 static int function_fieldqty_str(struct ast_channel *chan, const char *cmd,
00472              char *parse, struct ast_str **buf, ssize_t len)
00473 {
00474    return function_fieldqty_helper(chan, cmd, parse, NULL, buf, len);
00475 }
00476 
00477 static struct ast_custom_function fieldqty_function = {
00478    .name = "FIELDQTY",
00479    .read = function_fieldqty,
00480    .read2 = function_fieldqty_str,
00481 };
00482 
00483 static int function_fieldnum_helper(struct ast_channel *chan, const char *cmd,
00484             char *parse, char *buf, struct ast_str **sbuf, ssize_t len)
00485 {
00486    char *varsubst, *field;
00487    struct ast_str *str = ast_str_thread_get(&result_buf, 16);
00488    int fieldindex = 0, res = 0;
00489    AST_DECLARE_APP_ARGS(args,
00490       AST_APP_ARG(varname);
00491       AST_APP_ARG(delim);
00492       AST_APP_ARG(field);
00493    );
00494    char delim[2] = "";
00495    size_t delim_used;
00496 
00497    if (!str) {
00498       return -1;
00499    }
00500 
00501    AST_STANDARD_APP_ARGS(args, parse);
00502 
00503    if (args.argc < 3) {
00504       ast_log(LOG_ERROR, "Usage: FIELDNUM(<listname>,<delimiter>,<fieldvalue>)\n");
00505       res = -1;
00506    } else {
00507       varsubst = alloca(strlen(args.varname) + 4);
00508       sprintf(varsubst, "${%s}", args.varname);
00509 
00510       ast_str_substitute_variables(&str, 0, chan, varsubst);
00511 
00512       if (ast_str_strlen(str) == 0 || ast_strlen_zero(args.delim)) {
00513          fieldindex = 0;
00514       } else if (ast_get_encoded_char(args.delim, delim, &delim_used) == -1) {
00515          res = -1;
00516       } else {
00517          char *varval = ast_str_buffer(str);
00518 
00519          while ((field = strsep(&varval, delim)) != NULL) {
00520             fieldindex++;
00521 
00522             if (!strcasecmp(field, args.field)) {
00523                break;
00524             }
00525          }
00526 
00527          if (!field) {
00528             fieldindex = 0;
00529          }
00530 
00531          res = 0;
00532       }
00533    }
00534 
00535    if (sbuf) {
00536       ast_str_set(sbuf, len, "%d", fieldindex);
00537    } else {
00538       snprintf(buf, len, "%d", fieldindex);
00539    }
00540 
00541    return res;
00542 }
00543 
00544 static int function_fieldnum(struct ast_channel *chan, const char *cmd,
00545               char *parse, char *buf, size_t len)
00546 {
00547    return function_fieldnum_helper(chan, cmd, parse, buf, NULL, len);
00548 }
00549 
00550 static int function_fieldnum_str(struct ast_channel *chan, const char *cmd,
00551              char *parse, struct ast_str **buf, ssize_t len)
00552 {
00553    return function_fieldnum_helper(chan, cmd, parse, NULL, buf, len);
00554 }
00555 
00556 static struct ast_custom_function fieldnum_function = {
00557    .name = "FIELDNUM",
00558    .read = function_fieldnum,
00559    .read2 = function_fieldnum_str,
00560 };
00561 
00562 static int listfilter(struct ast_channel *chan, const char *cmd, char *parse, char *buf, struct ast_str **bufstr, ssize_t len)
00563 {
00564    AST_DECLARE_APP_ARGS(args,
00565       AST_APP_ARG(listname);
00566       AST_APP_ARG(delimiter);
00567       AST_APP_ARG(fieldvalue);
00568    );
00569    struct ast_str *orig_list = ast_str_thread_get(&tmp_buf, 16);
00570    const char *begin, *cur, *next;
00571    int dlen, flen, first = 1;
00572    struct ast_str *result, **result_ptr = &result;
00573    char *delim, *varsubst;
00574 
00575    AST_STANDARD_APP_ARGS(args, parse);
00576 
00577    if (buf) {
00578       if (!(result = ast_str_thread_get(&result_buf, 16))) {
00579          return -1;
00580       }
00581    } else {
00582       /* Place the result directly into the output buffer */
00583       result_ptr = bufstr;
00584    }
00585 
00586    if (args.argc < 3) {
00587       ast_log(LOG_ERROR, "Usage: LISTFILTER(<listname>,<delimiter>,<fieldvalue>)\n");
00588       return -1;
00589    }
00590 
00591    varsubst = alloca(strlen(args.listname) + 4);
00592    sprintf(varsubst, "${%s}", args.listname);
00593 
00594    /* If we don't lock the channel, the variable could disappear out from underneath us. */
00595    if (chan) {
00596       ast_channel_lock(chan);
00597    }
00598    ast_str_substitute_variables(&orig_list, 0, chan, varsubst);
00599    if (!ast_str_strlen(orig_list)) {
00600       ast_log(LOG_ERROR, "List variable '%s' not found\n", args.listname);
00601       if (chan) {
00602          ast_channel_unlock(chan);
00603       }
00604       return -1;
00605    }
00606 
00607    /* If the string isn't there, just copy out the string and be done with it. */
00608    if (!strstr(ast_str_buffer(orig_list), args.fieldvalue)) {
00609       if (buf) {
00610          ast_copy_string(buf, ast_str_buffer(orig_list), len);
00611       } else {
00612          ast_str_set(result_ptr, len, "%s", ast_str_buffer(orig_list));
00613       }
00614       if (chan) {
00615          ast_channel_unlock(chan);
00616       }
00617       return 0;
00618    }
00619 
00620    dlen = strlen(args.delimiter);
00621    delim = alloca(dlen + 1);
00622    ast_get_encoded_str(args.delimiter, delim, dlen + 1);
00623 
00624    if ((dlen = strlen(delim)) == 0) {
00625       delim = ",";
00626       dlen = 1;
00627    }
00628 
00629    flen = strlen(args.fieldvalue);
00630 
00631    ast_str_reset(*result_ptr);
00632    /* Enough space for any result */
00633    if (len > -1) {
00634       ast_str_make_space(result_ptr, len ? len : ast_str_strlen(orig_list) + 1);
00635    }
00636 
00637    begin = ast_str_buffer(orig_list);
00638    next = strstr(begin, delim);
00639 
00640    do {
00641       /* Find next boundary */
00642       if (next) {
00643          cur = next;
00644          next = strstr(cur + dlen, delim);
00645       } else {
00646          cur = strchr(begin + dlen, '\0');
00647       }
00648 
00649       if (flen == cur - begin && !strncmp(begin, args.fieldvalue, flen)) {
00650          /* Skip field */
00651          begin += flen + dlen;
00652       } else {
00653          /* Copy field to output */
00654          if (!first) {
00655             ast_str_append(result_ptr, len, "%s", delim);
00656          }
00657 
00658          ast_str_append_substr(result_ptr, len, begin, cur - begin);
00659          first = 0;
00660          begin = cur + dlen;
00661       }
00662    } while (*cur != '\0');
00663    if (chan) {
00664       ast_channel_unlock(chan);
00665    }
00666 
00667    if (buf) {
00668       ast_copy_string(buf, ast_str_buffer(result), len);
00669    }
00670 
00671    return 0;
00672 }
00673 
00674 static int listfilter_read(struct ast_channel *chan, const char *cmd, char *parse, char *buf, size_t len)
00675 {
00676    return listfilter(chan, cmd, parse, buf, NULL, len);
00677 }
00678 
00679 static int listfilter_read2(struct ast_channel *chan, const char *cmd, char *parse, struct ast_str **buf, ssize_t len)
00680 {
00681    return listfilter(chan, cmd, parse, NULL, buf, len);
00682 }
00683 
00684 static struct ast_custom_function listfilter_function = {
00685    .name = "LISTFILTER",
00686    .read = listfilter_read,
00687    .read2 = listfilter_read2,
00688 };
00689 
00690 static int filter(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
00691         size_t len)
00692 {
00693    AST_DECLARE_APP_ARGS(args,
00694               AST_APP_ARG(allowed);
00695               AST_APP_ARG(string);
00696    );
00697    char *outbuf = buf;
00698    unsigned char ac;
00699    char allowed[256] = "";
00700    size_t allowedlen = 0;
00701    int32_t bitfield[8] = { 0, }; /* 256 bits */
00702 
00703    AST_STANDARD_RAW_ARGS(args, parse);
00704 
00705    if (!args.string) {
00706       ast_log(LOG_ERROR, "Usage: FILTER(<allowed-chars>,<string>)\n");
00707       return -1;
00708    }
00709 
00710    if (args.allowed[0] == '"' && !ast_opt_dont_warn) {
00711       ast_log(LOG_WARNING, "FILTER allowed characters includes the quote (\") character.  This may not be what you want.\n");
00712    }
00713 
00714    /* Expand ranges */
00715    for (; *(args.allowed);) {
00716       char c1 = 0, c2 = 0;
00717       size_t consumed = 0;
00718 
00719       if (ast_get_encoded_char(args.allowed, &c1, &consumed))
00720          return -1;
00721       args.allowed += consumed;
00722 
00723       if (*(args.allowed) == '-') {
00724          if (ast_get_encoded_char(args.allowed + 1, &c2, &consumed))
00725             c2 = c1;
00726          args.allowed += consumed + 1;
00727 
00728          if ((unsigned char) c2 < (unsigned char) c1 && !ast_opt_dont_warn) {
00729             ast_log(LOG_WARNING, "Range wrapping in FILTER(%s,%s).  This may not be what you want.\n", parse, args.string);
00730          }
00731 
00732          /*!\note
00733           * Looks a little strange, until you realize that we can overflow
00734           * the size of a char.
00735           */
00736          for (ac = (unsigned char) c1; ac != (unsigned char) c2; ac++) {
00737             bitfield[ac / 32] |= 1 << (ac % 32);
00738          }
00739          bitfield[ac / 32] |= 1 << (ac % 32);
00740 
00741          ast_debug(4, "c1=%d, c2=%d\n", c1, c2);
00742       } else {
00743          ac = (unsigned char) c1;
00744          ast_debug(4, "c1=%d, consumed=%d, args.allowed=%s\n", c1, (int) consumed, args.allowed - consumed);
00745          bitfield[ac / 32] |= 1 << (ac % 32);
00746       }
00747    }
00748 
00749    for (ac = 1; ac != 0; ac++) {
00750       if (bitfield[ac / 32] & (1 << (ac % 32))) {
00751          allowed[allowedlen++] = ac;
00752       }
00753    }
00754 
00755    ast_debug(1, "Allowed: %s\n", allowed);
00756 
00757    for (; *(args.string) && (buf + len - 1 > outbuf); (args.string)++) {
00758       if (strchr(allowed, *(args.string)))
00759          *outbuf++ = *(args.string);
00760    }
00761    *outbuf = '\0';
00762 
00763    return 0;
00764 }
00765 
00766 static struct ast_custom_function filter_function = {
00767    .name = "FILTER",
00768    .read = filter,
00769 };
00770 
00771 static int replace(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
00772 {
00773    AST_DECLARE_APP_ARGS(args,
00774       AST_APP_ARG(varname);
00775       AST_APP_ARG(find);
00776       AST_APP_ARG(replace);
00777    );
00778    char *strptr, *varsubst;
00779    struct ast_str *str = ast_str_thread_get(&result_buf, 16);
00780    char find[256]; /* Only 256 characters possible */
00781    char replace[2] = "";
00782    size_t unused;
00783 
00784    AST_STANDARD_APP_ARGS(args, data);
00785 
00786    if (!str) {
00787       return -1;
00788    }
00789 
00790    if (args.argc < 2) {
00791       ast_log(LOG_ERROR, "Usage: %s(<varname>,<search-chars>[,<replace-char>])\n", cmd);
00792       return -1;
00793    }
00794 
00795    /* Decode escapes */
00796    ast_get_encoded_str(args.find, find, sizeof(find));
00797    ast_get_encoded_char(args.replace, replace, &unused);
00798 
00799    if (ast_strlen_zero(find) || ast_strlen_zero(args.varname)) {
00800       ast_log(LOG_ERROR, "The characters to search for and the variable name must not be empty.\n");
00801       return -1;
00802    }
00803 
00804    varsubst = alloca(strlen(args.varname) + 4);
00805    sprintf(varsubst, "${%s}", args.varname);
00806    ast_str_substitute_variables(&str, 0, chan, varsubst);
00807 
00808    if (!ast_str_strlen(str)) {
00809       /* Blank, nothing to replace */
00810       return -1;
00811    }
00812 
00813    ast_debug(3, "String to search: (%s)\n", ast_str_buffer(str));
00814    ast_debug(3, "Characters to find: (%s)\n", find);
00815    ast_debug(3, "Character to replace with: (%s)\n", replace);
00816 
00817    for (strptr = ast_str_buffer(str); *strptr; strptr++) {
00818       /* buf is already a mutable buffer, so we construct the result
00819        * directly there */
00820       if (strchr(find, *strptr)) {
00821          if (ast_strlen_zero(replace)) {
00822             /* Remove character */
00823             strcpy(strptr, strptr + 1); /* SAFE */
00824             strptr--;
00825          } else {
00826             /* Replace character */
00827             *strptr = *replace;
00828          }
00829       }
00830    }
00831 
00832    ast_str_set(buf, len, "%s", ast_str_buffer(str));
00833    return 0;
00834 }
00835 
00836 static struct ast_custom_function replace_function = {
00837    .name = "REPLACE",
00838    .read2 = replace,
00839 };
00840 
00841 static int regex(struct ast_channel *chan, const char *cmd, char *parse, char *buf,
00842        size_t len)
00843 {
00844    AST_DECLARE_APP_ARGS(args,
00845               AST_APP_ARG(null);
00846               AST_APP_ARG(reg);
00847               AST_APP_ARG(str);
00848    );
00849    int errcode;
00850    regex_t regexbuf;
00851 
00852    buf[0] = '\0';
00853 
00854    AST_NONSTANDARD_APP_ARGS(args, parse, '"');
00855 
00856    if (args.argc != 3) {
00857       ast_log(LOG_ERROR, "Unexpected arguments: should have been in the form '\"<regex>\" <string>'\n");
00858       return -1;
00859    }
00860    if ((*args.str == ' ') || (*args.str == '\t'))
00861       args.str++;
00862 
00863    ast_debug(1, "FUNCTION REGEX (%s)(%s)\n", args.reg, args.str);
00864 
00865    if ((errcode = regcomp(&regexbuf, args.reg, REG_EXTENDED | REG_NOSUB))) {
00866       regerror(errcode, &regexbuf, buf, len);
00867       ast_log(LOG_WARNING, "Malformed input %s(%s): %s\n", cmd, parse, buf);
00868       return -1;
00869    }
00870    
00871    strcpy(buf, regexec(&regexbuf, args.str, 0, NULL, 0) ? "0" : "1");
00872 
00873    regfree(&regexbuf);
00874 
00875    return 0;
00876 }
00877 
00878 static struct ast_custom_function regex_function = {
00879    .name = "REGEX",
00880    .read = regex,
00881 };
00882 
00883 #define HASH_PREFIX  "~HASH~%s~"
00884 #define HASH_FORMAT  HASH_PREFIX "%s~"
00885 
00886 static char *app_clearhash = "ClearHash";
00887 
00888 /* This function probably should migrate to main/pbx.c, as pbx_builtin_clearvar_prefix() */
00889 static void clearvar_prefix(struct ast_channel *chan, const char *prefix)
00890 {
00891    struct ast_var_t *var;
00892    int len = strlen(prefix);
00893    AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->varshead, var, entries) {
00894       if (strncasecmp(prefix, ast_var_name(var), len) == 0) {
00895          AST_LIST_REMOVE_CURRENT(entries);
00896          ast_free(var);
00897       }
00898    }
00899    AST_LIST_TRAVERSE_SAFE_END
00900 }
00901 
00902 static int exec_clearhash(struct ast_channel *chan, const char *data)
00903 {
00904    char prefix[80];
00905    snprintf(prefix, sizeof(prefix), HASH_PREFIX, data ? (char *)data : "null");
00906    clearvar_prefix(chan, prefix);
00907    return 0;
00908 }
00909 
00910 static int array(struct ast_channel *chan, const char *cmd, char *var,
00911        const char *value)
00912 {
00913    AST_DECLARE_APP_ARGS(arg1,
00914               AST_APP_ARG(var)[100];
00915    );
00916    AST_DECLARE_APP_ARGS(arg2,
00917               AST_APP_ARG(val)[100];
00918    );
00919    char *origvar = "", *value2, varname[256];
00920    int i, ishash = 0;
00921 
00922    value2 = ast_strdupa(value);
00923    if (!var || !value2)
00924       return -1;
00925 
00926    if (!strcmp(cmd, "HASH")) {
00927       const char *var2 = pbx_builtin_getvar_helper(chan, "~ODBCFIELDS~");
00928       origvar = var;
00929       if (var2)
00930          var = ast_strdupa(var2);
00931       else {
00932          if (chan)
00933             ast_autoservice_stop(chan);
00934          return -1;
00935       }
00936       ishash = 1;
00937    }
00938 
00939    /* The functions this will generally be used with are SORT and ODBC_*, which
00940     * both return comma-delimited lists.  However, if somebody uses literal lists,
00941     * their commas will be translated to vertical bars by the load, and I don't
00942     * want them to be surprised by the result.  Hence, we prefer commas as the
00943     * delimiter, but we'll fall back to vertical bars if commas aren't found.
00944     */
00945    ast_debug(1, "array (%s=%s)\n", var, S_OR(value2, ""));
00946    AST_STANDARD_APP_ARGS(arg1, var);
00947 
00948    AST_STANDARD_APP_ARGS(arg2, value2);
00949 
00950    for (i = 0; i < arg1.argc; i++) {
00951       ast_debug(1, "array set value (%s=%s)\n", arg1.var[i],
00952             S_OR(arg2.val[i], ""));
00953       if (i < arg2.argc) {
00954          if (ishash) {
00955             if (origvar[0] == '_') {
00956                if (origvar[1] == '_') {
00957                   snprintf(varname, sizeof(varname), "__" HASH_FORMAT, origvar + 2, arg1.var[i]);
00958                } else {
00959                   snprintf(varname, sizeof(varname), "_" HASH_FORMAT, origvar + 1, arg1.var[i]);
00960                }
00961             } else {
00962                snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
00963             }
00964 
00965             pbx_builtin_setvar_helper(chan, varname, arg2.val[i]);
00966          } else {
00967             pbx_builtin_setvar_helper(chan, arg1.var[i], arg2.val[i]);
00968          }
00969       } else {
00970          /* We could unset the variable, by passing a NULL, but due to
00971           * pushvar semantics, that could create some undesired behavior. */
00972          if (ishash) {
00973             snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]);
00974             pbx_builtin_setvar_helper(chan, varname, "");
00975          } else {
00976             pbx_builtin_setvar_helper(chan, arg1.var[i], "");
00977          }
00978       }
00979    }
00980 
00981    return 0;
00982 }
00983 
00984 static int hashkeys_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00985 {
00986    struct ast_var_t *newvar;
00987    struct ast_str *prefix = ast_str_alloca(80);
00988 
00989    ast_str_set(&prefix, -1, HASH_PREFIX, data);
00990    memset(buf, 0, len);
00991 
00992    AST_LIST_TRAVERSE(&chan->varshead, newvar, entries) {
00993       if (strncasecmp(ast_str_buffer(prefix), ast_var_name(newvar), ast_str_strlen(prefix)) == 0) {
00994          /* Copy everything after the prefix */
00995          strncat(buf, ast_var_name(newvar) + ast_str_strlen(prefix), len - strlen(buf) - 1);
00996          /* Trim the trailing ~ */
00997          buf[strlen(buf) - 1] = ',';
00998       }
00999    }
01000    /* Trim the trailing comma */
01001    buf[strlen(buf) - 1] = '\0';
01002    return 0;
01003 }
01004 
01005 static int hashkeys_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
01006 {
01007    struct ast_var_t *newvar;
01008    struct ast_str *prefix = ast_str_alloca(80);
01009    char *tmp;
01010 
01011    ast_str_set(&prefix, -1, HASH_PREFIX, data);
01012 
01013    AST_LIST_TRAVERSE(&chan->varshead, newvar, entries) {
01014       if (strncasecmp(ast_str_buffer(prefix), ast_var_name(newvar), ast_str_strlen(prefix)) == 0) {
01015          /* Copy everything after the prefix */
01016          ast_str_append(buf, len, "%s", ast_var_name(newvar) + ast_str_strlen(prefix));
01017          /* Trim the trailing ~ */
01018          tmp = ast_str_buffer(*buf);
01019          tmp[ast_str_strlen(*buf) - 1] = ',';
01020       }
01021    }
01022    /* Trim the trailing comma */
01023    tmp = ast_str_buffer(*buf);
01024    tmp[ast_str_strlen(*buf) - 1] = '\0';
01025    return 0;
01026 }
01027 
01028 static int hash_write(struct ast_channel *chan, const char *cmd, char *var, const char *value)
01029 {
01030    char varname[256];
01031    AST_DECLARE_APP_ARGS(arg,
01032       AST_APP_ARG(hashname);
01033       AST_APP_ARG(hashkey);
01034    );
01035 
01036    if (!strchr(var, ',')) {
01037       /* Single argument version */
01038       return array(chan, "HASH", var, value);
01039    }
01040 
01041    AST_STANDARD_APP_ARGS(arg, var);
01042    if (arg.hashname[0] == '_') {
01043       if (arg.hashname[1] == '_') {
01044          snprintf(varname, sizeof(varname), "__" HASH_FORMAT, arg.hashname + 2, arg.hashkey);
01045       } else {
01046          snprintf(varname, sizeof(varname), "_" HASH_FORMAT, arg.hashname + 1, arg.hashkey);
01047       }
01048    } else {
01049       snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
01050    }
01051    pbx_builtin_setvar_helper(chan, varname, value);
01052 
01053    return 0;
01054 }
01055 
01056 static int hash_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
01057 {
01058    char varname[256];
01059    const char *varvalue;
01060    AST_DECLARE_APP_ARGS(arg,
01061       AST_APP_ARG(hashname);
01062       AST_APP_ARG(hashkey);
01063    );
01064 
01065    AST_STANDARD_APP_ARGS(arg, data);
01066    if (arg.argc == 2) {
01067       snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey);
01068       varvalue = pbx_builtin_getvar_helper(chan, varname);
01069       if (varvalue)
01070          ast_copy_string(buf, varvalue, len);
01071       else
01072          *buf = '\0';
01073    } else if (arg.argc == 1) {
01074       char colnames[4096];
01075       int i;
01076       AST_DECLARE_APP_ARGS(arg2,
01077          AST_APP_ARG(col)[100];
01078       );
01079 
01080       /* Get column names, in no particular order */
01081       hashkeys_read(chan, "HASHKEYS", arg.hashname, colnames, sizeof(colnames));
01082       pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", colnames);
01083 
01084       AST_STANDARD_APP_ARGS(arg2, colnames);
01085       *buf = '\0';
01086 
01087       /* Now get the corresponding column values, in exactly the same order */
01088       for (i = 0; i < arg2.argc; i++) {
01089          snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg2.col[i]);
01090          varvalue = pbx_builtin_getvar_helper(chan, varname);
01091          strncat(buf, varvalue, len - strlen(buf) - 1);
01092          strncat(buf, ",", len - strlen(buf) - 1);
01093       }
01094 
01095       /* Strip trailing comma */
01096       buf[strlen(buf) - 1] = '\0';
01097    }
01098 
01099    return 0;
01100 }
01101 
01102 static struct ast_custom_function hash_function = {
01103    .name = "HASH",
01104    .write = hash_write,
01105    .read = hash_read,
01106 };
01107 
01108 static struct ast_custom_function hashkeys_function = {
01109    .name = "HASHKEYS",
01110    .read = hashkeys_read,
01111    .read2 = hashkeys_read2,
01112 };
01113 
01114 static struct ast_custom_function array_function = {
01115    .name = "ARRAY",
01116    .write = array,
01117 };
01118 
01119 static int quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
01120 {
01121    char *bufptr = buf, *dataptr = data;
01122 
01123    if (len < 3){ /* at least two for quotes and one for binary zero */
01124       ast_log(LOG_ERROR, "Not enough buffer\n");
01125       return -1;
01126    }
01127 
01128    if (ast_strlen_zero(data)) {
01129       ast_log(LOG_WARNING, "No argument specified!\n");
01130       ast_copy_string(buf, "\"\"", len);
01131       return 0;
01132    }
01133 
01134    *bufptr++ = '"';
01135    for (; bufptr < buf + len - 3; dataptr++) {
01136       if (*dataptr == '\\') {
01137          *bufptr++ = '\\';
01138          *bufptr++ = '\\';
01139       } else if (*dataptr == '"') {
01140          *bufptr++ = '\\';
01141          *bufptr++ = '"';
01142       } else if (*dataptr == '\0') {
01143          break;
01144       } else {
01145          *bufptr++ = *dataptr;
01146       }
01147    }
01148    *bufptr++ = '"';
01149    *bufptr = '\0';
01150    return 0;
01151 }
01152 
01153 static struct ast_custom_function quote_function = {
01154    .name = "QUOTE",
01155    .read = quote,
01156 };
01157 
01158 static int csv_quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
01159 {
01160    char *bufptr = buf, *dataptr = data;
01161 
01162    if (len < 3) { /* at least two for quotes and one for binary zero */
01163       ast_log(LOG_ERROR, "Not enough buffer\n");
01164       return -1;
01165    }
01166 
01167    if (ast_strlen_zero(data)) {
01168       ast_copy_string(buf, "\"\"", len);
01169       return 0;
01170    }
01171 
01172    *bufptr++ = '"';
01173    for (; bufptr < buf + len - 3; dataptr++){
01174       if (*dataptr == '"') {
01175          *bufptr++ = '"';
01176          *bufptr++ = '"';
01177       } else if (*dataptr == '\0') {
01178          break;
01179       } else {
01180          *bufptr++ = *dataptr;
01181       }
01182    }
01183    *bufptr++ = '"';
01184    *bufptr='\0';
01185    return 0;
01186 }
01187 
01188 static struct ast_custom_function csv_quote_function = {
01189    .name = "CSV_QUOTE",
01190    .read = csv_quote,
01191 };
01192 
01193 static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
01194 {
01195    int length = 0;
01196 
01197    if (data)
01198       length = strlen(data);
01199 
01200    snprintf(buf, buflen, "%d", length);
01201 
01202    return 0;
01203 }
01204 
01205 static struct ast_custom_function len_function = {
01206    .name = "LEN",
01207    .read = len,
01208    .read_max = 12,
01209 };
01210 
01211 static int acf_strftime(struct ast_channel *chan, const char *cmd, char *parse,
01212          char *buf, size_t buflen)
01213 {
01214    AST_DECLARE_APP_ARGS(args,
01215               AST_APP_ARG(epoch);
01216               AST_APP_ARG(timezone);
01217               AST_APP_ARG(format);
01218    );
01219    struct timeval when;
01220    struct ast_tm tm;
01221 
01222    buf[0] = '\0';
01223 
01224    AST_STANDARD_APP_ARGS(args, parse);
01225 
01226    ast_get_timeval(args.epoch, &when, ast_tvnow(), NULL);
01227    ast_localtime(&when, &tm, args.timezone);
01228 
01229    if (!args.format)
01230       args.format = "%c";
01231 
01232    if (ast_strftime(buf, buflen, args.format, &tm) <= 0)
01233       ast_log(LOG_WARNING, "C function strftime() output nothing?!!\n");
01234 
01235    buf[buflen - 1] = '\0';
01236 
01237    return 0;
01238 }
01239 
01240 static struct ast_custom_function strftime_function = {
01241    .name = "STRFTIME",
01242    .read = acf_strftime,
01243 };
01244 
01245 static int acf_strptime(struct ast_channel *chan, const char *cmd, char *data,
01246          char *buf, size_t buflen)
01247 {
01248    AST_DECLARE_APP_ARGS(args,
01249               AST_APP_ARG(timestring);
01250               AST_APP_ARG(timezone);
01251               AST_APP_ARG(format);
01252    );
01253    struct ast_tm tm;
01254 
01255    buf[0] = '\0';
01256 
01257    if (!data) {
01258       ast_log(LOG_ERROR,
01259             "Asterisk function STRPTIME() requires an argument.\n");
01260       return -1;
01261    }
01262 
01263    AST_STANDARD_APP_ARGS(args, data);
01264 
01265    if (ast_strlen_zero(args.format)) {
01266       ast_log(LOG_ERROR,
01267             "No format supplied to STRPTIME(<timestring>,<timezone>,<format>)");
01268       return -1;
01269    }
01270 
01271    if (!ast_strptime(args.timestring, args.format, &tm)) {
01272       ast_log(LOG_WARNING, "STRPTIME() found no time specified within the string\n");
01273    } else {
01274       struct timeval when;
01275       when = ast_mktime(&tm, args.timezone);
01276       snprintf(buf, buflen, "%d", (int) when.tv_sec);
01277    }
01278 
01279    return 0;
01280 }
01281 
01282 static struct ast_custom_function strptime_function = {
01283    .name = "STRPTIME",
01284    .read = acf_strptime,
01285 };
01286 
01287 static int function_eval(struct ast_channel *chan, const char *cmd, char *data,
01288           char *buf, size_t buflen)
01289 {
01290    if (ast_strlen_zero(data)) {
01291       ast_log(LOG_WARNING, "EVAL requires an argument: EVAL(<string>)\n");
01292       return -1;
01293    }
01294 
01295    pbx_substitute_variables_helper(chan, data, buf, buflen - 1);
01296 
01297    return 0;
01298 }
01299 
01300 static int function_eval2(struct ast_channel *chan, const char *cmd, char *data,
01301           struct ast_str **buf, ssize_t buflen)
01302 {
01303    if (ast_strlen_zero(data)) {
01304       ast_log(LOG_WARNING, "EVAL requires an argument: EVAL(<string>)\n");
01305       return -1;
01306    }
01307 
01308    ast_str_substitute_variables(buf, buflen, chan, data);
01309 
01310    return 0;
01311 }
01312 
01313 static struct ast_custom_function eval_function = {
01314    .name = "EVAL",
01315    .read = function_eval,
01316    .read2 = function_eval2,
01317 };
01318 
01319 static int keypadhash(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
01320 {
01321    char *bufptr, *dataptr;
01322 
01323    for (bufptr = buf, dataptr = data; bufptr < buf + buflen - 1; dataptr++) {
01324       if (*dataptr == '\0') {
01325          *bufptr++ = '\0';
01326          break;
01327       } else if (*dataptr == '1') {
01328          *bufptr++ = '1';
01329       } else if (strchr("AaBbCc2", *dataptr)) {
01330          *bufptr++ = '2';
01331       } else if (strchr("DdEeFf3", *dataptr)) {
01332          *bufptr++ = '3';
01333       } else if (strchr("GgHhIi4", *dataptr)) {
01334          *bufptr++ = '4';
01335       } else if (strchr("JjKkLl5", *dataptr)) {
01336          *bufptr++ = '5';
01337       } else if (strchr("MmNnOo6", *dataptr)) {
01338          *bufptr++ = '6';
01339       } else if (strchr("PpQqRrSs7", *dataptr)) {
01340          *bufptr++ = '7';
01341       } else if (strchr("TtUuVv8", *dataptr)) {
01342          *bufptr++ = '8';
01343       } else if (strchr("WwXxYyZz9", *dataptr)) {
01344          *bufptr++ = '9';
01345       } else if (*dataptr == '0') {
01346          *bufptr++ = '0';
01347       }
01348    }
01349    buf[buflen - 1] = '\0';
01350 
01351    return 0;
01352 }
01353 
01354 static struct ast_custom_function keypadhash_function = {
01355    .name = "KEYPADHASH",
01356    .read = keypadhash,
01357 };
01358 
01359 static int string_toupper(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
01360 {
01361    char *bufptr = buf, *dataptr = data;
01362 
01363    while ((bufptr < buf + buflen - 1) && (*bufptr++ = toupper(*dataptr++)));
01364 
01365    return 0;
01366 }
01367 
01368 static int string_toupper2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t buflen)
01369 {
01370    char *bufptr, *dataptr = data;
01371 
01372    if (buflen > -1) {
01373       ast_str_make_space(buf, buflen > 0 ? buflen : strlen(data) + 1);
01374    }
01375    bufptr = ast_str_buffer(*buf);
01376    while ((bufptr < ast_str_buffer(*buf) + ast_str_size(*buf) - 1) && (*bufptr++ = toupper(*dataptr++)));
01377    ast_str_update(*buf);
01378 
01379    return 0;
01380 }
01381 
01382 static struct ast_custom_function toupper_function = {
01383    .name = "TOUPPER",
01384    .read = string_toupper,
01385    .read2 = string_toupper2,
01386 };
01387 
01388 static int string_tolower(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
01389 {
01390    char *bufptr = buf, *dataptr = data;
01391 
01392    while ((bufptr < buf + buflen - 1) && (*bufptr++ = tolower(*dataptr++)));
01393 
01394    return 0;
01395 }
01396 
01397 static int string_tolower2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t buflen)
01398 {
01399    char *bufptr, *dataptr = data;
01400 
01401    if (buflen > -1) {
01402       ast_str_make_space(buf, buflen > 0 ? buflen : strlen(data) + 1);
01403    }
01404    bufptr = ast_str_buffer(*buf);
01405    while ((bufptr < ast_str_buffer(*buf) + ast_str_size(*buf) - 1) && (*bufptr++ = tolower(*dataptr++)));
01406    ast_str_update(*buf);
01407 
01408    return 0;
01409 }
01410 
01411 static struct ast_custom_function tolower_function = {
01412    .name = "TOLOWER",
01413    .read = string_tolower,
01414    .read2 = string_tolower2,
01415 };
01416 
01417 static int shift_pop(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
01418 {
01419 #define beginning (cmd[0] == 'S') /* SHIFT */
01420    char *after, delimiter[2] = ",", *varsubst;
01421    size_t unused;
01422    struct ast_str *before = ast_str_thread_get(&result_buf, 16);
01423    char *(*search_func)(const char *s, int c) = (beginning ? strchr : strrchr);
01424    AST_DECLARE_APP_ARGS(args,
01425       AST_APP_ARG(var);
01426       AST_APP_ARG(delimiter);
01427    );
01428 
01429    if (!before) {
01430       return -1;
01431    }
01432 
01433    AST_STANDARD_APP_ARGS(args, data);
01434 
01435    if (ast_strlen_zero(args.var)) {
01436       ast_log(LOG_WARNING, "%s requires a variable name\n", cmd);
01437       return -1;
01438    }
01439 
01440    varsubst = alloca(strlen(args.var) + 4);
01441    sprintf(varsubst, "${%s}", args.var);
01442    ast_str_substitute_variables(&before, 0, chan, varsubst);
01443 
01444    if (args.argc > 1 && !ast_strlen_zero(args.delimiter)) {
01445       ast_get_encoded_char(args.delimiter, delimiter, &unused);
01446    }
01447 
01448    if (!ast_str_strlen(before)) {
01449       /* Nothing to pop */
01450       return -1;
01451    }
01452 
01453    if (!(after = search_func(ast_str_buffer(before), delimiter[0]))) {
01454       /* Only one entry in array */
01455       ast_str_set(buf, len, "%s", ast_str_buffer(before));
01456       pbx_builtin_setvar_helper(chan, args.var, "");
01457    } else {
01458       *after++ = '\0';
01459       ast_str_set(buf, len, "%s", beginning ? ast_str_buffer(before) : after);
01460       pbx_builtin_setvar_helper(chan, args.var, beginning ? after : ast_str_buffer(before));
01461    }
01462 
01463    return 0;
01464 #undef beginning
01465 }
01466 
01467 static struct ast_custom_function shift_function = {
01468    .name = "SHIFT",
01469    .read2 = shift_pop,
01470 };
01471 
01472 static struct ast_custom_function pop_function = {
01473    .name = "POP",
01474    .read2 = shift_pop,
01475 };
01476 
01477 static int unshift_push(struct ast_channel *chan, const char *cmd, char *data, const char *new_value)
01478 {
01479 #define beginning (cmd[0] == 'U') /* UNSHIFT */
01480    char delimiter[2] = ",", *varsubst;
01481    size_t unused;
01482    struct ast_str *buf, *previous_value;
01483    AST_DECLARE_APP_ARGS(args,
01484       AST_APP_ARG(var);
01485       AST_APP_ARG(delimiter);
01486    );
01487 
01488    if (!(buf = ast_str_thread_get(&result_buf, 16)) ||
01489       !(previous_value = ast_str_thread_get(&tmp_buf, 16))) {
01490       return -1;
01491    }
01492 
01493    AST_STANDARD_APP_ARGS(args, data);
01494 
01495    if (ast_strlen_zero(args.var)) {
01496       ast_log(LOG_WARNING, "%s requires a variable name\n", cmd);
01497       return -1;
01498    }
01499 
01500    if (args.argc > 1 && !ast_strlen_zero(args.delimiter)) {
01501       ast_get_encoded_char(args.delimiter, delimiter, &unused);
01502    }
01503 
01504    varsubst = alloca(strlen(args.var) + 4);
01505    sprintf(varsubst, "${%s}", args.var);
01506    ast_str_substitute_variables(&previous_value, 0, chan, varsubst);
01507 
01508    if (!ast_str_strlen(previous_value)) {
01509       ast_str_set(&buf, 0, "%s", new_value);
01510    } else {
01511       ast_str_set(&buf, 0, "%s%c%s",
01512          beginning ? new_value : ast_str_buffer(previous_value),
01513          delimiter[0],
01514          beginning ? ast_str_buffer(previous_value) : new_value);
01515    }
01516 
01517    pbx_builtin_setvar_helper(chan, args.var, ast_str_buffer(buf));
01518 
01519    return 0;
01520 #undef beginning
01521 }
01522 
01523 static struct ast_custom_function push_function = {
01524    .name = "PUSH",
01525    .write = unshift_push,
01526 };
01527 
01528 static struct ast_custom_function unshift_function = {
01529    .name = "UNSHIFT",
01530    .write = unshift_push,
01531 };
01532 
01533 static int passthru(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
01534 {
01535    ast_str_set(buf, len, "%s", data);
01536    return 0;
01537 }
01538 
01539 static struct ast_custom_function passthru_function = {
01540    .name = "PASSTHRU",
01541    .read2 = passthru,
01542 };
01543 
01544 #ifdef TEST_FRAMEWORK
01545 AST_TEST_DEFINE(test_FIELDNUM)
01546 {
01547    int i, res = AST_TEST_PASS;
01548    struct ast_channel *chan;
01549    struct ast_str *str;
01550    char expression[256];
01551    struct {
01552       const char *fields;
01553       const char *delim;
01554       const char *field;
01555       const char *expected;
01556    } test_args[] = {
01557       {"abc,def,ghi,jkl", "\\,",     "ghi", "3"},
01558       {"abc def ghi jkl", " ",       "abc", "1"},
01559       {"abc/def/ghi/jkl", "\\\\x2f", "def", "2"},
01560       {"abc$def$ghi$jkl", "",        "ghi", "0"},
01561       {"abc,def,ghi,jkl", "-",       "",    "0"},
01562       {"abc-def-ghi-jkl", "-",       "mno", "0"}
01563    };
01564 
01565    switch (cmd) {
01566    case TEST_INIT:
01567       info->name = "func_FIELDNUM_test";
01568       info->category = "/funcs/func_strings/";
01569       info->summary = "Test FIELDNUM function";
01570       info->description = "Verify FIELDNUM behavior";
01571       return AST_TEST_NOT_RUN;
01572    case TEST_EXECUTE:
01573       break;
01574    }
01575 
01576    if (!(chan = ast_dummy_channel_alloc())) {
01577       ast_test_status_update(test, "Unable to allocate dummy channel\n");
01578       return AST_TEST_FAIL;
01579    }
01580 
01581    if (!(str = ast_str_create(16))) {
01582       ast_test_status_update(test, "Unable to allocate dynamic string buffer\n");
01583       ast_channel_release(chan);
01584       return AST_TEST_FAIL;
01585    }
01586 
01587    for (i = 0; i < ARRAY_LEN(test_args); i++) {
01588       struct ast_var_t *var = ast_var_assign("FIELDS", test_args[i].fields);
01589       AST_LIST_INSERT_HEAD(&chan->varshead, var, entries);
01590 
01591       snprintf(expression, sizeof(expression), "${FIELDNUM(%s,%s,%s)}", var->name, test_args[i].delim, test_args[i].field);
01592       ast_str_substitute_variables(&str, 0, chan, expression);
01593 
01594       AST_LIST_REMOVE(&chan->varshead, var, entries);
01595       ast_var_delete(var);
01596 
01597       if (strcasecmp(ast_str_buffer(str), test_args[i].expected)) {
01598          ast_test_status_update(test, "Evaluation of '%s' returned '%s' instead of the expected value '%s'\n",
01599             expression, ast_str_buffer(str), test_args[i].expected);
01600          res = AST_TEST_FAIL;
01601          break;
01602       }
01603    }
01604 
01605    ast_free(str);
01606    ast_channel_release(chan);
01607 
01608    return res;
01609 }
01610 
01611 AST_TEST_DEFINE(test_FILTER)
01612 {
01613    int i, res = AST_TEST_PASS;
01614    const char *test_strings[][2] = {
01615       {"A-R",            "DAHDI"},
01616       {"A\\-R",          "A"},
01617       {"\\x41-R",        "DAHDI"},
01618       {"0-9A-Ca-c",      "0042133333A12212"},
01619       {"0-9a-cA-C_+\\-", "0042133333A12212"},
01620       {NULL,             NULL},
01621    };
01622 
01623    switch (cmd) {
01624    case TEST_INIT:
01625       info->name = "func_FILTER_test";
01626       info->category = "/funcs/func_strings/";
01627       info->summary = "Test FILTER function";
01628       info->description = "Verify FILTER behavior";
01629       return AST_TEST_NOT_RUN;
01630    case TEST_EXECUTE:
01631       break;
01632    }
01633 
01634    for (i = 0; test_strings[i][0]; i++) {
01635       char tmp[256], tmp2[256] = "";
01636       snprintf(tmp, sizeof(tmp), "${FILTER(%s,0042133333&DAHDI/g1/2212)}", test_strings[i][0]);
01637       pbx_substitute_variables_helper(NULL, tmp, tmp2, sizeof(tmp2) - 1);
01638       if (strcmp(test_strings[i][1], tmp2)) {
01639          ast_test_status_update(test, "Format string '%s' substituted to '%s'.  Expected '%s'.\n", test_strings[i][0], tmp2, test_strings[i][1]);
01640          res = AST_TEST_FAIL;
01641       }
01642    }
01643    return res;
01644 }
01645 #endif
01646 
01647 static int unload_module(void)
01648 {
01649    int res = 0;
01650 
01651    AST_TEST_UNREGISTER(test_FIELDNUM);
01652    AST_TEST_UNREGISTER(test_FILTER);
01653    res |= ast_custom_function_unregister(&fieldqty_function);
01654    res |= ast_custom_function_unregister(&fieldnum_function);
01655    res |= ast_custom_function_unregister(&filter_function);
01656    res |= ast_custom_function_unregister(&replace_function);
01657    res |= ast_custom_function_unregister(&listfilter_function);
01658    res |= ast_custom_function_unregister(&regex_function);
01659    res |= ast_custom_function_unregister(&array_function);
01660    res |= ast_custom_function_unregister(&quote_function);
01661    res |= ast_custom_function_unregister(&csv_quote_function);
01662    res |= ast_custom_function_unregister(&len_function);
01663    res |= ast_custom_function_unregister(&strftime_function);
01664    res |= ast_custom_function_unregister(&strptime_function);
01665    res |= ast_custom_function_unregister(&eval_function);
01666    res |= ast_custom_function_unregister(&keypadhash_function);
01667    res |= ast_custom_function_unregister(&hashkeys_function);
01668    res |= ast_custom_function_unregister(&hash_function);
01669    res |= ast_unregister_application(app_clearhash);
01670    res |= ast_custom_function_unregister(&toupper_function);
01671    res |= ast_custom_function_unregister(&tolower_function);
01672    res |= ast_custom_function_unregister(&shift_function);
01673    res |= ast_custom_function_unregister(&pop_function);
01674    res |= ast_custom_function_unregister(&push_function);
01675    res |= ast_custom_function_unregister(&unshift_function);
01676    res |= ast_custom_function_unregister(&passthru_function);
01677 
01678    return res;
01679 }
01680 
01681 static int load_module(void)
01682 {
01683    int res = 0;
01684 
01685    AST_TEST_REGISTER(test_FIELDNUM);
01686    AST_TEST_REGISTER(test_FILTER);
01687    res |= ast_custom_function_register(&fieldqty_function);
01688    res |= ast_custom_function_register(&fieldnum_function);
01689    res |= ast_custom_function_register(&filter_function);
01690    res |= ast_custom_function_register(&replace_function);
01691    res |= ast_custom_function_register(&listfilter_function);
01692    res |= ast_custom_function_register(&regex_function);
01693    res |= ast_custom_function_register(&array_function);
01694    res |= ast_custom_function_register(&quote_function);
01695    res |= ast_custom_function_register(&csv_quote_function);
01696    res |= ast_custom_function_register(&len_function);
01697    res |= ast_custom_function_register(&strftime_function);
01698    res |= ast_custom_function_register(&strptime_function);
01699    res |= ast_custom_function_register(&eval_function);
01700    res |= ast_custom_function_register(&keypadhash_function);
01701    res |= ast_custom_function_register(&hashkeys_function);
01702    res |= ast_custom_function_register(&hash_function);
01703    res |= ast_register_application_xml(app_clearhash, exec_clearhash);
01704    res |= ast_custom_function_register(&toupper_function);
01705    res |= ast_custom_function_register(&tolower_function);
01706    res |= ast_custom_function_register(&shift_function);
01707    res |= ast_custom_function_register(&pop_function);
01708    res |= ast_custom_function_register(&push_function);
01709    res |= ast_custom_function_register(&unshift_function);
01710    res |= ast_custom_function_register(&passthru_function);
01711 
01712    return res;
01713 }
01714 
01715 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "String handling dialplan functions");

Generated on Mon Oct 8 12:39:02 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7