[asterisk-commits] tilghman: branch tilghman/str_substitution r174214 - in /team/tilghman/str_su...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Sun Feb 8 00:05:56 CST 2009


Author: tilghman
Date: Sun Feb  8 00:05:55 2009
New Revision: 174214

URL: http://svn.digium.com/svn-view/asterisk?view=rev&rev=174214
Log:
String substitution, using ast_str

Modified:
    team/tilghman/str_substitution/funcs/func_aes.c
    team/tilghman/str_substitution/funcs/func_base64.c
    team/tilghman/str_substitution/funcs/func_blacklist.c
    team/tilghman/str_substitution/include/asterisk/ast_expr.h
    team/tilghman/str_substitution/include/asterisk/pbx.h
    team/tilghman/str_substitution/main/ast_expr2f.c
    team/tilghman/str_substitution/main/pbx.c
    team/tilghman/str_substitution/main/strings.c

Modified: team/tilghman/str_substitution/funcs/func_aes.c
URL: http://svn.digium.com/svn-view/asterisk/team/tilghman/str_substitution/funcs/func_aes.c?view=diff&rev=174214&r1=174213&r2=174214
==============================================================================
--- team/tilghman/str_substitution/funcs/func_aes.c (original)
+++ team/tilghman/str_substitution/funcs/func_aes.c Sun Feb  8 00:05:55 2009
@@ -31,6 +31,7 @@
 #include "asterisk/pbx.h"
 #include "asterisk/app.h"
 #include "asterisk/aes.h"
+#include "asterisk/strings.h"
 
 #define AES_BLOCK_SIZE 16
 
@@ -71,15 +72,15 @@
 
 
 static int aes_helper(struct ast_channel *chan, const char *cmd, char *data,
-	       char *buf, size_t len)
+	       char *buf, struct ast_str **str, int maxlen)
 {
 	unsigned char curblock[AES_BLOCK_SIZE] = { 0, };
-	char *tmp;
+	char *tmp = NULL;
 	char *tmpP;
-	int data_len, encrypt;
+	int data_len, encrypt, i;
+	int keylen, len, tmplen, elen = 0;
 	ast_aes_encrypt_key ecx;                        /*  AES 128 Encryption context */
 	ast_aes_decrypt_key dcx;
-
 	AST_DECLARE_APP_ARGS(args,
 		AST_APP_ARG(key);
 		AST_APP_ARG(data);
@@ -92,22 +93,47 @@
 		return -1;
 	}
 
-	if (strlen(args.key) != AES_BLOCK_SIZE) {        /* key must be of 16 characters in length, 128 bits */
-		ast_log(LOG_WARNING, "Syntax: %s(<key>,<data>) - <key> parameter must be exactly 16 characters!\n", cmd);
-		return -1;
-	}
-
-	ast_aes_encrypt_key((unsigned char *) args.key, &ecx);   /* encryption:  plaintext -> encryptedtext -> base64 */
-	ast_aes_decrypt_key((unsigned char *) args.key, &dcx);   /* decryption:  base64 -> encryptedtext -> plaintext */
-	tmp = ast_calloc(1, len);                     /* requires a tmp buffer for the base64 decode */
-	tmpP = tmp;
+	if ((keylen = strlen(args.key)) != AES_BLOCK_SIZE) {        /* key must be of 16 characters in length, 128 bits */
+		ast_log(LOG_WARNING, "Syntax: %s(<key>,<data>) - <key> parameter must be exactly 16 characters%s!\n", cmd, keylen < 16 ? " - padding" : "");
+		if (keylen < 16) {
+			char *newkey = alloca(17);
+			strcpy(newkey, args.key);
+			for (i = keylen; i < 16; i++) {
+				newkey[i] = ' ';
+			}
+			newkey[16] = '\0';
+			args.key = newkey;
+		} else {
+			return -1;
+		}
+	}
+
+	if (buf) {
+		len = maxlen;
+	} else if (maxlen == -1) {
+		len = ast_str_size(*str) - ast_str_strlen(*str);
+	} else if (maxlen > 0) {
+		len = maxlen - ast_str_strlen(*str);
+	} else {
+		len = INT_MAX;
+	}
+
 	encrypt = strcmp("AES_DECRYPT", cmd);           /* -1 if encrypting, 0 if decrypting */
+	/* Round up the buffer to an even multiple of 16, plus 1 */
+	tmplen = (strlen(args.data) / 16 + 1) * 16 + 1;
+	tmp = ast_calloc(1, tmplen);
 
 	if (encrypt) {                                  /* if decrypting first decode src to base64 */
-		ast_copy_string(tmp, args.data, len);
+		/* encryption:  plaintext -> encryptedtext -> base64 */
+		ast_aes_encrypt_key((unsigned char *) args.key, &ecx);
+		strcpy(tmp, args.data);
 		data_len = strlen(tmp);
+		tmpP = args.data;
 	} else {
-		data_len = ast_base64decode((unsigned char *) tmp, args.data, len);
+		/* decryption:  base64 -> encryptedtext -> plaintext */
+		ast_aes_decrypt_key((unsigned char *) args.key, &dcx);
+		tmpP = tmp;
+		data_len = ast_base64decode((unsigned char *) tmp, args.data, tmplen);
 	}
 
 	if (data_len >= len) {                        /* make sure to not go over buffer len */
@@ -116,6 +142,9 @@
 	}
 
 	while (data_len > 0) {
+		/* Tricky operation.  We first copy the data into curblock, then
+		 * the data is encrypted or decrypted and put back into the original
+		 * buffer. */
 		memset(curblock, 0, AES_BLOCK_SIZE);
 		memcpy(curblock, tmpP, (data_len < AES_BLOCK_SIZE) ? data_len : AES_BLOCK_SIZE);
 		if (encrypt) {
@@ -125,26 +154,53 @@
 		}
 		tmpP += AES_BLOCK_SIZE;
 		data_len -= AES_BLOCK_SIZE;
+		elen += AES_BLOCK_SIZE;
 	}
 
 	if (encrypt) {                            /* if encrypting encode result to base64 */
-		ast_base64encode(buf, (unsigned char *) tmp, strlen(tmp), len);
+		if (buf) {
+			ast_base64encode(buf, (unsigned char *) tmp, elen, len);
+		} else {
+			if (maxlen >= 0) {
+				ast_str_make_space(str, maxlen ? maxlen : ast_str_strlen(*str) + elen * 4 / 3 + 2);
+			}
+			ast_base64encode(ast_str_buffer(*str) + ast_str_strlen(*str), (unsigned char *) tmp, elen, ast_str_size(*str) - ast_str_strlen(*str));
+			ast_str_update(*str);
+		}
 	} else {
-		memcpy(buf, tmp, len);
+		if (buf) {
+			memcpy(buf, tmp, len);
+		} else {
+			ast_str_append(str, maxlen, "%s", tmp);
+		}
 	}
 	ast_free(tmp);
 
 	return 0;
+}
+
+static int aes_buf_helper(struct ast_channel *chan, const char *cmd, char *data,
+	       char *buf, size_t maxlen)
+{
+	return aes_helper(chan, cmd, data, buf, NULL, maxlen);
+}
+
+static int aes_str_helper(struct ast_channel *chan, const char *cmd, char *data,
+	       struct ast_str **buf, int maxlen)
+{
+	return aes_helper(chan, cmd, data, NULL, buf, maxlen);
 }
 
 static struct ast_custom_function aes_encrypt_function = {
 	.name = "AES_ENCRYPT",
-	.read = aes_helper,
+	.read = aes_buf_helper,
+	.read2 = aes_str_helper,
 };
 
 static struct ast_custom_function aes_decrypt_function = {
 	.name = "AES_DECRYPT",
-	.read = aes_helper,
+	.read = aes_buf_helper,
+	.read2 = aes_str_helper,
 };
 
 static int unload_module(void)

