From 47355c358deec4d6bc1326294e86ca482418ab93 Mon Sep 17 00:00:00 2001 From: MARTINSONS Frederic Date: Wed, 20 Jan 2021 13:23:24 +0000 Subject: 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 --- 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
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(); -- cgit v1.2.1