Thu Sep 7 01:03:00 2017

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

Generated on 7 Sep 2017 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1