[glib: 1/2] Add g_dbus_utils_object_path_escape and g_dbus_utils_object_path_unescape




commit 47355c358deec4d6bc1326294e86ca482418ab93
Author: MARTINSONS Frederic <frederic martinsons sigfox com>
Date:   Wed Jan 20 13:23:24 2021 +0000

    Add g_dbus_utils_object_path_escape and g_dbus_utils_object_path_unescape
    
    These two APIs are useful to publish an object which path content is not
    controlled (e.g. dynamically built or coming from external source).
    
    Closes #968
    
    (Rebased and tweaked by Frederic Martinsons)
    
    Signed-off-by: Frederic Martinsons <frederic martinsons sigfox com>

 docs/reference/gio/gio-sections-common.txt |   3 +
 gio/gdbusutils.c                           | 124 +++++++++++++++++++++++++++++
 gio/gdbusutils.h                           |   6 ++
 gio/tests/gdbus-names.c                    |  44 ++++++++++
 4 files changed, 177 insertions(+)
---
diff --git a/docs/reference/gio/gio-sections-common.txt b/docs/reference/gio/gio-sections-common.txt
index 3289bf6c6..945c26ade 100644
--- a/docs/reference/gio/gio-sections-common.txt
+++ b/docs/reference/gio/gio-sections-common.txt
@@ -2804,6 +2804,9 @@ g_dbus_is_member_name
 g_dbus_is_interface_name
 g_dbus_gvalue_to_gvariant
 g_dbus_gvariant_to_gvalue
+g_dbus_escape_object_path_bytestring
+g_dbus_escape_object_path
+g_dbus_unescape_object_path
 </SECTION>
 
 <SECTION>
diff --git a/gio/gdbusutils.c b/gio/gdbusutils.c
index eb7eee9c8..bb34e2d58 100644
--- a/gio/gdbusutils.c
+++ b/gio/gdbusutils.c
@@ -693,3 +693,127 @@ g_dbus_gvalue_to_gvariant (const GValue       *gvalue,
 
   return ret;
 }
+
+/**
+ * g_dbus_escape_object_path_bytestring:
+ * @bytes: (array zero-terminated=1) (element-type guint8): the string of bytes to escape
+ *
+ * Escapes @bytes for use in a D-Bus object path component.
+ * @bytes is an array of zero or more nonzero bytes in an
+ * unspecified encoding, followed by a single zero byte.
+ *
+ * The escaping method consists of replacing all non-alphanumeric
+ * characters (see g_ascii_isalnum()) with their hexadecimal value
+ * preceded by an underscore (`_`). For example:
+ * `foo.bar.baz` will become `foo_2ebar_2ebaz`.
+ *
+ * This method is appropriate to use when the input is nearly
+ * a valid object path component but is not when your input
+ * is far from being a valid object path component.
+ * Other escaping algorithms are also valid to use with
+ * D-Bus object paths.
+ *
+ * This can be reversed with g_dbus_unescape_object_path().
+ *
+ * Returns: an escaped version of @bytes. Free with g_free().
+ *
+ * Since: 2.68
+ *
+ */
+gchar *
+g_dbus_escape_object_path_bytestring (const guint8 *bytes)
+{
+  GString *escaped;
+  const guint8 *p;
+
+  g_return_val_if_fail (bytes != NULL, NULL);
+
+  if (*bytes == '\0')
+    return g_strdup ("_");
+
+  escaped = g_string_new (NULL);
+  for (p = bytes; *p; p++)
+    {
+      if (g_ascii_isalnum (*p))
+        g_string_append_c (escaped, *p);
+      else
+        g_string_append_printf (escaped, "_%02x", *p);
+    }
+
+  return g_string_free (escaped, FALSE);
+}
+
+/**
+ * g_dbus_escape_object_path:
+ * @s: the string to escape
+ *
+ * This is a language binding friendly version of g_dbus_escape_object_path_bytestring().
+ *
+ * Returns: an escaped version of @s. Free with g_free().
+ *
+ * Since: 2.68
+ */
+gchar *
+g_dbus_escape_object_path (const gchar *s)
+{
+  return (gchar *) g_dbus_escape_object_path_bytestring ((const guint8 *) s);
+}
+
+/**
+ * g_dbus_unescape_object_path:
+ * @s: the string to unescape
+ *
+ * Unescapes an string that was previously escaped with
+ * g_dbus_escape_object_path(). If the string is in a format that could
+ * not have been returned by g_dbus_escape_object_path(), this function
+ * returns %NULL.
+ *
+ * Encoding alphanumeric characters which do not need to be
+ * encoded is not allowed (e.g `_63` is not valid, the string
+ * should contain `c` instead).
+ *
+ * Returns: (array zero-terminated=1) (element-type guint8) (nullable): an
+ *   unescaped version of @s, or %NULL if @s is not a string returned
+ *   from g_dbus_escape_object_path(). Free with g_free().
+ *
+ * Since: 2.68
+ */
+guint8 *
+g_dbus_unescape_object_path (const gchar *s)
+{
+  GString *unescaped;
+  const gchar *p;
+
+  g_return_val_if_fail (s != NULL, NULL);
+
+  if (g_str_equal (s, "_"))
+    return (guint8 *) g_strdup ("");
+
+  unescaped = g_string_new (NULL);
+  for (p = s; *p; p++)
+    {
+      gint hi, lo;
+
+      if (g_ascii_isalnum (*p))
+        {
+          g_string_append_c (unescaped, *p);
+        }
+      else if (*p == '_' &&
+               ((hi = g_ascii_xdigit_value (p[1])) >= 0) &&
+               ((lo = g_ascii_xdigit_value (p[2])) >= 0) &&
+               (hi || lo) &&                      /* \0 is not allowed */
+               !g_ascii_isalnum ((hi << 4) | lo)) /* alnums must not be encoded */
+        {
+          g_string_append_c (unescaped, (hi << 4) | lo);
+          p += 2;
+        }
+      else
+        {
+          /* the string was not encoded correctly */
+          g_string_free (unescaped, TRUE);
+          return NULL;
+        }
+    }
+
+  return (guint8 *) g_string_free (unescaped, FALSE);
+}
diff --git a/gio/gdbusutils.h b/gio/gdbusutils.h
index 5aecb5142..fd7358fcf 100644
--- a/gio/gdbusutils.h
+++ b/gio/gdbusutils.h
@@ -49,6 +49,12 @@ void g_dbus_gvariant_to_gvalue (GVariant  *value,
 GLIB_AVAILABLE_IN_ALL
 GVariant *g_dbus_gvalue_to_gvariant (const GValue         *gvalue,
                                      const GVariantType   *type);
+GLIB_AVAILABLE_IN_2_68
+gchar *g_dbus_escape_object_path_bytestring (const guint8 *bytes);
+GLIB_AVAILABLE_IN_2_68
+gchar *g_dbus_escape_object_path (const gchar *s);
+GLIB_AVAILABLE_IN_2_68
+guint8 *g_dbus_unescape_object_path (const gchar *s);
 
 G_END_DECLS
 
diff --git a/gio/tests/gdbus-names.c b/gio/tests/gdbus-names.c
index 20f429e58..c1d925342 100644
--- a/gio/tests/gdbus-names.c
+++ b/gio/tests/gdbus-names.c
@@ -769,6 +769,49 @@ test_validate_names (void)
     }
 }
 
