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>
This commit is contained in:
MARTINSONS Frederic 2021-01-20 13:23:24 +00:00 committed by Philip Withnall
parent 86aec50c0a
commit 47355c358d
4 changed files with 177 additions and 0 deletions

View File

@ -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>

View File

@ -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);
}

View File

@ -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

View File

@ -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();