Modified: team/tilghman/str_substitution/funcs/func_base64.c
URL: http://svn.digium.com/svn-view/asterisk/team/tilghman/str_substitution/funcs/func_base64.c?view=diff&rev=174214&r1=174213&r2=174214
==============================================================================
--- team/tilghman/str_substitution/funcs/func_base64.c (original)
+++ team/tilghman/str_substitution/funcs/func_base64.c Sun Feb  8 00:05:55 2009
@@ -29,6 +29,7 @@
 #include "asterisk/module.h"
 #include "asterisk/pbx.h"	/* function register/unregister */
 #include "asterisk/utils.h"
+#include "asterisk/strings.h"
 
 /*** DOCUMENTATION
 	<function name="BASE64_ENCODE" language="en_US">
@@ -59,40 +60,61 @@
 	</function>
  ***/
 
-static int base64_encode(struct ast_channel *chan, const char *cmd, char *data,
-			 char *buf, size_t len)
+static int base64_helper(struct ast_channel *chan, const char *cmd, char *data,
+			 char *buf, struct ast_str **str, int len)
 {
 	if (ast_strlen_zero(data)) {
-		ast_log(LOG_WARNING, "Syntax: BASE64_ENCODE(<data>) - missing argument!\n");
+		ast_log(LOG_WARNING, "Syntax: %s(<data>) - missing argument!\n", cmd);
 		return -1;
 	}
 
-	ast_base64encode(buf, (unsigned char *) data, strlen(data), len);
+	if (cmd[7] == 'E') {
+		if (buf) {
+			ast_base64encode(buf, (unsigned char *) data, strlen(data), len);
+		} else {
+			if (len >= 0) {
+				ast_str_make_space(str, len ? len : ast_str_strlen(*str) + strlen(data) * 4 / 3 + 2);
+			}
+			ast_base64encode(ast_str_buffer(*str) + ast_str_strlen(*str), (unsigned char *) data, strlen(data), ast_str_size(*str) - ast_str_strlen(*str));
+			ast_str_update(*str);
+		}
+	} else {
+		if (buf) {
+			ast_base64decode((unsigned char *) buf, data, len);
+		} else {
+			if (len >= 0) {
+				ast_str_make_space(str, len ? len : ast_str_strlen(*str) + strlen(data) * 3 / 4 + 2);
+			}
+			ast_base64decode((unsigned char *) ast_str_buffer(*str) + ast_str_strlen(*str), data, ast_str_size(*str) - ast_str_strlen(*str));
+			ast_str_update(*str);
+		}
+	}
 
 	return 0;
 }
 
