diff --git a/configs/cdr_custom.conf.sample b/configs/cdr_custom.conf.sample
index 8bc2cb34e6..999d9aa8cd 100644
--- a/configs/cdr_custom.conf.sample
+++ b/configs/cdr_custom.conf.sample
@@ -6,5 +6,6 @@
;
;
;[mappings]
-;Master.csv => "${CDR(clid)}","${CDR(src)}","${CDR(dst)}","${CDR(dcontext)}","${CDR(channel)}","${CDR(dstchannel)}","${CDR(lastapp)}","${CDR(lastdata)}","${CDR(start)}","${CDR(answer)}","${CDR(end)}","${CDR(duration)}","${CDR(billsec)}","${CDR(disposition)}","${CDR(amaflags)}","${CDR(accountcode)}","${CDR(uniqueid)}","${CDR(userfield)}"
+;Master.csv => ${CSV_QUOTE(${CDR(clid)})},${CSV_QUOTE(${CDR(src)})},${CSV_QUOTE(${CDR(dst)})},${CSV_QUOTE(${CDR(dcontext)})},${CSV_QUOTE(${CDR(channel)})},${CSV_QUOTE(${CDR(dstchannel)})},${CSV_QUOTE(${CDR(lastapp)})},${CSV_QUOTE(${CDR(lastdata)})},${CSV_QUOTE(${CDR(start)})},${CSV_QUOTE(${CDR(answer)})},${CSV_QUOTE(${CDR(end)})},${CSV_QUOTE(${CDR(duration)})},${CSV_QUOTE(${CDR(billsec)})},${CSV_QUOTE(${CDR(disposition)})},${CSV_QUOTE(${CDR(amaflags)})},${CSV_QUOTE(${CDR(accountcode)})},${CSV_QUOTE(${CDR(uniqueid)})},${CSV_QUOTE(${CDR(userfield)})}
+;Simple.csv => ${CSV_QUOTE(${EPOCH})},${CSV_QUOTE(${CDR(src)})},${CSV_QUOTE(${CDR(dst)})}
diff --git a/funcs/func_strings.c b/funcs/func_strings.c
index 1af4ed05fe..8d931efe63 100644
--- a/funcs/func_strings.c
+++ b/funcs/func_strings.c
@@ -39,6 +39,326 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/app.h"
#include "asterisk/localtime.h"
+AST_THREADSTORAGE(result_buf);
+
+/*** DOCUMENTATION
+
+
+ Count the fields with an arbitrary delimiter
+
+
+
+
+
+
+ The delimiter may be specified as a special or extended ASCII character, by encoding it. The characters
+ \n, \r, and \t are all recognized as the newline,
+ carriage return, and tab characters, respectively. Also, octal and hexadecimal specifications are recognized
+ by the patterns \0nnn and \xHH, respectively. For example, if you wanted
+ to encode a comma as the delimiter, you could use either \054 or \x2C.
+ Example: If ${example} contains ex-amp-le, then ${FIELDQTY(example,-)} returns 3.
+
+
+
+ Remove an item from a list, by name.
+
+
+
+
+
+
+ Remove value from the list contained in the varname
+ variable, where the list delimiter is specified by the delim parameter. This is
+ very useful for removing a single channel name from a list of channels, for example.
+
+
+
+
+ Filter the string to include only the allowed characters
+
+
+
+
+
+
+ Permits all characters listed in allowed-chars,
+ filtering all others outs. In addition to literally listing the characters,
+ you may also use ranges of characters (delimited by a -
+ Hexadecimal characters started with a \x(i.e. \x20)
+ Octal characters started with a \0 (i.e. \040)
+ Also \t,\n and \r are recognized.
+ If you want the - character it needs to be prefixed with a
+ \
+
+
+
+
+ Check string against a regular expression.
+
+
+
+
+
+
+ Return 1 on regular expression match or 0 otherwise
+ Please note that the space following the double quotes separating the
+ regex from the data is optional and if present, is skipped. If a space is
+ desired at the beginning of the data, then put two spaces there; the second
+ will not be skipped.
+
+
+
+
+ Clear the keys from a specified hashname.
+
+
+
+
+
+ Clears all keys out of the specified hashname.
+
+
+
+
+ Implementation of a dialplan associative array
+
+
+
+
+
+
+ In two arguments mode, gets and sets values to corresponding keys within
+ a named associative array. The single-argument mode will only work when assigned
+ to from a function defined by func_odbc
+
+
+
+
+ Retrieve the keys of the HASH() function.
+
+
+
+
+
+ Returns a comma-delimited list of the current keys of the associative array
+ defined by the HASH() function. Note that if you iterate over the keys of
+ the result, adding keys during iteration will cause the result of the HASHKEYS()
+ function to change.
+
+
+
+
+ Hash the letters in string into equivalent keypad numbers.
+
+
+
+
+
+ Example: ${KEYPADHASH(Les)} returns "537"
+
+
+
+
+ Allows setting multiple variables at once.
+
+
+
+
+
+
+
+ The comma-delimited list passed as a value to which the function is set will
+ be interpreted as a set of values to which the comma-delimited list of
+ variable names in the argument should be set.
+ Example: Set(ARRAY(var1,var2)=1,2) will set var1 to 1 and var2 to 2
+
+
+
+
+ Returns the epoch of the arbitrary date/time string structured as described by the format.
+
+
+
+
+
+
+
+ This is useful for converting a date into EPOCH time,
+ possibly to pass to an application like SayUnixTime or to calculate the difference
+ between the two date strings
+ Example: ${STRPTIME(2006-03-01 07:30:35,America/Chicago,%Y-%m-%d %H:%M:%S)} returns 1141219835
+
+
+
+
+ Returns the current date/time in the specified format.
+
+
+
+
+
+
+
+ STRFTIME supports all of the same formats as the underlying C function
+ strftime(3).
+ It also supports the following format: %[n]q - fractions of a second,
+ with leading zeros.
+ Example: %3q will give milliseconds and %1q
+ will give tenths of a second. The default is set at milliseconds (n=3).
+ The common case is to use it in combination with %S, as in %S.%3q.
+
+
+ [strftime(3)]
+
+
+
+
+ Evaluate stored variables
+
+
+
+
+
+ Using EVAL basically causes a string to be evaluated twice.
+ When a variable or expression is in the dialplan, it will be
+ evaluated at runtime. However, if the results of the evaluation
+ is in fact another variable or expression, using EVAL will have it
+ evaluated a second time.
+ Example: If the MYVAR contains
+ OTHERVAR, then the result of ${EVAL(
+ MYVAR)} in the dialplan will be the
+ contents of OTHERVAR. Normally just
+ putting MYVAR in the dialplan the result
+ would be OTHERVAR.
+
+
+
+
+ Convert string to all uppercase letters.
+
+
+
+
+
+ Example: ${TOUPPER(Example)} returns "EXAMPLE"
+
+
+
+
+ Convert string to all lowercase letters.
+
+
+
+
+
+ Example: ${TOLOWER(Example)} returns "example"
+
+
+
+
+ Return the length of the string given.
+
+
+
+
+
+ Example: ${LEN(example)} returns 7
+
+
+
+
+ Quotes a given string, escaping embedded quotes as necessary
+
+
+
+
+
+ Example: ${QUOTE(ab"c"de)} will return "abcde"
+
+
+
+
+ Quotes a given string for use in a CSV file, escaping embedded quotes as necessary
+
+
+
+
+
+ Example: ${CSV_QUOTE("a,b" 123)} will return """a,b"" 123"
+
+
+
+
+ Removes and returns the first item off of a variable containing delimited text
+
+
+
+
+
+
+ Example:
+ exten => s,1,Set(array=one,two,three)
+ exten => s,n,While($["${SET(var=${SHIFT(array)})}" != ""])
+ exten => s,n,NoOp(var is ${var})
+ exten => s,n,EndWhile
+ This would iterate over each value in array, left to right, and
+ would result in NoOp(var is one), NoOp(var is two), and
+ NoOp(var is three) being executed.
+
+
+
+
+
+ Removes and returns the last item off of a variable containing delimited text
+
+
+
+
+
+
+ Example:
+ exten => s,1,Set(array=one,two,three)
+ exten => s,n,While($["${SET(var=${POP(array)})}" != ""])
+ exten => s,n,NoOp(var is ${var})
+ exten => s,n,EndWhile
+ This would iterate over each value in array, right to left, and
+ would result in NoOp(var is three), NoOp(var is two), and
+ NoOp(var is one) being executed.
+
+
+
+
+
+ Appends one or more values to the end of a variable containing delimited text
+
+
+
+
+
+
+ Example: Set(PUSH(array)=one,two,three) would append one,
+ two, and three to the end of the values stored in the variable
+ "array".
+
+
+
+
+
+ Inserts one or more values to the beginning of a variable containing delimited text
+
+
+
+
+
+
+ Example: Set(UNSHIFT(array)=one,two,three) would insert one,
+ two, and three before the values stored in the variable
+ "array".
+
+
+
+ ***/
+
static int function_fieldqty(struct ast_channel *chan, const char *cmd,
char *parse, char *buf, size_t len)
{
@@ -580,6 +900,12 @@ static struct ast_custom_function sprintf_function = {
static int quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
{
char *bufptr = buf, *dataptr = data;
+
+ if (len < 3){ /* at least two for quotes and one for binary zero */
+ ast_log(LOG_ERROR, "Not enough buffer");
+ return -1;
+ }
+
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "No argument specified!\n");
ast_copy_string(buf, "\"\"", len);
@@ -587,7 +913,7 @@ static int quote(struct ast_channel *chan, const char *cmd, char *data, char *bu
}
*bufptr++ = '"';
- for (; bufptr < buf + len - 1; dataptr++) {
+ for (; bufptr < buf + len - 3; dataptr++) {
if (*dataptr == '\\') {
*bufptr++ = '\\';
*bufptr++ = '\\';
@@ -612,9 +938,43 @@ static struct ast_custom_function quote_function = {
.read = quote,
};
+static int csv_quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ char *bufptr = buf, *dataptr = data;
-static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf,
- size_t buflen)
+ if (len < 3){ /* at least two for quotes and one for binary zero */
+ ast_log(LOG_ERROR, "Not enough buffer");
+ return -1;
+ }
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "No argument specified!\n");
+ ast_copy_string(buf,"\"\"",len);
+ return 0;
+ }
+
+ *bufptr++ = '"';
+ for (; bufptr < buf + len - 3; dataptr++){
+ if (*dataptr == '"') {
+ *bufptr++ = '"';
+ *bufptr++ = '"';
+ } else if (*dataptr == '\0') {
+ break;
+ } else {
+ *bufptr++ = *dataptr;
+ }
+ }
+ *bufptr++ = '"';
+ *bufptr='\0';
+ return 0;
+}
+
+static struct ast_custom_function csv_quote_function = {
+ .name = "CSV_QUOTE",
+ .read = csv_quote,
+};
+
+static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
{
int length = 0;
@@ -849,6 +1209,7 @@ static int unload_module(void)
res |= ast_custom_function_unregister(®ex_function);
res |= ast_custom_function_unregister(&array_function);
res |= ast_custom_function_unregister("e_function);
+ res |= ast_custom_function_unregister(&csv_quote_function);
res |= ast_custom_function_unregister(&len_function);
res |= ast_custom_function_unregister(&strftime_function);
res |= ast_custom_function_unregister(&strptime_function);
@@ -873,6 +1234,7 @@ static int load_module(void)
res |= ast_custom_function_register(®ex_function);
res |= ast_custom_function_register(&array_function);
res |= ast_custom_function_register("e_function);
+ res |= ast_custom_function_register(&csv_quote_function);
res |= ast_custom_function_register(&len_function);
res |= ast_custom_function_register(&strftime_function);
res |= ast_custom_function_register(&strptime_function);