From: Michal Privoznik <[email protected]>

DBus allows only some characters to be specified verbatim [1].
Everything else must be specified via '%NN' syntax where NN is
hex value of the escaped character. Introduce
virStringEscapeDBus() helper to handle string escaping.

1: 
https://gitlab.freedesktop.org/dbus/dbus/-/blob/2dee5236088bcf690ba92b743a584720b8cb6f55/dbus/dbus-address.c#L288
Signed-off-by: Michal Privoznik <[email protected]>
---
 src/libvirt_private.syms |  1 +
 src/util/virstring.c     | 64 ++++++++++++++++++++++++++++++++++++++++
 src/util/virstring.h     |  1 +
 tests/virstringtest.c    | 43 +++++++++++++++++++++++++++
 4 files changed, 109 insertions(+)

diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 4e57e4a8f6..ef8ddd0330 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -3478,6 +3478,7 @@ virSkipSpacesBackwards;
 virSkipToDigit;
 virStrcpy;
 virStringBufferIsPrintable;
+virStringEscapeDBus;
 virStringFilterChars;
 virStringFormatHex;
 virStringHasCaseSuffix;
diff --git a/src/util/virstring.c b/src/util/virstring.c
index e001d76bf1..0729002d12 100644
--- a/src/util/virstring.c
+++ b/src/util/virstring.c
@@ -1118,3 +1118,67 @@ virStringFormatHex(const unsigned char *buf, size_t len)
 
     return g_steal_pointer(&hex);
 }
+
+
+/**
+ * virStringEscapeDBus:
+ * @str: string to escape
+ *
+ * Produces new string that's safe to pass to DBUS. Specifically,
+ * alphanumerical characters, digits, '-', '_', '/', '\\', '*' and '.' are
+ * kept. Everything else is escaped using "%NN", where NN is value of the
+ * character in hex.
+ *
+ * Returns: newly allocated string. Caller must free.
+ */
+char *
+virStringEscapeDBus(const char *str)
+{
+    size_t len;
+    size_t i;
+    size_t j = 0;
+    size_t alloc = 1;
+    char *ret;
+
+    if (!str)
+        return NULL;
+
+    len = strlen(str);
+    alloc = len * 1.25; /* assume some escaping */
+    ret = g_new0(char, alloc + 1);
+
+    for (i = 0; str[i]; i++) {
+        const char c = str[i];
+
+        if ((c >= 'a' && c <= 'z') ||
+            (c >= 'A' && c <= 'Z') ||
+            (c >= '0' && c <= '9') ||
+            (c == '-') ||
+            (c == '_') ||
+            (c == '/') ||
+            (c == '\\') ||
+            (c == '*') ||
+            (c == '.')) {
+            if (j >= alloc) {
+                ret = g_renew(char, ret, alloc + 11);
+                alloc += 10;
+            }
+
+            ret[j++] = c;
+        } else {
+            static const char hextable[] = "0123456789ABCDEF";
+
+            if (j + 3 >= alloc) {
+                ret = g_renew(char, ret, alloc + 11);
+                alloc += 10;
+            }
+
+            ret[j++] = '%';
+            ret[j++] = hextable[(c >> 4) & 15];
+            ret[j++] = hextable[c & 15];
+        }
+    }
+
+    ret[j++] = '\0';
+    return ret;
+}
diff --git a/src/util/virstring.h b/src/util/virstring.h
index 8c2208ece8..aadfc37e41 100644
--- a/src/util/virstring.h
+++ b/src/util/virstring.h
@@ -141,3 +141,4 @@ int virStringParseVersion(unsigned long long *version,
 
 void virStringListRemoveDuplicates(char ***list);
 char *virStringFormatHex(const unsigned char *buf, size_t len);
+char *virStringEscapeDBus(const char *str);
diff --git a/tests/virstringtest.c b/tests/virstringtest.c
index 0792155cc3..50b0e2cc5c 100644
--- a/tests/virstringtest.c
+++ b/tests/virstringtest.c
@@ -486,6 +486,29 @@ static int testFilterChars(const void *args)
     return 0;
 }
 
+
+struct testDBusData {
+    const char *string;
+    const char *expected;
+};
+
+
+static int
+testDBusEscape(const void *args)
+{
+    const struct testDBusData *data = args;
+    g_autofree char *res = virStringEscapeDBus(data->string);
+
+    if (STRNEQ_NULLABLE(res, data->expected)) {
+        fprintf(stderr, "%s: returned '%s', expected '%s'\n",
+                __FUNCTION__, NULLSTR(res), NULLSTR(data->expected));
+        return -1;
+    }
+
+    return 0;
+}
+
+
 static int
 mymain(void)
 {
@@ -767,6 +790,26 @@ mymain(void)
     TEST_FILTER_CHARS(NULL, NULL, NULL);
     TEST_FILTER_CHARS("hello 123 hello", "helo", "hellohello");
 
+#define TEST_DBUS_ESCAPE(str, exp) \
+    do { \
+        struct testDBusData dbusData = { \
+            .string = str, \
+            .expected = exp, \
+        }; \
+        if (virTestRun("DBus escape " #str, \
+                       testDBusEscape, &dbusData) < 0) \
+            ret = -1; \
+    } while (0)
+
+    TEST_DBUS_ESCAPE(NULL, NULL);
+    TEST_DBUS_ESCAPE("", "");
+    TEST_DBUS_ESCAPE("-_/\\*.", "-_/\\*.");
+    TEST_DBUS_ESCAPE("abcdefghijklmnopqrstuvwxyz",
+                     "abcdefghijklmnopqrstuvwxyz");
+    TEST_DBUS_ESCAPE("/some/~/path/with space/-_\\*./#$",
+                     "/some/%7E/path/with%20space/-_\\*./%23%24");
+
+
     return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
 }
 
-- 
2.51.2

Reply via email to