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 f0fd9cc61f..03c3f645b3 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 len)
+	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 len)
 {
 	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);