-static int base64_decode(struct ast_channel *chan, const char *cmd, char *data,
+static int base64_buf_helper(struct ast_channel *chan, const char *cmd, char *data,
 			 char *buf, size_t len)
 {
-	if (ast_strlen_zero(data)) {
-		ast_log(LOG_WARNING, "Syntax: BASE64_DECODE(<base_64 string>) - missing argument!\n");
-		return -1;
-	}
+	return base64_helper(chan, cmd, data, buf, NULL, len);
+}
 
-	ast_base64decode((unsigned char *) buf, data, len);
-
-	return 0;
+static int base64_str_helper(struct ast_channel *chan, const char *cmd, char *data,
+			 struct ast_str **buf, int len)
+{
+	return base64_helper(chan, cmd, data, NULL, buf, len);
 }
 
 static struct ast_custom_function base64_encode_function = {
 	.name = "BASE64_ENCODE",
-	.read = base64_encode,
+	.read = base64_buf_helper,
+	.read2 = base64_str_helper,
 };
 
 static struct ast_custom_function base64_decode_function = {
 	.name = "BASE64_DECODE",
-	.read = base64_decode,
+	.read = base64_buf_helper,
+	.read2 = base64_str_helper,
 };
 
 static int unload_module(void)

Modified: team/tilghman/str_substitution/funcs/func_blacklist.c
URL: http://svn.digium.com/svn-view/asterisk/team/tilghman/str_substitution/funcs/func_blacklist.c?view=diff&rev=174214&r1=174213&r2=174214
==============================================================================
--- team/tilghman/str_substitution/funcs/func_blacklist.c (original)
+++ team/tilghman/str_substitution/funcs/func_blacklist.c Sun Feb  8 00:05:55 2009
@@ -70,9 +70,26 @@
 	return 0;
 }
 
+static int blacklist_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **str, int len)
+{
+	/* 2 bytes is a single integer, plus terminating null */
+	if (ast_str_size(*str) - ast_str_strlen(*str) < 2) {
+		if (len > ast_str_size(*str) || len == 0) {
+			ast_str_make_space(str, len ? len : ast_str_strlen(*str) + 2);
+		}
+	}
+	if (ast_str_size(*str) - ast_str_strlen(*str) >= 2) {
+		int res = blacklist_read(chan, cmd, data, ast_str_buffer(*str) + ast_str_strlen(*str), 2);
+		ast_str_update(*str);
+		return res;
+	}
+	return -1;
+}
+
 static struct ast_custom_function blacklist_function = {
 	.name = "BLACKLIST",
 	.read = blacklist_read,
+	.read2 = blacklist_read2,
 };
 
 static int unload_module(void)

Modified: team/tilghman/str_substitution/include/asterisk/ast_expr.h
URL: http://svn.digium.com/svn-view/asterisk/team/tilghman/str_substitution/include/asterisk/ast_expr.h?view=diff&rev=174214&r1=174213&r2=174214
==============================================================================
--- team/tilghman/str_substitution/include/asterisk/ast_expr.h (original)
+++ team/tilghman/str_substitution/include/asterisk/ast_expr.h Sun Feb  8 00:05:55 2009
@@ -32,6 +32,7 @@
 #endif
 
 int ast_expr(char *expr, char *buf, int length, struct ast_channel *chan);
+int ast_str_expr(struct ast_str **str, int maxlen, struct ast_channel *chan, char *expr);
 
 #if defined(__cplusplus) || defined(c_plusplus)
 }

Modified: team/tilghman/str_substitution/include/asterisk/pbx.h
URL: http://svn.digium.com/svn-view/asterisk/team/tilghman/str_substitution/include/asterisk/pbx.h?view=diff&rev=174214&r1=174213&r2=174214
==============================================================================
--- team/tilghman/str_substitution/include/asterisk/pbx.h (original)
+++ team/tilghman/str_substitution/include/asterisk/pbx.h Sun Feb  8 00:05:55 2009
@@ -89,6 +89,7 @@
 	);
 	enum ast_doc_src docsrc;		/*!< Where the documentation come from */
 	int (*read)(struct ast_channel *, const char *, char *, char *, size_t);	/*!< Read function, if read is supported */
