summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 01550c0b1..1dd09850c 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;
}