<p>Richard Mudgett has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/8038">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">pbx_variables.c: Misc fixes in variable substitution.<br><br>* Copy more than one character at a time when there is nothing to<br>substitute.<br><br>* Fix off by one error if a '}' or ']' is missing.<br><br>* Eliminated the requirement that the "used" parameter had to point to a<br>variable. The current callers were always declaring a variable to meet<br>the requirement and discarding the value put into that variable. Now it<br>can be NULL.<br><br>* In ast_str_substitute_variables_full() fixed using the bogus channel to<br>evaluate a function. We were not using the bogus channel we just created<br>to help evaluate a subexpression.<br><br>Change-Id: Ia83d99f4f16abe47f329eb39b6ff2013ae7c9854<br>---<br>M include/asterisk/pbx.h<br>M main/pbx_variables.c<br>2 files changed, 157 insertions(+), 90 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/38/8038/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">diff --git a/include/asterisk/pbx.h b/include/asterisk/pbx.h<br>index f177b09..6dea744 100644<br>--- a/include/asterisk/pbx.h<br>+++ b/include/asterisk/pbx.h<br>@@ -1378,7 +1378,7 @@<br> * \param c Channel variables from which to extract values, and channel to pass to any dialplan functions.<br> * \param headp If no channel is specified, a channel list from which to extract variable values<br> * \param templ Variable template to expand.<br>- * \param used Number of bytes read from the template.<br>+ * \param used Number of bytes read from the template. (May be NULL)<br> */<br> void ast_str_substitute_variables_full(struct ast_str **buf, ssize_t maxlen, struct ast_channel *c, struct varshead *headp, const char *templ, size_t *used);<br> /*! @} */<br>diff --git a/main/pbx_variables.c b/main/pbx_variables.c<br>index 16ddbd0..f09540b 100644<br>--- a/main/pbx_variables.c<br>+++ b/main/pbx_variables.c<br>@@ -398,51 +398,74 @@<br> void ast_str_substitute_variables_full(struct ast_str **buf, ssize_t maxlen, struct ast_channel *c, struct varshead *headp, const char *templ, size_t *used)<br> {<br> /* Substitutes variables into buf, based on string templ */<br>- char *cp4 = NULL;<br> const char *whereweare;<br>- int orig_size = 0;<br>- int offset, offset2, isfunction;<br>- const char *nextvar, *nextexp, *nextthing;<br>- const char *vars, *vare;<br>- char *finalvars;<br>- int pos, brackets, needsub, len;<br>- struct ast_str *substr1 = ast_str_create(16), *substr2 = NULL, *substr3 = ast_str_create(16);<br>+ struct ast_str *substr1 = ast_str_create(16);<br>+ struct ast_str *substr2 = NULL;<br>+ struct ast_str *substr3 = ast_str_create(16);<br> <br> ast_str_reset(*buf);<br>+<br>+ if (!substr1 || !substr3) {<br>+ if (used) {<br>+ *used = ast_str_strlen(*buf);<br>+ }<br>+ ast_free(substr1);<br>+ ast_free(substr3);<br>+ return;<br>+ }<br>+<br> whereweare = templ;<br> while (!ast_strlen_zero(whereweare)) {<br>+ const char *nextvar = NULL;<br>+ const char *nextexp = NULL;<br>+ const char *nextthing;<br>+ const char *vars;<br>+ const char *vare;<br>+ char *finalvars;<br>+ int pos;<br>+ int brackets;<br>+ int needsub;<br>+ int len;<br>+<br> /* reset our buffer */<br> ast_str_reset(substr3);<br> <br>- /* Assume we're copying the whole remaining string */<br>- pos = strlen(whereweare);<br>- nextvar = NULL;<br>- nextexp = NULL;<br>+ /* Determine how much simply needs to be copied to the output buf. */<br> nextthing = strchr(whereweare, '$');<br> if (nextthing) {<br>+ pos = nextthing - whereweare;<br> switch (nextthing[1]) {<br> case '{':<br>+ /* Variable substitution */<br> nextvar = nextthing;<br>- pos = nextvar - whereweare;<br> break;<br> case '[':<br>+ /* Expression substitution */<br> nextexp = nextthing;<br>- pos = nextexp - whereweare;<br> break;<br> default:<br>- pos = 1;<br>+ /* '$' is not part of a substitution so include it too. */<br>+ ++pos;<br>+ break;<br> }<br>+ } else {<br>+ /* We're copying the whole remaining string */<br>+ pos = strlen(whereweare);<br> }<br> <br> if (pos) {<br> /* Copy that many bytes */<br> ast_str_append_substr(buf, maxlen, whereweare, pos);<br> <br>- templ += pos;<br> whereweare += pos;<br> }<br> <br> if (nextvar) {<br>+ int offset;<br>+ int offset2;<br>+ int isfunction;<br>+ int res;<br>+<br> /* We have a variable. Find the start and end, and determine<br> if we are going to have to recursively call ourselves on the<br> contents */<br>@@ -454,33 +477,42 @@<br> while (brackets && *vare) {<br> if ((vare[0] == '$') && (vare[1] == '{')) {<br> needsub++;<br>+ brackets++;<br>+ vare++;<br> } else if (vare[0] == '{') {<br> brackets++;<br> } else if (vare[0] == '}') {<br> brackets--;<br>- } else if ((vare[0] == '$') && (vare[1] == '['))<br>+ } else if ((vare[0] == '$') && (vare[1] == '[')) {<br> needsub++;<br>+ vare++;<br>+ }<br> vare++;<br> }<br>- if (brackets)<br>+ len = vare - vars;<br>+ if (brackets) {<br> ast_log(LOG_WARNING, "Error in extension logic (missing '}')\n");<br>- len = vare - vars - 1;<br>+ } else {<br>+ /* Don't count the closing '}' in the length. */<br>+ --len;<br>+ }<br> <br> /* Skip totally over variable string */<br>- whereweare += (len + 3);<br>+ whereweare = vare;<br> <br>- /* Store variable name (and truncate) */<br>+ /* Store variable name expression to lookup. */<br> ast_str_set_substr(&substr1, 0, vars, len);<br> ast_debug(5, "Evaluating '%s' (from '%s' len %d)\n", ast_str_buffer(substr1), vars, len);<br> <br> /* Substitute if necessary */<br> if (needsub) {<br>- size_t my_used;<br>-<br> if (!substr2) {<br> substr2 = ast_str_create(16);<br>+ if (!substr2) {<br>+ continue;<br>+ }<br> }<br>- ast_str_substitute_variables_full(&substr2, 0, c, headp, ast_str_buffer(substr1), &my_used);<br>+ ast_str_substitute_variables_full(&substr2, 0, c, headp, ast_str_buffer(substr1), NULL);<br> finalvars = ast_str_buffer(substr2);<br> } else {<br> finalvars = ast_str_buffer(substr1);<br>@@ -490,28 +522,32 @@<br> if (isfunction) {<br> /* Evaluate function */<br> if (c || !headp) {<br>- cp4 = ast_func_read2(c, finalvars, &substr3, 0) ? NULL : ast_str_buffer(substr3);<br>+ res = ast_func_read2(c, finalvars, &substr3, 0);<br> } else {<br> struct varshead old;<br>- struct ast_channel *bogus = ast_dummy_channel_alloc();<br>+ struct ast_channel *bogus;<br>+<br>+ bogus = ast_dummy_channel_alloc();<br> if (bogus) {<br>- memcpy(&old, ast_channel_varshead(bogus), sizeof(old));<br>- memcpy(ast_channel_varshead(bogus), headp, sizeof(*ast_channel_varshead(bogus)));<br>- cp4 = ast_func_read2(c, finalvars, &substr3, 0) ? NULL : ast_str_buffer(substr3);<br>+ old = *ast_channel_varshead(bogus);<br>+ *ast_channel_varshead(bogus) = *headp;<br>+ res = ast_func_read2(bogus, finalvars, &substr3, 0);<br> /* Don't deallocate the varshead that was passed in */<br>- memcpy(ast_channel_varshead(bogus), &old, sizeof(*ast_channel_varshead(bogus)));<br>+ *ast_channel_varshead(bogus) = old;<br> ast_channel_unref(bogus);<br> } else {<br>- ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution. Function results may be blank.\n");<br>+ ast_log(LOG_ERROR, "Unable to allocate bogus channel for function value substitution.\n");<br>+ res = -1;<br> }<br> }<br>- ast_debug(2, "Function %s result is '%s'\n", finalvars, cp4 ? cp4 : "(null)");<br>+ ast_debug(2, "Function %s result is '%s'\n",<br>+ finalvars, res ? "" : ast_str_buffer(substr3));<br> } else {<br> /* Retrieve variable value */<br> ast_str_retrieve_variable(&substr3, 0, c, headp, finalvars);<br>- cp4 = ast_str_buffer(substr3);<br>+ res = 0;<br> }<br>- if (cp4) {<br>+ if (!res) {<br> ast_str_substring(substr3, offset, offset2);<br> ast_str_append(buf, maxlen, "%s", ast_str_buffer(substr3));<br> }<br>@@ -539,24 +575,29 @@<br> }<br> vare++;<br> }<br>- if (brackets)<br>+ len = vare - vars;<br>+ if (brackets) {<br> ast_log(LOG_WARNING, "Error in extension logic (missing ']')\n");<br>- len = vare - vars - 1;<br>+ } else {<br>+ /* Don't count the closing ']' in the length. */<br>+ --len;<br>+ }<br> <br> /* Skip totally over expression */<br>- whereweare += (len + 3);<br>+ whereweare = vare;<br> <br>- /* Store variable name (and truncate) */<br>+ /* Store expression to evaluate. */<br> ast_str_set_substr(&substr1, 0, vars, len);<br> <br> /* Substitute if necessary */<br> if (needsub) {<br>- size_t my_used;<br>-<br> if (!substr2) {<br> substr2 = ast_str_create(16);<br>+ if (!substr2) {<br>+ continue;<br>+ }<br> }<br>- ast_str_substitute_variables_full(&substr2, 0, c, headp, ast_str_buffer(substr1), &my_used);<br>+ ast_str_substitute_variables_full(&substr2, 0, c, headp, ast_str_buffer(substr1), NULL);<br> finalvars = ast_str_buffer(substr2);<br> } else {<br> finalvars = ast_str_buffer(substr1);<br>@@ -568,7 +609,9 @@<br> ast_str_append(buf, maxlen, "%s", ast_str_buffer(substr3));<br> }<br> }<br>- *used = ast_str_strlen(*buf) - orig_size;<br>+ if (used) {<br>+ *used = ast_str_strlen(*buf);<br>+ }<br> ast_free(substr1);<br> ast_free(substr2);<br> ast_free(substr3);<br>@@ -576,49 +619,58 @@<br> <br> void ast_str_substitute_variables(struct ast_str **buf, ssize_t maxlen, struct ast_channel *chan, const char *templ)<br> {<br>- size_t used;<br>- ast_str_substitute_variables_full(buf, maxlen, chan, NULL, templ, &used);<br>+ ast_str_substitute_variables_full(buf, maxlen, chan, NULL, templ, NULL);<br> }<br> <br> void ast_str_substitute_variables_varshead(struct ast_str **buf, ssize_t maxlen, struct varshead *headp, const char *templ)<br> {<br>- size_t used;<br>- ast_str_substitute_variables_full(buf, maxlen, NULL, headp, templ, &used);<br>+ ast_str_substitute_variables_full(buf, maxlen, NULL, headp, templ, NULL);<br> }<br> <br> void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int count, size_t *used)<br> {<br> /* Substitutes variables into cp2, based on string cp1, cp2 NO LONGER NEEDS TO BE ZEROED OUT!!!! */<br>- char *cp4 = NULL;<br>- const char *whereweare, *orig_cp2 = cp2;<br>- int length, offset, offset2, isfunction;<br>+ const char *whereweare;<br>+ const char *orig_cp2 = cp2;<br> char *workspace = NULL;<br>- char *ltmp = NULL, *var = NULL;<br>- char *nextvar, *nextexp, *nextthing;<br>- char *vars, *vare;<br>- int pos, brackets, needsub, len;<br>+ char *ltmp = NULL;<br>+ char *var = NULL;<br> <br> *cp2 = 0; /* just in case nothing ends up there */<br> whereweare = cp1;<br> while (!ast_strlen_zero(whereweare) && count) {<br>- /* Assume we're copying the whole remaining string */<br>- pos = strlen(whereweare);<br>- nextvar = NULL;<br>- nextexp = NULL;<br>+ char *nextvar = NULL;<br>+ char *nextexp = NULL;<br>+ char *nextthing;<br>+ char *vars;<br>+ char *vare;<br>+ int length;<br>+ int pos;<br>+ int brackets;<br>+ int needsub;<br>+ int len;<br>+<br>+ /* Determine how much simply needs to be copied to the output buf. */<br> nextthing = strchr(whereweare, '$');<br> if (nextthing) {<br>+ pos = nextthing - whereweare;<br> switch (nextthing[1]) {<br> case '{':<br>+ /* Variable substitution */<br> nextvar = nextthing;<br>- pos = nextvar - whereweare;<br> break;<br> case '[':<br>+ /* Expression substitution */<br> nextexp = nextthing;<br>- pos = nextexp - whereweare;<br> break;<br> default:<br>- pos = 1;<br>+ /* '$' is not part of a substitution so include it too. */<br>+ ++pos;<br>+ break;<br> }<br>+ } else {<br>+ /* We're copying the whole remaining string */<br>+ pos = strlen(whereweare);<br> }<br> <br> if (pos) {<br>@@ -636,6 +688,11 @@<br> }<br> <br> if (nextvar) {<br>+ int offset;<br>+ int offset2;<br>+ int isfunction;<br>+ char *cp4;<br>+<br> /* We have a variable. Find the start and end, and determine<br> if we are going to have to recursively call ourselves on the<br> contents */<br>@@ -647,35 +704,41 @@<br> while (brackets && *vare) {<br> if ((vare[0] == '$') && (vare[1] == '{')) {<br> needsub++;<br>+ brackets++;<br>+ vare++;<br> } else if (vare[0] == '{') {<br> brackets++;<br> } else if (vare[0] == '}') {<br> brackets--;<br>- } else if ((vare[0] == '$') && (vare[1] == '['))<br>+ } else if ((vare[0] == '$') && (vare[1] == '[')) {<br> needsub++;<br>+ vare++;<br>+ }<br> vare++;<br> }<br>- if (brackets)<br>+ len = vare - vars;<br>+ if (brackets) {<br> ast_log(LOG_WARNING, "Error in extension logic (missing '}')\n");<br>- len = vare - vars - 1;<br>+ } else {<br>+ /* Don't count the closing '}' in the length. */<br>+ --len;<br>+ }<br> <br> /* Skip totally over variable string */<br>- whereweare += (len + 3);<br>+ whereweare = vare;<br> <br> if (!var)<br> var = ast_alloca(VAR_BUF_SIZE);<br> <br>- /* Store variable name (and truncate) */<br>+ /* Store variable name expression to lookup (and truncate). */<br> ast_copy_string(var, vars, len + 1);<br> <br> /* Substitute if necessary */<br> if (needsub) {<br>- size_t my_used;<br>-<br> if (!ltmp) {<br> ltmp = ast_alloca(VAR_BUF_SIZE);<br> }<br>- pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1, &my_used);<br>+ pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1, NULL);<br> vars = ltmp;<br> } else {<br> vars = var;<br>@@ -693,16 +756,19 @@<br> cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace;<br> else {<br> struct varshead old;<br>- struct ast_channel *c = ast_dummy_channel_alloc();<br>- if (c) {<br>- memcpy(&old, ast_channel_varshead(c), sizeof(old));<br>- memcpy(ast_channel_varshead(c), headp, sizeof(*ast_channel_varshead(c)));<br>- cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace;<br>+ struct ast_channel *bogus;<br>+<br>+ bogus = ast_dummy_channel_alloc();<br>+ if (bogus) {<br>+ old = *ast_channel_varshead(bogus);<br>+ *ast_channel_varshead(bogus) = *headp;<br>+ cp4 = ast_func_read(bogus, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace;<br> /* Don't deallocate the varshead that was passed in */<br>- memcpy(ast_channel_varshead(c), &old, sizeof(*ast_channel_varshead(c)));<br>- c = ast_channel_unref(c);<br>+ *ast_channel_varshead(bogus) = old;<br>+ ast_channel_unref(bogus);<br> } else {<br>- ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution. Function results may be blank.\n");<br>+ ast_log(LOG_ERROR, "Unable to allocate bogus channel for function value substitution.\n");<br>+ cp4 = NULL;<br> }<br> }<br> ast_debug(2, "Function %s result is '%s'\n", vars, cp4 ? cp4 : "(null)");<br>@@ -745,34 +811,35 @@<br> }<br> vare++;<br> }<br>- if (brackets)<br>+ len = vare - vars;<br>+ if (brackets) {<br> ast_log(LOG_WARNING, "Error in extension logic (missing ']')\n");<br>- len = vare - vars - 1;<br>+ } else {<br>+ /* Don't count the closing ']' in the length. */<br>+ --len;<br>+ }<br> <br> /* Skip totally over expression */<br>- whereweare += (len + 3);<br>+ whereweare = vare;<br> <br> if (!var)<br> var = ast_alloca(VAR_BUF_SIZE);<br> <br>- /* Store variable name (and truncate) */<br>+ /* Store expression to evaluate (and truncate). */<br> ast_copy_string(var, vars, len + 1);<br> <br> /* Substitute if necessary */<br> if (needsub) {<br>- size_t my_used;<br>-<br> if (!ltmp) {<br> ltmp = ast_alloca(VAR_BUF_SIZE);<br> }<br>- pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1, &my_used);<br>+ pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1, NULL);<br> vars = ltmp;<br> } else {<br> vars = var;<br> }<br> <br> length = ast_expr(vars, cp2, count, c);<br>-<br> if (length) {<br> ast_debug(1, "Expression result is '%s'\n", cp2);<br> count -= length;<br>@@ -781,19 +848,19 @@<br> }<br> }<br> }<br>- *used = cp2 - orig_cp2;<br>+ if (used) {<br>+ *used = cp2 - orig_cp2;<br>+ }<br> }<br> <br> void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count)<br> {<br>- size_t used;<br>- pbx_substitute_variables_helper_full(c, (c) ? ast_channel_varshead(c) : NULL, cp1, cp2, count, &used);<br>+ pbx_substitute_variables_helper_full(c, (c) ? ast_channel_varshead(c) : NULL, cp1, cp2, count, NULL);<br> }<br> <br> void pbx_substitute_variables_varshead(struct varshead *headp, const char *cp1, char *cp2, int count)<br> {<br>- size_t used;<br>- pbx_substitute_variables_helper_full(NULL, headp, cp1, cp2, count, &used);<br>+ pbx_substitute_variables_helper_full(NULL, headp, cp1, cp2, count, NULL);<br> }<br> <br> /*! \brief CLI support for listing global variables in a parseable way */<br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/8038">change 8038</a>. To unsubscribe, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/8038"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 13 </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>
<div style="display:none"> Gerrit-Change-Id: Ia83d99f4f16abe47f329eb39b6ff2013ae7c9854 </div>
<div style="display:none"> Gerrit-Change-Number: 8038 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Richard Mudgett <rmudgett@digium.com> </div>