+	int (*read2)(struct ast_channel *, const char *, char *, struct ast_str **, int);	/*!< Read function, if read is supported */
 	int (*write)(struct ast_channel *, const char *, char *, const char *);		/*!< Write function, if write is supported */
 	struct ast_module *mod;         /*!< Module this custom function belongs to */
 	AST_RWLIST_ENTRY(ast_custom_function) acflist;
@@ -415,6 +416,8 @@
  * Otherwise, 0 is returned.
  */
 int ast_get_hint(char *hint, int maxlen, char *name, int maxnamelen, 
+	struct ast_channel *c, const char *context, const char *exten);
+int ast_str_get_hint(struct ast_str **hint, int maxlen, struct ast_str **name, int maxnamelen, 
 	struct ast_channel *c, const char *context, const char *exten);
 
 /*!
@@ -933,19 +936,23 @@
 
 int pbx_builtin_raise_exception(struct ast_channel *chan, void *data);
 
-void pbx_substitute_variables_helper(struct ast_channel *c,const char *cp1,char *cp2,int count);
+void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count);
 void pbx_substitute_variables_varshead(struct varshead *headp, const char *cp1, char *cp2, int count);
 void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int cp2_size, size_t *used);
-void ast_str_substitute_variables(struct ast_str **buf, size_t maxlen, struct ast_channel *chan, const char *templ);
+
+char *ast_str_retrieve_variable(struct ast_str **buf, int maxlen, struct ast_channel *chan, struct varshead *headp, const char *var);
+void ast_str_substitute_variables(struct ast_str **buf, int maxlen, struct ast_channel *chan, const char *templ);
+void ast_str_substitute_variables_varshead(struct ast_str **buf, int maxlen, struct varshead *headp, const char *templ);
+void ast_str_substitute_variables_full(struct ast_str **buf, int maxlen, struct ast_channel *c, struct varshead *headp, const char *templ, size_t *used);
 
 int ast_extension_patmatch(const char *pattern, const char *data);
 
-/*! Set "autofallthrough" flag, if newval is <0, does not acutally set.  If
+/*! Set "autofallthrough" flag, if newval is <0, does not actually set.  If
   set to 1, sets to auto fall through.  If newval set to 0, sets to no auto
   fall through (reads extension instead).  Returns previous value. */
 int pbx_set_autofallthrough(int newval);
 
-/*! Set "extenpatternmatchnew" flag, if newval is <0, does not acutally set.  If
+/*! Set "extenpatternmatchnew" flag, if newval is <0, does not actually set.  If
   set to 1, sets to use the new Trie-based pattern matcher.  If newval set to 0, sets to use
   the old linear-search algorithm.  Returns previous value. */
 int pbx_set_extenpatternmatchnew(int newval);
@@ -1024,6 +1031,7 @@
  * \return zero on success, non-zero on failure
  */
 int ast_func_read(struct ast_channel *chan, const char *function, char *workspace, size_t len);
+int ast_func_read2(struct ast_channel *chan, const char *function, struct ast_str **str, int maxlen);
 
 /*!
  * \brief executes a write operation on a function

Modified: team/tilghman/str_substitution/main/ast_expr2f.c
URL: http://svn.digium.com/svn-view/asterisk/team/tilghman/str_substitution/main/ast_expr2f.c?view=diff&rev=174214&r1=174213&r2=174214
==============================================================================
--- team/tilghman/str_substitution/main/ast_expr2f.c (original)
+++ team/tilghman/str_substitution/main/ast_expr2f.c Sun Feb  8 00:05:55 2009
@@ -4152,19 +4152,12 @@
 
 int ast_expr(char *expr, char *buf, int length, struct ast_channel *chan)
 {
-	struct parse_io io;
+	struct parse_io io = { .string = expr, .chan = chan };
 	int return_value = 0;
-	
-	memset(&io, 0, sizeof(io));
-	io.string = expr;  /* to pass to the error routine */
-	io.chan = chan;
-	
+
 	ast_yylex_init(&io.scanner);
-	
 	ast_yy_scan_string(expr, io.scanner);
-	
 	ast_yyparse ((void *) &io);
-
 	ast_yylex_destroy(io.scanner);
 
 	if (!io.val) {
@@ -4195,6 +4188,32 @@
 	}
 	return return_value;
 }
