[pulseaudio-discuss] [PATCH v3 6/6] message-handler: Allow parameter strings to contain escaped curly braces
Georg Chini
georg at chini.tk
Mon Jan 22 18:12:56 UTC 2018
For string parameters that contain curly braces, those braces must be escaped
by adding a "\" before them. This however means that a trailing backslash would
falsely escape the closing bracket. To avoid this, single quotes must be added
at start and end of the string. A function pa_strbuf_put_escaped_parameter_string()
was added to the string buffer functions which must be used to correctly format a
string which might fulfill one of the following conditions:
- It contains curly braces
- It contains a trailing "\"
- It is enclosed in single quotes
Other strings can be passed without modification.
If pa_split_message_parameter_string() returns a string, it reverts the changes
that pa_strbuf_put_escaped_parameter_string() has introduced when the last
enclosing brackets are removed.
---
doc/messaging_api.txt | 17 +++++++++++
src/pulse/util.c | 66 ++++++++++++++++++++++++++++++++++++-----
src/pulsecore/core.c | 4 +--
src/pulsecore/message-handler.c | 36 +++++++++++-----------
src/pulsecore/strbuf.c | 26 ++++++++++++++++
src/pulsecore/strbuf.h | 1 +
6 files changed, 123 insertions(+), 27 deletions(-)
diff --git a/doc/messaging_api.txt b/doc/messaging_api.txt
index 07500724..7f7056a6 100644
--- a/doc/messaging_api.txt
+++ b/doc/messaging_api.txt
@@ -18,6 +18,23 @@ to specify message parameters. The following reference lists available messages,
their parameters and return values. If a return value is enclosed in {}, this
means that multiple elements of the same type may be returned.
+For string parameters that contain curly braces, those braces must be escaped
+by adding a "\" before them. This however means that a trailing backslash would
+falsely escape the closing bracket. To avoid this, single quotes must be added
+at start and end of the string. A function pa_strbuf_put_escaped_parameter_string()
+was added to the string buffer functions which must be used to correctly format a
+string which might fulfill one of the following conditions:
+
+- It contains curly braces
+- It contains a trailing "\"
+- It is enclosed in single quotes
+
+Other strings can be passed without modification.
+
+The function pa_split_message_parameter_string() can be used to parse the message
+parameter string. This function also reverts the changes that
+pa_strbuf_put_escaped_parameter_string() has introduced.
+
Object path: /core
Message: list-handlers
Parameters: None
diff --git a/src/pulse/util.c b/src/pulse/util.c
index c3a3320a..137aebd7 100644
--- a/src/pulse/util.c
+++ b/src/pulse/util.c
@@ -350,13 +350,18 @@ int pa_msleep(unsigned long t) {
* length. If max_length is not 0, it is verified that the retrieved
* element is within the bounds of the parent element. If the parameter
* element is not NULL, a newly allocated string containing the retrieved
- * element is returned. The caller is responsible to free the string.
+ * element is returned. In that case, "\" characters before curly braces
+ * and leading and trailing single quotes are removed if no enclosing
+ * braces remain. This does not modify the original string. The caller
+ * is responsible to free the string returned in element.
* The variable state points to, should be initialized to NULL before
* the first call. The function returns 1 on success, 0 if end of string
* or end of element is encountered and -1 on parse error. */
int pa_split_message_parameter_string(const char *c, uint32_t max_length, const char **start_pos, uint32_t *length, char **element, const char **state) {
const char *current = *state ? *state : c;
uint32_t open_braces;
+ bool unpacked = true;
+ bool found_backslash = false;
pa_assert(start_pos);
pa_assert(length);
@@ -370,10 +375,17 @@ int pa_split_message_parameter_string(const char *c, uint32_t max_length, const
/* Find opening brace */
while (*current != 0) {
- if (*current == '{')
+ /* Skip escaped curly braces. */
+ if (*current == '\\') {
+ found_backslash = true;
+ current++;
+ continue;
+ }
+
+ if (*current == '{' && !found_backslash)
break;
- if (*current == '}') {
+ if (*current == '}' && !found_backslash) {
/* reached end of element, same as end of string */
if (current == c + max_length)
@@ -383,22 +395,35 @@ int pa_split_message_parameter_string(const char *c, uint32_t max_length, const
return -1;
}
+ found_backslash = false;
current++;
}
/* No opening brace found, end of string */
if (*current == 0)
- return 0;
+ return 0;
*start_pos = current + 1;
+ found_backslash = false;
open_braces = 1;
while (open_braces != 0 && *current != 0) {
current++;
- if (*current == '{')
+
+ /* Skip escaped curly braces. */
+ if (*current == '\\') {
+ found_backslash = true;
+ continue;
+ }
+
+ if (*current == '{' && !found_backslash) {
open_braces++;
- if (*current == '}')
+ unpacked = false;
+ }
+ if (*current == '}' && !found_backslash)
open_braces--;
+
+ found_backslash = false;
}
/* Parse error, closing brace missing */
@@ -416,8 +441,35 @@ int pa_split_message_parameter_string(const char *c, uint32_t max_length, const
*state = current + 1;
*length = current - *start_pos;
- if (element)
+ if (element) {
+ char *result, *tmp;
+
*element = pa_xstrndup(*start_pos, *length);
+ /* Result is not completely unpacked, so do not remove escaping */
+ if (!unpacked)
+ return 1;
+
+ result = *element;
+
+ /* Remove leading and trailing quotes if present */
+ if (*result == '\'' && result[strlen(result) - 1] == '\'') {
+ memmove(result, result + 1, strlen(result));
+ result[strlen(result) - 1] = 0;
+ }
+
+ /* Remove escape character from curly braces if present. */
+ while ((tmp = strstr(result, "\\{")))
+ memmove(tmp, tmp + 1, strlen(result) - (size_t)(tmp - result));
+ while ((tmp = strstr(result, "\\}")))
+ memmove(tmp, tmp + 1, strlen(result) - (size_t)(tmp - result));
+
+ /* Remove trailing 0's. */
+ tmp = pa_xstrdup(result);
+ pa_xfree(result);
+
+ *element = tmp;
+ }
+
return 1;
}
diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c
index 264c9999..1edeb9fd 100644
--- a/src/pulsecore/core.c
+++ b/src/pulsecore/core.c
@@ -72,11 +72,11 @@ static char *message_handler_list(pa_core *c) {
buf = pa_strbuf_new();
PA_HASHMAP_FOREACH(handler, c->message_handlers, state) {
- pa_strbuf_puts(buf, "{");
+ pa_strbuf_putc(buf, '{');
pa_strbuf_printf(buf, "{%s} {", handler->object_path);
if (handler->description)
- pa_strbuf_puts(buf, handler->description);
+ pa_strbuf_put_escaped_parameter_string(buf, handler->description);
pa_strbuf_puts(buf, "}}");
}
diff --git a/src/pulsecore/message-handler.c b/src/pulsecore/message-handler.c
index db6b06ce..d51462d6 100644
--- a/src/pulsecore/message-handler.c
+++ b/src/pulsecore/message-handler.c
@@ -31,15 +31,25 @@
#include "message-handler.h"
-/* Check if a string does not contain control characters. Currently these are
- * only "{" and "}". */
-static bool string_is_valid(const char *test_string) {
+/* Check if a path string starts with a / and only contains valid characters */
+static bool object_path_is_valid(const char *test_string) {
uint32_t i;
+ if (test_string[0] != '/')
+ return false;
+
for (i = 0; test_string[i]; i++) {
- if (test_string[i] == '{' ||
- test_string[i] == '}')
- return false;
+
+ if ((test_string[i] >= 'a' && test_string[i] <= 'z') ||
+ (test_string[i] >= 'A' && test_string[i] <= 'Z') ||
+ (test_string[i] >= '0' && test_string[i] <= '9') ||
+ test_string[i] == '.' ||
+ test_string[i] == '_' ||
+ test_string[i] == '-' ||
+ test_string[i] == '/')
+ continue;
+
+ return false;
}
return true;
@@ -56,13 +66,8 @@ void pa_message_handler_register(pa_core *c, const char *object_path, const char
pa_assert(cb);
pa_assert(userdata);
- /* Ensure that the object path is not empty and starts with "/". */
- pa_assert(object_path[0] == '/');
-
- /* Ensure that object path and description are valid strings */
- pa_assert(string_is_valid(object_path));
- if (description)
- pa_assert(string_is_valid(description));
+ /* Ensure that object path is valid */
+ pa_assert(object_path_is_valid(object_path));
handler = pa_xnew0(struct pa_message_handler, 1);
handler->userdata = userdata;
@@ -116,11 +121,6 @@ int pa_message_handler_set_description(pa_core *c, const char *object_path, cons
if (!(handler = pa_hashmap_get(c->message_handlers, object_path)))
return -PA_ERR_NOENTITY;
- if (description) {
- if (!string_is_valid(description))
- return -PA_ERR_INVALID;
- }
-
pa_xfree(handler->description);
handler->description = pa_xstrdup(description);
diff --git a/src/pulsecore/strbuf.c b/src/pulsecore/strbuf.c
index 11f131bf..8d5127fe 100644
--- a/src/pulsecore/strbuf.c
+++ b/src/pulsecore/strbuf.c
@@ -191,3 +191,29 @@ bool pa_strbuf_isempty(pa_strbuf *sb) {
return sb->length <= 0;
}
+
+/* Adds leading and trailing single quotes and also a "\" before curly
+ * braces to the input string while putting the string into the buffer.
+ * Used to prepare a string to be sent as part of a message parameter
+ * list. The function only needs to be used if the original string might
+ * fulfill any of the following conditions:
+ * - It contains curly braces
+ * - It is enclosed in single quotes
+ * - It ends with a "\" */
+void pa_strbuf_put_escaped_parameter_string(pa_strbuf *buf, const char *in_str) {
+ const char *s;
+
+ pa_assert(in_str);
+ pa_assert(buf);
+
+ pa_strbuf_putc(buf, '\'');
+
+ for (s = in_str; *s; ++s) {
+ if (*s == '{' || *s == '}')
+ pa_strbuf_putc(buf, '\\');
+
+ pa_strbuf_putc(buf, *s);
+ }
+
+ pa_strbuf_putc(buf, '\'');
+}
diff --git a/src/pulsecore/strbuf.h b/src/pulsecore/strbuf.h
index 469f6f78..b57125b4 100644
--- a/src/pulsecore/strbuf.h
+++ b/src/pulsecore/strbuf.h
@@ -34,6 +34,7 @@ size_t pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) PA_GCC_PRINTF_A
void pa_strbuf_puts(pa_strbuf *sb, const char *t);
void pa_strbuf_putsn(pa_strbuf *sb, const char *t, size_t m);
void pa_strbuf_putc(pa_strbuf *sb, char c);
+void pa_strbuf_put_escaped_parameter_string(pa_strbuf *buf, const char *in_str);
bool pa_strbuf_isempty(pa_strbuf *sb);
--
2.14.1
More information about the pulseaudio-discuss
mailing list