1 /* GDBus - GLib D-Bus Library
2 *
3 * Copyright (C) 2008-2010 Red Hat, Inc.
4 *
5 * SPDX-License-Identifier: LGPL-2.1-or-later
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General
18 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 *
20 * Author: David Zeuthen <davidz@redhat.com>
21 */
22
23 #include "config.h"
24
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include "gdbusutils.h"
29
30 #include "glibintl.h"
31
32 static gboolean
33 is_valid_bus_name_character (gint c,
34 gboolean allow_hyphen)
35 {
36 return
37 (c >= '0' && c <= '9') ||
38 (c >= 'A' && c <= 'Z') ||
39 (c >= 'a' && c <= 'z') ||
40 (c == '_') ||
41 (allow_hyphen && c == '-');
42 }
43
44 static gboolean
45 is_valid_initial_bus_name_character (gint c,
46 gboolean allow_initial_digit,
47 gboolean allow_hyphen)
48 {
49 if (allow_initial_digit)
50 return is_valid_bus_name_character (c, allow_hyphen);
51 else
52 return
53 (c >= 'A' && c <= 'Z') ||
54 (c >= 'a' && c <= 'z') ||
55 (c == '_') ||
56 (allow_hyphen && c == '-');
57 }
58
59 static gboolean
60 is_valid_name (const gchar *start,
61 guint len,
62 gboolean allow_initial_digit,
63 gboolean allow_hyphen)
64 {
65 gboolean ret;
66 const gchar *s;
67 const gchar *end;
68 gboolean has_dot;
69
70 ret = FALSE;
71
72 if (len == 0)
73 goto out;
74
75 s = start;
76 end = s + len;
77 has_dot = FALSE;
78 while (s != end)
79 {
80 if (*s == '.')
81 {
82 s += 1;
83 if (G_UNLIKELY (!is_valid_initial_bus_name_character (*s, allow_initial_digit, allow_hyphen)))
84 goto out;
85 has_dot = TRUE;
86 }
87 else if (G_UNLIKELY (!is_valid_bus_name_character (*s, allow_hyphen)))
88 {
89 goto out;
90 }
91 s += 1;
92 }
93
94 if (G_UNLIKELY (!has_dot))
95 goto out;
96
97 ret = TRUE;
98
99 out:
100 return ret;
101 }
102
103 /**
104 * g_dbus_is_name:
105 * @string: The string to check.
106 *
107 * Checks if @string is a valid D-Bus bus name (either unique or well-known).
108 *
109 * Returns: %TRUE if valid, %FALSE otherwise.
110 *
111 * Since: 2.26
112 */
113 gboolean
114 g_dbus_is_name (const gchar *string)
115 {
116 guint len;
117 gboolean ret;
118 const gchar *s;
119
120 g_return_val_if_fail (string != NULL, FALSE);
121
122 ret = FALSE;
123
124 len = strlen (string);
125 if (G_UNLIKELY (len == 0 || len > 255))
126 goto out;
127
128 s = string;
129 if (*s == ':')
130 {
131 /* handle unique name */
132 if (!is_valid_name (s + 1, len - 1, TRUE, TRUE))
133 goto out;
134 ret = TRUE;
135 goto out;
136 }
137 else if (G_UNLIKELY (*s == '.'))
138 {
139 /* can't start with a . */
140 goto out;
141 }
142 else if (G_UNLIKELY (!is_valid_initial_bus_name_character (*s, FALSE, TRUE)))
143 goto out;
144
145 ret = is_valid_name (s + 1, len - 1, FALSE, TRUE);
146
147 out:
148 return ret;
149 }
150
151 /**
152 * g_dbus_is_unique_name:
153 * @string: The string to check.
154 *
155 * Checks if @string is a valid D-Bus unique bus name.
156 *
157 * Returns: %TRUE if valid, %FALSE otherwise.
158 *
159 * Since: 2.26
160 */
161 gboolean
162 g_dbus_is_unique_name (const gchar *string)
163 {
164 gboolean ret;
165 guint len;
166
167 g_return_val_if_fail (string != NULL, FALSE);
168
169 ret = FALSE;
170
171 len = strlen (string);
172 if (G_UNLIKELY (len == 0 || len > 255))
173 goto out;
174
175 if (G_UNLIKELY (*string != ':'))
176 goto out;
177
178 if (G_UNLIKELY (!is_valid_name (string + 1, len - 1, TRUE, TRUE)))
179 goto out;
180
181 ret = TRUE;
182
183 out:
184 return ret;
185 }
186
187 /**
188 * g_dbus_is_member_name:
189 * @string: The string to check.
190 *
191 * Checks if @string is a valid D-Bus member (e.g. signal or method) name.
192 *
193 * Returns: %TRUE if valid, %FALSE otherwise.
194 *
195 * Since: 2.26
196 */
197 gboolean
198 g_dbus_is_member_name (const gchar *string)
199 {
200 gboolean ret;
201 guint n;
202
203 ret = FALSE;
204 if (G_UNLIKELY (string == NULL))
205 goto out;
206
207 if (G_UNLIKELY (!is_valid_initial_bus_name_character (string[0], FALSE, FALSE)))
208 goto out;
209
210 for (n = 1; string[n] != '\0'; n++)
211 {
212 if (G_UNLIKELY (!is_valid_bus_name_character (string[n], FALSE)))
213 {
214 goto out;
215 }
216 }
217
218 ret = TRUE;
219
220 out:
221 return ret;
222 }
223
224 /**
225 * g_dbus_is_interface_name:
226 * @string: The string to check.
227 *
228 * Checks if @string is a valid D-Bus interface name.
229 *
230 * Returns: %TRUE if valid, %FALSE otherwise.
231 *
232 * Since: 2.26
233 */
234 gboolean
235 g_dbus_is_interface_name (const gchar *string)
236 {
237 guint len;
238 gboolean ret;
239 const gchar *s;
240
241 g_return_val_if_fail (string != NULL, FALSE);
242
243 ret = FALSE;
244
245 len = strlen (string);
246 if (G_UNLIKELY (len == 0 || len > 255))
247 goto out;
248
249 s = string;
250 if (G_UNLIKELY (*s == '.'))
251 {
252 /* can't start with a . */
253 goto out;
254 }
255 else if (G_UNLIKELY (!is_valid_initial_bus_name_character (*s, FALSE, FALSE)))
256 goto out;
257
258 ret = is_valid_name (s + 1, len - 1, FALSE, FALSE);
259
260 out:
261 return ret;
262 }
263
264 /**
265 * g_dbus_is_error_name:
266 * @string: The string to check.
267 *
268 * Check whether @string is a valid D-Bus error name.
269 *
270 * This function returns the same result as g_dbus_is_interface_name(),
271 * because D-Bus error names are defined to have exactly the
272 * same syntax as interface names.
273 *
274 * Returns: %TRUE if valid, %FALSE otherwise.
275 *
276 * Since: 2.70
277 */
278 gboolean
279 g_dbus_is_error_name (const gchar *string)
280 {
281 /* Error names are the same syntax as interface names.
282 * See https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names-error */
283 return g_dbus_is_interface_name (string);
284 }
285
286 /* ---------------------------------------------------------------------------------------------------- */
287
288 /* TODO: maybe move to glib? if so, it should conform to http://en.wikipedia.org/wiki/Guid and/or
289 * http://tools.ietf.org/html/rfc4122 - specifically it should have hyphens then.
290 */
291
292 /**
293 * g_dbus_generate_guid:
294 *
295 * Generate a D-Bus GUID that can be used with
296 * e.g. g_dbus_connection_new().
297 *
298 * See the
299 * [D-Bus specification](https://dbus.freedesktop.org/doc/dbus-specification.html#uuids)
300 * regarding what strings are valid D-Bus GUIDs. The specification refers to
301 * these as ‘UUIDs’ whereas GLib (for historical reasons) refers to them as
302 * ‘GUIDs’. The terms are interchangeable.
303 *
304 * Note that D-Bus GUIDs do not follow
305 * [RFC 4122](https://datatracker.ietf.org/doc/html/rfc4122).
306 *
307 * Returns: A valid D-Bus GUID. Free with g_free().
308 *
309 * Since: 2.26
310 */
311 gchar *
312 g_dbus_generate_guid (void)
313 {
314 GString *s;
315 guint32 r1;
316 guint32 r2;
317 guint32 r3;
318 gint64 now_us;
319
320 s = g_string_new (NULL);
321
322 r1 = g_random_int ();
323 r2 = g_random_int ();
324 r3 = g_random_int ();
325 now_us = g_get_real_time ();
326
327 g_string_append_printf (s, "%08x", r1);
328 g_string_append_printf (s, "%08x", r2);
329 g_string_append_printf (s, "%08x", r3);
330 g_string_append_printf (s, "%08x", (guint32) (now_us / G_USEC_PER_SEC));
331
332 return g_string_free (s, FALSE);
333 }
334
335 /**
336 * g_dbus_is_guid:
337 * @string: The string to check.
338 *
339 * Checks if @string is a D-Bus GUID.
340 *
341 * See the documentation for g_dbus_generate_guid() for more information about
342 * the format of a GUID.
343 *
344 * Returns: %TRUE if @string is a GUID, %FALSE otherwise.
345 *
346 * Since: 2.26
347 */
348 gboolean
349 g_dbus_is_guid (const gchar *string)
350 {
351 gboolean ret;
352 guint n;
353
354 g_return_val_if_fail (string != NULL, FALSE);
355
356 ret = FALSE;
357
358 for (n = 0; n < 32; n++)
359 {
360 if (!g_ascii_isxdigit (string[n]))
361 goto out;
362 }
363 if (string[32] != '\0')
364 goto out;
365
366 ret = TRUE;
367
368 out:
369 return ret;
370 }
371
372 /* ---------------------------------------------------------------------------------------------------- */
373
374 /**
375 * g_dbus_gvariant_to_gvalue:
376 * @value: A #GVariant.
377 * @out_gvalue: (out): Return location pointing to a zero-filled (uninitialized) #GValue.
378 *
379 * Converts a #GVariant to a #GValue. If @value is floating, it is consumed.
380 *
381 * The rules specified in the g_dbus_gvalue_to_gvariant() function are
382 * used - this function is essentially its reverse form. So, a #GVariant
383 * containing any basic or string array type will be converted to a #GValue
384 * containing a basic value or string array. Any other #GVariant (handle,
385 * variant, tuple, dict entry) will be converted to a #GValue containing that
386 * #GVariant.
387 *
388 * The conversion never fails - a valid #GValue is always returned in
389 * @out_gvalue.
390 *
391 * Since: 2.30
392 */
393 void
394 g_dbus_gvariant_to_gvalue (GVariant *value,
395 GValue *out_gvalue)
396 {
397 const GVariantType *type;
398 gchar **array;
399
400 g_return_if_fail (value != NULL);
401 g_return_if_fail (out_gvalue != NULL);
402
403 memset (out_gvalue, '\0', sizeof (GValue));
404
405 switch (g_variant_classify (value))
406 {
407 case G_VARIANT_CLASS_BOOLEAN:
408 g_value_init (out_gvalue, G_TYPE_BOOLEAN);
409 g_value_set_boolean (out_gvalue, g_variant_get_boolean (value));
410 break;
411
412 case G_VARIANT_CLASS_BYTE:
413 g_value_init (out_gvalue, G_TYPE_UCHAR);
414 g_value_set_uchar (out_gvalue, g_variant_get_byte (value));
415 break;
416
417 case G_VARIANT_CLASS_INT16:
418 g_value_init (out_gvalue, G_TYPE_INT);
419 g_value_set_int (out_gvalue, g_variant_get_int16 (value));
420 break;
421
422 case G_VARIANT_CLASS_UINT16:
423 g_value_init (out_gvalue, G_TYPE_UINT);
424 g_value_set_uint (out_gvalue, g_variant_get_uint16 (value));
425 break;
426
427 case G_VARIANT_CLASS_INT32:
428 g_value_init (out_gvalue, G_TYPE_INT);
429 g_value_set_int (out_gvalue, g_variant_get_int32 (value));
430 break;
431
432 case G_VARIANT_CLASS_UINT32:
433 g_value_init (out_gvalue, G_TYPE_UINT);
434 g_value_set_uint (out_gvalue, g_variant_get_uint32 (value));
435 break;
436
437 case G_VARIANT_CLASS_INT64:
438 g_value_init (out_gvalue, G_TYPE_INT64);
439 g_value_set_int64 (out_gvalue, g_variant_get_int64 (value));
440 break;
441
442 case G_VARIANT_CLASS_UINT64:
443 g_value_init (out_gvalue, G_TYPE_UINT64);
444 g_value_set_uint64 (out_gvalue, g_variant_get_uint64 (value));
445 break;
446
447 case G_VARIANT_CLASS_DOUBLE:
448 g_value_init (out_gvalue, G_TYPE_DOUBLE);
449 g_value_set_double (out_gvalue, g_variant_get_double (value));
450 break;
451
452 case G_VARIANT_CLASS_STRING:
453 g_value_init (out_gvalue, G_TYPE_STRING);
454 g_value_set_string (out_gvalue, g_variant_get_string (value, NULL));
455 break;
456
457 case G_VARIANT_CLASS_OBJECT_PATH:
458 g_value_init (out_gvalue, G_TYPE_STRING);
459 g_value_set_string (out_gvalue, g_variant_get_string (value, NULL));
460 break;
461
462 case G_VARIANT_CLASS_SIGNATURE:
463 g_value_init (out_gvalue, G_TYPE_STRING);
464 g_value_set_string (out_gvalue, g_variant_get_string (value, NULL));
465 break;
466
467 case G_VARIANT_CLASS_ARRAY:
468 type = g_variant_get_type (value);
469 switch (g_variant_type_peek_string (type)[1])
470 {
471 case G_VARIANT_CLASS_BYTE:
472 g_value_init (out_gvalue, G_TYPE_STRING);
473 g_value_set_string (out_gvalue, g_variant_get_bytestring (value));
474 break;
475
476 case G_VARIANT_CLASS_STRING:
477 g_value_init (out_gvalue, G_TYPE_STRV);
478 array = g_variant_dup_strv (value, NULL);
479 g_value_take_boxed (out_gvalue, array);
480 break;
481
482 case G_VARIANT_CLASS_OBJECT_PATH:
483 g_value_init (out_gvalue, G_TYPE_STRV);
484 array = g_variant_dup_objv (value, NULL);
485 g_value_take_boxed (out_gvalue, array);
486 break;
487
488 case G_VARIANT_CLASS_ARRAY:
489 switch (g_variant_type_peek_string (type)[2])
490 {
491 case G_VARIANT_CLASS_BYTE:
492 g_value_init (out_gvalue, G_TYPE_STRV);
493 array = g_variant_dup_bytestring_array (value, NULL);
494 g_value_take_boxed (out_gvalue, array);
495 break;
496
497 default:
498 g_value_init (out_gvalue, G_TYPE_VARIANT);
499 g_value_set_variant (out_gvalue, value);
500 break;
501 }
502 break;
503
504 default:
505 g_value_init (out_gvalue, G_TYPE_VARIANT);
506 g_value_set_variant (out_gvalue, value);
507 break;
508 }
509 break;
510
511 case G_VARIANT_CLASS_HANDLE:
512 case G_VARIANT_CLASS_VARIANT:
513 case G_VARIANT_CLASS_MAYBE:
514 case G_VARIANT_CLASS_TUPLE:
515 case G_VARIANT_CLASS_DICT_ENTRY:
516 g_value_init (out_gvalue, G_TYPE_VARIANT);
517 g_value_set_variant (out_gvalue, value);
518 break;
519 }
520 }
521
522
523 /**
524 * g_dbus_gvalue_to_gvariant:
525 * @gvalue: A #GValue to convert to a #GVariant
526 * @type: A #GVariantType
527 *
528 * Converts a #GValue to a #GVariant of the type indicated by the @type
529 * parameter.
530 *
531 * The conversion is using the following rules:
532 *
533 * - `G_TYPE_STRING`: 's', 'o', 'g' or 'ay'
534 * - `G_TYPE_STRV`: 'as', 'ao' or 'aay'
535 * - `G_TYPE_BOOLEAN`: 'b'
536 * - `G_TYPE_UCHAR`: 'y'
537 * - `G_TYPE_INT`: 'i', 'n'
538 * - `G_TYPE_UINT`: 'u', 'q'
539 * - `G_TYPE_INT64`: 'x'
540 * - `G_TYPE_UINT64`: 't'
541 * - `G_TYPE_DOUBLE`: 'd'
542 * - `G_TYPE_VARIANT`: Any #GVariantType
543 *
544 * This can fail if e.g. @gvalue is of type %G_TYPE_STRING and @type
545 * is 'i', i.e. %G_VARIANT_TYPE_INT32. It will also fail for any #GType
546 * (including e.g. %G_TYPE_OBJECT and %G_TYPE_BOXED derived-types) not
547 * in the table above.
548 *
549 * Note that if @gvalue is of type %G_TYPE_VARIANT and its value is
550 * %NULL, the empty #GVariant instance (never %NULL) for @type is
551 * returned (e.g. 0 for scalar types, the empty string for string types,
552 * '/' for object path types, the empty array for any array type and so on).
553 *
554 * See the g_dbus_gvariant_to_gvalue() function for how to convert a
555 * #GVariant to a #GValue.
556 *
557 * Returns: (transfer full): A #GVariant (never floating) of
558 * #GVariantType @type holding the data from @gvalue or an empty #GVariant
559 * in case of failure. Free with g_variant_unref().
560 *
561 * Since: 2.30
562 */
563 GVariant *
564 g_dbus_gvalue_to_gvariant (const GValue *gvalue,
565 const GVariantType *type)
566 {
567 GVariant *ret;
568 const gchar *s;
569 const gchar * const *as;
570 const gchar *empty_strv[1] = {NULL};
571
572 g_return_val_if_fail (gvalue != NULL, NULL);
573 g_return_val_if_fail (type != NULL, NULL);
574
575 ret = NULL;
576
577 /* @type can easily be e.g. "s" with the GValue holding a GVariant - for example this
578 * can happen when using the org.gtk.GDBus.C.ForceGVariant annotation with the
579 * gdbus-codegen(1) tool.
580 */
581 if (G_VALUE_TYPE (gvalue) == G_TYPE_VARIANT)
582 {
583 ret = g_value_dup_variant (gvalue);
584 }
585 else
586 {
587 switch (g_variant_type_peek_string (type)[0])
588 {
589 case G_VARIANT_CLASS_BOOLEAN:
590 ret = g_variant_ref_sink (g_variant_new_boolean (g_value_get_boolean (gvalue)));
591 break;
592
593 case G_VARIANT_CLASS_BYTE:
594 ret = g_variant_ref_sink (g_variant_new_byte (g_value_get_uchar (gvalue)));
595 break;
596
597 case G_VARIANT_CLASS_INT16:
598 ret = g_variant_ref_sink (g_variant_new_int16 (g_value_get_int (gvalue)));
599 break;
600
601 case G_VARIANT_CLASS_UINT16:
602 ret = g_variant_ref_sink (g_variant_new_uint16 (g_value_get_uint (gvalue)));
603 break;
604
605 case G_VARIANT_CLASS_INT32:
606 ret = g_variant_ref_sink (g_variant_new_int32 (g_value_get_int (gvalue)));
607 break;
608
609 case G_VARIANT_CLASS_UINT32:
610 ret = g_variant_ref_sink (g_variant_new_uint32 (g_value_get_uint (gvalue)));
611 break;
612
613 case G_VARIANT_CLASS_INT64:
614 ret = g_variant_ref_sink (g_variant_new_int64 (g_value_get_int64 (gvalue)));
615 break;
616
617 case G_VARIANT_CLASS_UINT64:
618 ret = g_variant_ref_sink (g_variant_new_uint64 (g_value_get_uint64 (gvalue)));
619 break;
620
621 case G_VARIANT_CLASS_DOUBLE:
622 ret = g_variant_ref_sink (g_variant_new_double (g_value_get_double (gvalue)));
623 break;
624
625 case G_VARIANT_CLASS_STRING:
626 s = g_value_get_string (gvalue);
627 if (s == NULL)
628 s = "";
629 ret = g_variant_ref_sink (g_variant_new_string (s));
630 break;
631
632 case G_VARIANT_CLASS_OBJECT_PATH:
633 s = g_value_get_string (gvalue);
634 if (s == NULL)
635 s = "/";
636 ret = g_variant_ref_sink (g_variant_new_object_path (s));
637 break;
638
639 case G_VARIANT_CLASS_SIGNATURE:
640 s = g_value_get_string (gvalue);
641 if (s == NULL)
642 s = "";
643 ret = g_variant_ref_sink (g_variant_new_signature (s));
644 break;
645
646 case G_VARIANT_CLASS_ARRAY:
647 switch (g_variant_type_peek_string (type)[1])
648 {
649 case G_VARIANT_CLASS_BYTE:
650 s = g_value_get_string (gvalue);
651 if (s == NULL)
652 s = "";
653 ret = g_variant_ref_sink (g_variant_new_bytestring (s));
654 break;
655
656 case G_VARIANT_CLASS_STRING:
657 as = g_value_get_boxed (gvalue);
658 if (as == NULL)
659 as = empty_strv;
660 ret = g_variant_ref_sink (g_variant_new_strv (as, -1));
661 break;
662
663 case G_VARIANT_CLASS_OBJECT_PATH:
664 as = g_value_get_boxed (gvalue);
665 if (as == NULL)
666 as = empty_strv;
667 ret = g_variant_ref_sink (g_variant_new_objv (as, -1));
668 break;
669
670 case G_VARIANT_CLASS_ARRAY:
671 switch (g_variant_type_peek_string (type)[2])
672 {
673 case G_VARIANT_CLASS_BYTE:
674 as = g_value_get_boxed (gvalue);
675 if (as == NULL)
676 as = empty_strv;
677 ret = g_variant_ref_sink (g_variant_new_bytestring_array (as, -1));
678 break;
679
680 default:
681 ret = g_value_dup_variant (gvalue);
682 break;
683 }
684 break;
685
686 default:
687 ret = g_value_dup_variant (gvalue);
688 break;
689 }
690 break;
691
692 case G_VARIANT_CLASS_HANDLE:
693 case G_VARIANT_CLASS_VARIANT:
694 case G_VARIANT_CLASS_MAYBE:
695 case G_VARIANT_CLASS_TUPLE:
696 case G_VARIANT_CLASS_DICT_ENTRY:
697 ret = g_value_dup_variant (gvalue);
698 break;
699 }
700 }
701
702 /* Could be that the GValue is holding a NULL GVariant - in that case,
703 * we return an "empty" GVariant instead of a NULL GVariant
704 */
705 if (ret == NULL)
706 {
707 GVariant *untrusted_empty;
708 untrusted_empty = g_variant_new_from_data (type, NULL, 0, FALSE, NULL, NULL);
709 ret = g_variant_take_ref (g_variant_get_normal_form (untrusted_empty));
710 g_variant_unref (untrusted_empty);
711 }
712
713 g_assert (!g_variant_is_floating (ret));
714
715 return ret;
716 }
717
718 /**
719 * g_dbus_escape_object_path_bytestring:
720 * @bytes: (array zero-terminated=1) (element-type guint8): the string of bytes to escape
721 *
722 * Escapes @bytes for use in a D-Bus object path component.
723 * @bytes is an array of zero or more nonzero bytes in an
724 * unspecified encoding, followed by a single zero byte.
725 *
726 * The escaping method consists of replacing all non-alphanumeric
727 * characters (see g_ascii_isalnum()) with their hexadecimal value
728 * preceded by an underscore (`_`). For example:
729 * `foo.bar.baz` will become `foo_2ebar_2ebaz`.
730 *
731 * This method is appropriate to use when the input is nearly
732 * a valid object path component but is not when your input
733 * is far from being a valid object path component.
734 * Other escaping algorithms are also valid to use with
735 * D-Bus object paths.
736 *
737 * This can be reversed with g_dbus_unescape_object_path().
738 *
739 * Returns: an escaped version of @bytes. Free with g_free().
740 *
741 * Since: 2.68
742 *
743 */
744 gchar *
745 g_dbus_escape_object_path_bytestring (const guint8 *bytes)
746 {
747 GString *escaped;
748 const guint8 *p;
749
750 g_return_val_if_fail (bytes != NULL, NULL);
751
752 if (*bytes == '\0')
753 return g_strdup ("_");
754
755 escaped = g_string_new (NULL);
756 for (p = bytes; *p; p++)
757 {
758 if (g_ascii_isalnum (*p))
759 g_string_append_c (escaped, *p);
760 else
761 g_string_append_printf (escaped, "_%02x", *p);
762 }
763
764 return g_string_free (escaped, FALSE);
765 }
766
767 /**
768 * g_dbus_escape_object_path:
769 * @s: the string to escape
770 *
771 * This is a language binding friendly version of g_dbus_escape_object_path_bytestring().
772 *
773 * Returns: an escaped version of @s. Free with g_free().
774 *
775 * Since: 2.68
776 */
777 gchar *
778 g_dbus_escape_object_path (const gchar *s)
779 {
780 return (gchar *) g_dbus_escape_object_path_bytestring ((const guint8 *) s);
781 }
782
783 /**
784 * g_dbus_unescape_object_path:
785 * @s: the string to unescape
786 *
787 * Unescapes an string that was previously escaped with
788 * g_dbus_escape_object_path(). If the string is in a format that could
789 * not have been returned by g_dbus_escape_object_path(), this function
790 * returns %NULL.
791 *
792 * Encoding alphanumeric characters which do not need to be
793 * encoded is not allowed (e.g `_63` is not valid, the string
794 * should contain `c` instead).
795 *
796 * Returns: (array zero-terminated=1) (element-type guint8) (nullable): an
797 * unescaped version of @s, or %NULL if @s is not a string returned
798 * from g_dbus_escape_object_path(). Free with g_free().
799 *
800 * Since: 2.68
801 */
802 guint8 *
803 g_dbus_unescape_object_path (const gchar *s)
804 {
805 GString *unescaped;
806 const gchar *p;
807
808 g_return_val_if_fail (s != NULL, NULL);
809
810 if (g_str_equal (s, "_"))
811 return (guint8 *) g_strdup ("");
812
813 unescaped = g_string_new (NULL);
814 for (p = s; *p; p++)
815 {
816 gint hi, lo;
817
818 if (g_ascii_isalnum (*p))
819 {
820 g_string_append_c (unescaped, *p);
821 }
822 else if (*p == '_' &&
823 ((hi = g_ascii_xdigit_value (p[1])) >= 0) &&
824 ((lo = g_ascii_xdigit_value (p[2])) >= 0) &&
825 (hi || lo) && /* \0 is not allowed */
826 !g_ascii_isalnum ((hi << 4) | lo)) /* alnums must not be encoded */
827 {
828 g_string_append_c (unescaped, (hi << 4) | lo);
829 p += 2;
830 }
831 else
832 {
833 /* the string was not encoded correctly */
834 g_string_free (unescaped, TRUE);
835 return NULL;
836 }
837 }
838
839 return (guint8 *) g_string_free (unescaped, FALSE);
840 }