+
+#if !defined(STANDALONE)
+int ast_str_expr(struct ast_str **str, int maxlen, struct ast_channel *chan, char *expr)
+{
+	struct parse_io io = { .string = expr, .chan = chan };
+
+	ast_yylex_init(&io.scanner);
+	ast_yy_scan_string(expr, io.scanner);
+	ast_yyparse ((void *) &io);
+	ast_yylex_destroy(io.scanner);
+
+	if (!io.val) {
+		ast_str_set(str, maxlen, "0");
+	} else {
+		if (io.val->type == AST_EXPR_number) {
+			int res_length;
+			ast_str_set(str, maxlen, FP___PRINTF, io.val->u.i);
+		} else if (io.val->u.s) {
+			ast_str_set(str, maxlen, "%s", io.val->u.s);
+			free(io.val->u.s);
+		}
+		free(io.val);
+	}
+	return ast_str_strlen(*str);
+}
+#endif
 
 
 char extra_error_message[4095];

Modified: team/tilghman/str_substitution/main/pbx.c
URL: http://svn.digium.com/svn-view/asterisk/team/tilghman/str_substitution/main/pbx.c?view=diff&rev=174214&r1=174213&r2=174214
==============================================================================
--- team/tilghman/str_substitution/main/pbx.c (original)
+++ team/tilghman/str_substitution/main/pbx.c Sun Feb  8 00:05:55 2009
@@ -2804,6 +2804,54 @@
 	return ret;
 }
 
+static char *ast_str_substring(struct ast_str *value, int offset, int length)
+{
+	int lr;	/* length of the input string after the copy */
+
+	lr = ast_str_strlen(value); /* compute length after copy, so we never go out of the workspace */
+
+	/* Quick check if no need to do anything */
+	if (offset == 0 && length >= lr)	/* take the whole string */
+		return ast_str_buffer(value);
+
+	if (offset < 0)	{	/* translate negative offset into positive ones */
+		offset = lr + offset;
+		if (offset < 0) /* If the negative offset was greater than the length of the string, just start at the beginning */
+			offset = 0;
+	}
+
+	/* too large offset result in empty string so we know what to return */
+	if (offset >= lr) {
+		ast_str_reset(value);
+		return ast_str_buffer(value);
+	}
+
+	if (offset > 0) {
+		/* Go ahead and chop off the beginning */
+		memcpy(ast_str_buffer(value), ast_str_buffer(value) + offset, ast_str_strlen(value) - offset);
+		lr -= offset;
+	}
+
+	if (length >= 0 && length < lr) {	/* truncate if necessary */
+		char *tmp = ast_str_buffer(value);
+		tmp[length] = '\0';
+		ast_str_update(value);
+	} else if (length < 0) {
+		if (lr > -length) { /* After we remove from the front and from the rear, is there anything left? */
+			char *tmp = ast_str_buffer(value);
+			tmp[lr + length] = '\0';
+			ast_str_update(value);
+		} else {
+			ast_str_reset(value);
+		}
+	} else {
+		/* Nothing to do, but update the buffer length */
+		ast_str_update(value);
+	}
+
+	return ast_str_buffer(value);
+}
+
 /*! \brief  Support for Asterisk built-in variables in the dialplan
 
 \note	See also
@@ -2812,8 +2860,18 @@
  */
 void pbx_retrieve_variable(struct ast_channel *c, const char *var, char **ret, char *workspace, int workspacelen, struct varshead *headp)
 {
+	struct ast_str *str = ast_str_create(16);
+
+	*ret = ast_str_retrieve_variable(&str, 0, c, headp, var);
+	ast_copy_string(workspace, ast_str_buffer(str), workspacelen);
+	*ret = *ret ? workspace : NULL;
+	ast_free(str);
+}
+
+char *ast_str_retrieve_variable(struct ast_str **str, int maxlen, struct ast_channel *c, struct varshead *headp, const char *var)
+{
 	const char not_found = '\0';
-	char *tmpvar;
+	char *tmpvar, *ret;
 	const char *s;	/* the result */
 	int offset, length;
 	int i, need_substring;
@@ -2852,47 +2910,48 @@
 		if (!strncmp(var, "CALL", 4)) {
 			if (!strncmp(var + 4, "ING", 3)) {
 				if (!strcmp(var + 7, "PRES")) {			/* CALLINGPRES */
-					snprintf(workspace, workspacelen, "%d", c->cid.cid_pres);
-					s = workspace;
+					ast_str_set(str, maxlen, "%d", c->cid.cid_pres);
+					s = ast_str_buffer(*str);
 				} else if (!strcmp(var + 7, "ANI2")) {		/* CALLINGANI2 */
-					snprintf(workspace, workspacelen, "%d", c->cid.cid_ani2);
-					s = workspace;
+					ast_str_set(str, maxlen, "%d", c->cid.cid_ani2);
+					s = ast_str_buffer(*str);
 				} else if (!strcmp(var + 7, "TON")) {		/* CALLINGTON */
-					snprintf(workspace, workspacelen, "%d", c->cid.cid_ton);
-					s = workspace;
+					ast_str_set(str, maxlen, "%d", c->cid.cid_ton);
+					s = ast_str_buffer(*str);
 				} else if (!strcmp(var + 7, "TNS")) {		/* CALLINGTNS */
-					snprintf(workspace, workspacelen, "%d", c->cid.cid_tns);
-					s = workspace;
+					ast_str_set(str, maxlen, "%d", c->cid.cid_tns);
+					s = ast_str_buffer(*str);
 				}
 			}
 		} else if (!strcmp(var, "HINT")) {
-			s = ast_get_hint(workspace, workspacelen, NULL, 0, c, c->context, c->exten) ? workspace : NULL;
+			s = ast_str_get_hint(str, maxlen, NULL, 0, c, c->context, c->exten) ? ast_str_buffer(*str) : NULL;
 		} else if (!strcmp(var, "HINTNAME")) {
-			s = ast_get_hint(NULL, 0, workspace, workspacelen, c, c->context, c->exten) ? workspace : NULL;
+			s = ast_str_get_hint(NULL, 0, str, maxlen, c, c->context, c->exten) ? ast_str_buffer(*str) : NULL;
 		} else if (!strcmp(var, "EXTEN")) {
 			s = c->exten;
 		} else if (!strcmp(var, "CONTEXT")) {
 			s = c->context;
 		} else if (!strcmp(var, "PRIORITY")) {
-			snprintf(workspace, workspacelen, "%d", c->priority);
-			s = workspace;
+			ast_str_set(str, maxlen, "%d", c->priority);
+			s = ast_str_buffer(*str);
 		} else if (!strcmp(var, "CHANNEL")) {
 			s = c->name;
 		} else if (!strcmp(var, "UNIQUEID")) {
 			s = c->uniqueid;
 		} else if (!strcmp(var, "HANGUPCAUSE")) {
-			snprintf(workspace, workspacelen, "%d", c->hangupcause);
-			s = workspace;
+			ast_str_set(str, maxlen, "%d", c->hangupcause);
+			s = ast_str_buffer(*str);
 		}
 	}
 	if (s == &not_found) { /* look for more */
 		if (!strcmp(var, "EPOCH")) {
-			snprintf(workspace, workspacelen, "%u",(int)time(NULL));
-			s = workspace;
+			ast_str_set(str, maxlen, "%u", (int) time(NULL));
+			s = ast_str_buffer(*str);
 		} else if (!strcmp(var, "SYSTEMNAME")) {
 			s = ast_config_AST_SYSTEM_NAME;
 		} else if (!strcmp(var, "ENTITYID")) {
-			ast_eid_to_str(workspace, workspacelen, &g_eid);
+			char workspace[20];
+			ast_eid_to_str(workspace, sizeof(workspace), &g_eid);
 			s = workspace;
 		}
 	}