+static void
+assert_cmp_escaped_object_path (const gchar *s,
+                                const gchar *correct_escaped)
+{
+  gchar *escaped;
+  guint8 *unescaped;
+
+  escaped = g_dbus_escape_object_path (s);
+  g_assert_cmpstr (escaped, ==, correct_escaped);
+
+  g_free (escaped);
+  escaped = g_dbus_escape_object_path_bytestring ((const guint8 *) s);
+  g_assert_cmpstr (escaped, ==, correct_escaped);
+
+  unescaped = g_dbus_unescape_object_path (escaped);
+  g_assert_cmpstr ((const gchar *) unescaped, ==, s);
+
+  g_free (escaped);
+  g_free (unescaped);
+}
+
+static void
+test_escape_object_path (void)
+{
+  assert_cmp_escaped_object_path ("Foo42", "Foo42");
+  assert_cmp_escaped_object_path ("foo.bar.baz", "foo_2ebar_2ebaz");
+  assert_cmp_escaped_object_path ("foo_bar_baz", "foo_5fbar_5fbaz");
+  assert_cmp_escaped_object_path ("_", "_5f");
+  assert_cmp_escaped_object_path ("__", "_5f_5f");
+  assert_cmp_escaped_object_path ("", "_");
+  assert_cmp_escaped_object_path (":1.42", "_3a1_2e42");
+  assert_cmp_escaped_object_path ("a/b", "a_2fb");
+  assert_cmp_escaped_object_path (" ", "_20");
+  assert_cmp_escaped_object_path ("\n", "_0a");
+
+  g_assert_null (g_dbus_unescape_object_path ("_ii"));
+  g_assert_null (g_dbus_unescape_object_path ("döner"));
+  g_assert_null (g_dbus_unescape_object_path ("_00"));
+  g_assert_null (g_dbus_unescape_object_path ("_61"));
+  g_assert_null (g_dbus_unescape_object_path ("_ga"));
+  g_assert_null (g_dbus_unescape_object_path ("_ag"));
+}
+
 /* ---------------------------------------------------------------------------------------------------- */
 
 int
@@ -786,6 +829,7 @@ main (int   argc,
   g_test_add_func ("/gdbus/validate-names", test_validate_names);
   g_test_add_func ("/gdbus/bus-own-name", test_bus_own_name);
   g_test_add_func ("/gdbus/bus-watch-name", test_bus_watch_name);
+  g_test_add_func ("/gdbus/escape-object-path", test_escape_object_path);
 
   ret = g_test_run();
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]