summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorge Joseph <george.joseph@fairview5.com>2014-11-05 00:17:10 +0000
committerGeorge Joseph <george.joseph@fairview5.com>2014-11-05 00:17:10 +0000
commitd5de94201e969b3eeb6ffcef2b92c3b94694f5ce (patch)
tree83c69bee90a50cdf99f385195559d6b58cc879ed
parentc77a71ad2fd330c20e8f5055044ede1c9ab495b0 (diff)
config: Make text_file_save and 'dialplan save' escape semicolons in values.
When a config file is read, an unescaped semicolon signals comments which are stripped from the value before it's stored. Escaped semicolons are then unescaped and become part of the value. Both of these behaviors are normal and expected. When the config is serialized either by 'dialplan save' or AMI/UpdateConfig however, the now unescaped semicolons are written as-is. If you actually reload the file just saved, the unescaped semicolons are now treated as start of comments. Since true comments are stripped on read, any semicolons in ast_variable.value must have been escaped originally. This patch re-escapes semicolons in ast_variable.values before they're written to file either by 'dialplan save' or config/ast_config_text_file_save which is called by AMI/UpdateConfig. I also fixed a few pre-existing formatting issues nearby in pbx_config.c Tested-by: George Joseph ASTERISK-20127 #close Review: https://reviewboard.asterisk.org/r/4132/ ........ Merged revisions 427275 from http://svn.asterisk.org/svn/asterisk/branches/12 ........ Merged revisions 427276 from http://svn.asterisk.org/svn/asterisk/branches/13 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@427277 65c4cc65-6c06-0410-ace0-fbb531ad65f3
-rw-r--r--include/asterisk/utils.h10
-rw-r--r--main/config.c21
-rw-r--r--main/utils.c31
-rw-r--r--pbx/pbx_config.c31
-rw-r--r--tests/test_strings.c70
5 files changed, 152 insertions, 11 deletions
diff --git a/include/asterisk/utils.h b/include/asterisk/utils.h
index 464ff1cdc..a0e7cb6bf 100644
--- a/include/asterisk/utils.h
+++ b/include/asterisk/utils.h
@@ -319,6 +319,16 @@ int ast_xml_escape(const char *string, char *outbuf, size_t buflen);
char *ast_escape_quoted(const char *string, char *outbuf, int buflen);
/*!
+ * \brief Escape semicolons found in a string.
+ *
+ * \param string string to be escaped
+ * \param outbuf resulting escaped string
+ * \param buflen size of output buffer
+ * \return a pointer to the escaped string
+ */
+char *ast_escape_semicolons(const char *string, char *outbuf, int buflen);
+
+/*!
* \brief Unescape quotes in a string
*
* \param quote_str The string with quotes to be unescaped
diff --git a/main/config.c b/main/config.c
index 23de2c8ba..95f0b696e 100644
--- a/main/config.c
+++ b/main/config.c
@@ -2513,6 +2513,7 @@ int ast_config_text_file_save(const char *configfile, const struct ast_config *c
while (var) {
struct ast_category_template_instance *x;
int found = 0;
+
AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
struct ast_variable *v;
for (v = x->inst->root; v; v = v->next) {
@@ -2558,10 +2559,22 @@ int ast_config_text_file_save(const char *configfile, const struct ast_config *c
if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
fprintf(f,"%s", cmt->cmt);
}
- if (var->sameline)
- fprintf(f, "%s %s %s %s", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
- else
- fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
+
+ { /* Block for 'escaped' scope */
+ int escaped_len = 2 * strlen(var->value) + 1;
+ char escaped[escaped_len];
+
+ ast_escape_semicolons(var->value, escaped, escaped_len);
+
+ if (var->sameline) {
+ fprintf(f, "%s %s %s %s", var->name, (var->object ? "=>" : "="),
+ escaped, var->sameline->cmt);
+ } else {
+ fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="),
+ escaped);
+ }
+ }
+
for (cmt = var->trailing; cmt; cmt=cmt->next) {
if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
fprintf(f,"%s", cmt->cmt);
diff --git a/main/utils.c b/main/utils.c
index 40818c37a..3a095ca7b 100644
--- a/main/utils.c
+++ b/main/utils.c
@@ -484,6 +484,37 @@ char *ast_escape_quoted(const char *string, char *outbuf, int buflen)
return outbuf;
}
+char *ast_escape_semicolons(const char *string, char *outbuf, int buflen)
+{
+ const char *ptr = string;
+ char *out = outbuf;
+
+ if (string == NULL || outbuf == NULL) {
+ ast_assert(string != NULL && outbuf != NULL);
+ return NULL;
+ }
+
+ while (*ptr && out - outbuf < buflen - 1) {
+ if (*ptr == ';') {
+ if (out - outbuf >= buflen - 2) {
+ break;
+ }
+ strcpy(out, "\\;");
+ out += 2;
+ } else {
+ *out = *ptr;
+ out++;
+ }
+ ptr++;
+ }
+
+ if (buflen) {
+ *out = '\0';
+ }
+
+ return outbuf;
+}
+
void ast_unescape_quoted(char *quote_str)
{
int esc_pos;
diff --git a/pbx/pbx_config.c b/pbx/pbx_config.c
index e76f6e892..a42ea47fd 100644
--- a/pbx/pbx_config.c
+++ b/pbx/pbx_config.c
@@ -890,7 +890,11 @@ static char *handle_cli_dialplan_save(struct ast_cli_entry *e, int cmd, struct a
if ((v = ast_variable_browse(cfg, "globals"))) {
fprintf(output, "[globals]\n");
while(v) {
- fprintf(output, "%s => %s\n", v->name, v->value);
+ int escaped_len = 2 * strlen(v->value) + 1;
+ char escaped[escaped_len];
+
+ ast_escape_semicolons(v->value, escaped, escaped_len);
+ fprintf(output, "%s => %s\n", v->name, escaped);
v = v->next;
}
fprintf(output, "\n");
@@ -951,20 +955,33 @@ static char *handle_cli_dialplan_save(struct ast_cli_entry *e, int cmd, struct a
const char *sep, *cid;
const char *el = ast_get_extension_label(p);
char label[128] = "";
-
+ char *appdata = ast_get_extension_app_data(p);
+ char *escaped;
+
if (ast_get_extension_matchcid(p)) {
sep = "/";
cid = ast_get_extension_cidmatch(p);
- } else
+ } else {
sep = cid = "";
-
- if (el && (snprintf(label, sizeof(label), "(%s)", el) != (strlen(el) + 2)))
+ }
+
+ if (el && (snprintf(label, sizeof(label), "(%s)", el) != (strlen(el) + 2))) {
incomplete = 1; /* error encountered or label > 125 chars */
-
+ }
+
+ if (!ast_strlen_zero(appdata)) {
+ int escaped_len = 2 * strlen(appdata) + 1;
+ char escaped[escaped_len];
+
+ ast_escape_semicolons(appdata, escaped, escaped_len);
+ } else {
+ escaped = "";
+ }
+
fprintf(output, "exten => %s%s%s,%d%s,%s(%s)\n",
ast_get_extension_name(p), (ast_strlen_zero(sep) ? "" : sep), (ast_strlen_zero(cid) ? "" : cid),
ast_get_extension_priority(p), label,
- ast_get_extension_app(p), (ast_strlen_zero(ast_get_extension_app_data(p)) ? "" : (const char *)ast_get_extension_app_data(p)));
+ ast_get_extension_app(p), escaped);
}
}
}
diff --git a/tests/test_strings.c b/tests/test_strings.c
index 127ee789d..05cc8df84 100644
--- a/tests/test_strings.c
+++ b/tests/test_strings.c
@@ -387,6 +387,74 @@ AST_TEST_DEFINE(strsep_test)
return AST_TEST_PASS;
}
+static int test_semi(char *string1, char *string2, int test_len)
+{
+ char *test2 = NULL;
+ if (test_len >= 0) {
+ test2 = ast_alloca(test_len);
+ *test2 = '\0';
+ }
+ ast_escape_semicolons(string1, test2, test_len);
+ if (test2 != NULL && strcmp(string2, test2) == 0) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+AST_TEST_DEFINE(escape_semicolons_test)
+{
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "escape_semicolons";
+ info->category = "/main/strings/";
+ info->summary = "Test ast_escape_semicolons";
+ info->description = "Test ast_escape_semicolons";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+
+ ast_test_validate(test, test_semi("this is a ;test", "this is a \\;test", 18));
+ ast_test_validate(test, test_semi(";", "\\;", 3));
+
+ /* The following tests should return empty because there's not enough room to output
+ * an escaped ; or even a single character.
+ */
+ ast_test_validate(test, test_semi(";", "", 0));
+ ast_test_validate(test, test_semi(";", "", 1));
+ ast_test_validate(test, test_semi(";", "", 2));
+ ast_test_validate(test, test_semi("x", "", 0));
+ ast_test_validate(test, test_semi("x", "", 1));
+
+ /* At least some output should be produced now. */
+ ast_test_validate(test, test_semi("xx;xx", "x", 2));
+ ast_test_validate(test, test_semi("xx;xx", "xx", 3));
+
+ /* There's still not enough room to output \; so
+ * don't even print the \
+ */
+ ast_test_validate(test, test_semi("xx;xx", "xx", 4));
+
+ ast_test_validate(test, test_semi("xx;xx", "xx\\;", 5));
+ ast_test_validate(test, test_semi("xx;xx", "xx\\;x", 6));
+ ast_test_validate(test, test_semi("xx;xx", "xx\\;xx", 7));
+ ast_test_validate(test, test_semi("xx;xx", "xx\\;xx", 8));
+
+ /* Random stuff */
+ ast_test_validate(test, test_semi("xx;xx;this is a test", "xx\\;xx\\;this is a test", 32));
+ ast_test_validate(test, test_semi(";;;;;", "\\;\\;\\;\\;\\;", 32));
+ ast_test_validate(test, test_semi(";;;;;", "\\;\\;\\;\\;", 10));
+ ast_test_validate(test, test_semi(";;;;;", "\\;\\;\\;\\;\\;", 11));
+ ast_test_validate(test, test_semi(";;\\;;;", "\\;\\;\\\\;\\;\\;", 32));
+
+ ast_test_status_update(test, "This test should produce 2 'ast_escape_semicolons: FRACK!, Failed assertion' messages.\n");
+ ast_test_validate(test, !test_semi(NULL, "xx\\;xx", 8));
+ ast_test_validate(test, !test_semi("xx;xx", "xx\\;xx", -1));
+
+ return AST_TEST_PASS;
+}
static int unload_module(void)
{
@@ -394,6 +462,7 @@ static int unload_module(void)
AST_TEST_UNREGISTER(begins_with_test);
AST_TEST_UNREGISTER(ends_with_test);
AST_TEST_UNREGISTER(strsep_test);
+ AST_TEST_UNREGISTER(escape_semicolons_test);
return 0;
}
@@ -403,6 +472,7 @@ static int load_module(void)
AST_TEST_REGISTER(begins_with_test);
AST_TEST_REGISTER(ends_with_test);
AST_TEST_REGISTER(strsep_test);
+ AST_TEST_REGISTER(escape_semicolons_test);
return AST_MODULE_LOAD_SUCCESS;
}