@@ -2912,18 +2971,22 @@
 		if (places[i] == &globals)
 			ast_rwlock_unlock(&globalslock);
 	}
-	if (s == &not_found || s == NULL)
-		*ret = NULL;
-	else {
-		if (s != workspace)
-			ast_copy_string(workspace, s, workspacelen);
-		*ret = workspace;
-		if (need_substring)
-			*ret = substring(*ret, offset, length, workspace, workspacelen);
-	}
-
-	if (c)
+	if (s == &not_found || s == NULL) {
+		ret = NULL;
+	} else {
+		if (s != ast_str_buffer(*str)) {
+			ast_str_set(str, maxlen, "%s", s);
+		}
+		ret = ast_str_buffer(*str);
+		if (need_substring) {
+			ret = ast_str_substring(*str, offset, length);
+		}
+	}
+
+	if (c) {
 		ast_channel_unlock(c);
+	}
+	return ret;
 }
 
 static void exception_store_free(void *data)
@@ -3317,6 +3380,38 @@
 	return -1;
 }
 
+int ast_func_read2(struct ast_channel *chan, const char *function, struct ast_str **str, int maxlen)
+{
+	char *copy = ast_strdupa(function);
+	char *args = func_args(copy);
+	struct ast_custom_function *acfptr = ast_custom_function_find(copy);
+
+	if (acfptr == NULL) {
+		ast_log(LOG_ERROR, "Function %s not registered\n", copy);
+	} else if (!acfptr->read && !acfptr->read2) {
+		ast_log(LOG_ERROR, "Function %s cannot be read\n", copy);
+	} else {
+		int res;
+		struct ast_module_user *u = NULL;
+		if (acfptr->mod)
+			u = __ast_module_user_add(acfptr->mod, chan);
+		if (acfptr->read2) {
+			/* ast_str enabled */
+			res = acfptr->read2(chan, copy, args, str, maxlen);
+		} else {
+			/* Legacy function pointer, allocate buffer for result */
+			if (maxlen > -1) {
+				ast_str_make_space(str, maxlen ? maxlen : VAR_BUF_SIZE);
+			}
+			res = acfptr->read(chan, copy, args, ast_str_buffer(*str), maxlen > 0 ? maxlen : maxlen < 0 ? ast_str_size(*str) : VAR_BUF_SIZE);
+		}
+		if (acfptr->mod && u)
+			__ast_module_user_remove(acfptr->mod, u);
+		return res;
+	}
+	return -1;
+}
+
 int ast_func_write(struct ast_channel *chan, const char *function, const char *value)
 {
 	char *copy = ast_strdupa(function);
@@ -3339,6 +3434,191 @@
 	}
 
 	return -1;
