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

source code of gtk/subprojects/glib/gio/gdbusutils.c