+}
+
+void ast_str_substitute_variables_full(struct ast_str **buf, int maxlen, struct ast_channel *c, struct varshead *headp, const char *templ, size_t *used)
+{
+	/* Substitutes variables into buf, based on string templ */
+	char *cp4;
+	const char *tmp, *whereweare;
+	int orig_size = ast_str_strlen(*buf);
+	int offset, offset2, isfunction;
+	char *nextvar, *nextexp, *nextthing;
+	char *vars, *vare;
+	int pos, brackets, needsub, len;
+	struct ast_str *substr1 = ast_str_create(16), *substr2 = NULL, *substr3 = ast_str_create(16);
+
+	whereweare = tmp = templ;
+	while (!ast_strlen_zero(whereweare)) {
+		/* Assume we're copying the whole remaining string */
+		pos = strlen(whereweare);
+		nextvar = NULL;
+		nextexp = NULL;
+		nextthing = strchr(whereweare, '$');
+		if (nextthing) {
+			switch (nextthing[1]) {
+			case '{':
+				nextvar = nextthing;
+				pos = nextvar - whereweare;
+				break;
+			case '[':
+				nextexp = nextthing;
+				pos = nextexp - whereweare;
+				break;
+			default:
+				pos = 1;
+			}
+		}
+
+		if (pos) {
+			/* Copy that many bytes */
+			ast_str_append_substr(buf, maxlen, whereweare, pos);
+
+			templ += pos;
+			whereweare += pos;
+		}
+
+		if (nextvar) {
+			/* We have a variable.  Find the start and end, and determine
+			   if we are going to have to recursively call ourselves on the
+			   contents */
+			vars = vare = nextvar + 2;
+			brackets = 1;
+			needsub = 0;
+
+			/* Find the end of it */
+			while (brackets && *vare) {
+				if ((vare[0] == '$') && (vare[1] == '{')) {
+					needsub++;
+				} else if (vare[0] == '{') {
+					brackets++;
+				} else if (vare[0] == '}') {
+					brackets--;
+				} else if ((vare[0] == '$') && (vare[1] == '['))
+					needsub++;
+				vare++;
+			}
+			if (brackets)
+				ast_log(LOG_WARNING, "Error in extension logic (missing '}')\n");
+			len = vare - vars - 1;
+
+			/* Skip totally over variable string */
+			whereweare += (len + 3);
+
+			/* Store variable name (and truncate) */
+			ast_str_set_substr(&substr1, 0, vars, len);
+
+			/* Substitute if necessary */
+			if (needsub) {
+				size_t used;
+				if (!substr2) {
+					substr2 = ast_str_create(16);
+				}
+
+				ast_str_substitute_variables_full(&substr2, 0, c, headp, ast_str_buffer(substr1), &used);
+				vars = ast_str_buffer(substr2);
+			} else {
+				vars = ast_str_buffer(substr1);
+			}
+
+			parse_variable_name(vars, &offset, &offset2, &isfunction);
+			if (isfunction) {
+				/* Evaluate function */
+				if (c || !headp) {
+					cp4 = ast_func_read2(c, vars, &substr3, 0) ? NULL : ast_str_buffer(substr3);
+				} else {
+					struct varshead old;
+					struct ast_channel *bogus = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/%p", vars);
+					if (bogus) {
+						memcpy(&old, &bogus->varshead, sizeof(old));
+						memcpy(&bogus->varshead, headp, sizeof(bogus->varshead));
+						cp4 = ast_func_read2(c, vars, &substr3, 0) ? NULL : ast_str_buffer(substr3);
+						/* Don't deallocate the varshead that was passed in */
+						memcpy(&bogus->varshead, &old, sizeof(bogus->varshead));
+						ast_channel_free(bogus);
+					} else {
+						ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution.  Function results may be blank.\n");
+					}
+				}
+				ast_debug(2, "Function result is '%s'\n", cp4 ? cp4 : "(null)");
+			} else {
+				/* Retrieve variable value */
+				ast_str_retrieve_variable(&substr3, 0, c, headp, vars);
+				cp4 = ast_str_buffer(substr3);
+			}
+			if (cp4) {
+				ast_str_substring(substr3, offset, offset2);
+				ast_str_append(buf, maxlen, "%s", ast_str_buffer(substr3));
+			}
+		} else if (nextexp) {
+			/* We have an expression.  Find the start and end, and determine
+			   if we are going to have to recursively call ourselves on the
+			   contents */
+			vars = vare = nextexp + 2;
+			brackets = 1;
+			needsub = 0;
+
+			/* Find the end of it */
+			while (brackets && *vare) {
+				if ((vare[0] == '$') && (vare[1] == '[')) {
+					needsub++;
+					brackets++;
+					vare++;
+				} else if (vare[0] == '[') {
+					brackets++;
+				} else if (vare[0] == ']') {
+					brackets--;
+				} else if ((vare[0] == '$') && (vare[1] == '{')) {
+					needsub++;
+					vare++;
+				}
+				vare++;
+			}
+			if (brackets)
+				ast_log(LOG_WARNING, "Error in extension logic (missing ']')\n");
+			len = vare - vars - 1;
+
+			/* Skip totally over expression */
+			whereweare += (len + 3);
+
+			/* Store variable name (and truncate) */
+			ast_str_set_substr(&substr1, 0, vars, len);
+
+			/* Substitute if necessary */
+			if (needsub) {
+				size_t used;
+				if (!substr2) {
+					substr2 = ast_str_create(16);
+				}
+
+				ast_str_substitute_variables_full(&substr2, 0, c, headp, ast_str_buffer(substr1), &used);
+				vars = ast_str_buffer(substr2);
+			} else {
+				vars = ast_str_buffer(substr1);
+			}
+
+			if (ast_str_expr(&substr3, 0, c, vars)) {
+				ast_debug(1, "Expression result is '%s'\n", ast_str_buffer(substr3));
+			}
+			ast_str_append(buf, maxlen, "%s", ast_str_buffer(substr3));
+		}
+	}
+	*used = ast_str_strlen(*buf) - orig_size;
+	ast_free(substr1);
+	ast_free(substr2);
+	ast_free(substr3);
+}
+
+void ast_str_substitute_variables(struct ast_str **buf, int maxlen, struct ast_channel *chan, const char *templ)
+{
+	size_t used;
+	ast_str_substitute_variables_full(buf, maxlen, chan, NULL, templ, &used);
+}
+
+void ast_str_substitute_variables_varshead(struct ast_str **buf, int maxlen, struct varshead *headp, const char *templ)
+{
+	size_t used;
+	ast_str_substitute_variables_full(buf, maxlen, NULL, headp, templ, &used);
 }
 
 void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int count, size_t *used)
@@ -4056,6 +4336,26 @@
 			const char *tmp = ast_get_extension_app_data(e);
 			if (tmp)
 				ast_copy_string(name, tmp, namesize);
+		}
+		return -1;
+	}
+	return 0;
+}
+
+/*! \brief Get hint for channel */
+int ast_str_get_hint(struct ast_str **hint, int hintsize, struct ast_str **name, int namesize, struct ast_channel *c, const char *context, const char *exten)
+{
+	struct ast_exten *e = ast_hint_extension(c, context, exten);
+
+	if (e) {
+		if (hint) {
+			ast_str_set(hint, hintsize, "%s", ast_get_extension_app(e));
+		}
+		if (name) {
+			const char *tmp = ast_get_extension_app_data(e);
+			if (tmp) {
+				ast_str_set(name, namesize, "%s", tmp);
+			}
 		}
 		return -1;
 	}

Modified: team/tilghman/str_substitution/main/strings.c
URL: http://svn.digium.com/svn-view/asterisk/team/tilghman/str_substitution/main/strings.c?view=diff&rev=174214&r1=174213&r2=174214
==============================================================================
--- team/tilghman/str_substitution/main/strings.c (original)
+++ team/tilghman/str_substitution/main/strings.c Sun Feb  8 00:05:55 2009
@@ -98,17 +98,6 @@
 	return res;
 }
 
-void ast_str_substitute_variables(struct ast_str **buf, size_t maxlen, struct ast_channel *chan, const char *template)
-{
-	int first = 1;
-	do {
-		ast_str_make_space(buf, maxlen ? maxlen :
-			(first ? strlen(template) * 2 : (*buf)->__AST_STR_LEN * 2));
-		pbx_substitute_variables_helper_full(chan, NULL, template, (*buf)->__AST_STR_STR, (*buf)->__AST_STR_LEN - 1, &((*buf)->__AST_STR_USED));
-		first = 0;
-	} while (maxlen == 0 && (*buf)->__AST_STR_LEN - 5 < (*buf)->__AST_STR_USED);
-}
-
 char *__ast_str_helper2(struct ast_str **buf, size_t maxlen, const char *src, size_t maxsrc, int append, int escapecommas)
 {
 	int dynamic = 0;




More information about the asterisk-commits mailing list