gkdbus: Attach only needed flags
[platform/upstream/glib.git] / gio / gdbusaddress.c
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 #include <stdio.h>
26 #include <errno.h>
27
28 #include "gioerror.h"
29 #include "gdbusutils.h"
30 #include "gdbusaddress.h"
31 #include "gdbuserror.h"
32 #include "gioenumtypes.h"
33 #include "gnetworkaddress.h"
34 #include "gsocketclient.h"
35 #include "giostream.h"
36 #include "gasyncresult.h"
37 #include "gtask.h"
38 #include "glib-private.h"
39 #include "gdbusprivate.h"
40 #include "gstdio.h"
41
42 #ifdef G_OS_UNIX
43 #include "gkdbus.h"
44 #include <unistd.h>
45 #include <sys/stat.h>
46 #include <sys/types.h>
47 #include <gio/gunixsocketaddress.h>
48 #endif
49
50 #ifdef G_OS_WIN32
51 #include <windows.h>
52 #endif
53
54 #include "glibintl.h"
55
56 /**
57  * SECTION:gdbusaddress
58  * @title: D-Bus Addresses
59  * @short_description: D-Bus connection endpoints
60  * @include: gio/gio.h
61  *
62  * Routines for working with D-Bus addresses. A D-Bus address is a string
63  * like `unix:tmpdir=/tmp/my-app-name`. The exact format of addresses
64  * is explained in detail in the
65  * [D-Bus specification](http://dbus.freedesktop.org/doc/dbus-specification.html#addresses).
66  *
67  * TCP D-Bus connections are supported, but accessing them via a proxy is
68  * currently not supported.
69  */
70
71 static gchar *get_session_address_platform_specific (GError **error);
72 static gchar *get_session_address_dbus_launch       (GError **error);
73
74 /* ---------------------------------------------------------------------------------------------------- */
75
76 /**
77  * g_dbus_is_address:
78  * @string: A string.
79  *
80  * Checks if @string is a
81  * [D-Bus address](https://dbus.freedesktop.org/doc/dbus-specification.html#addresses).
82  *
83  * This doesn't check if @string is actually supported by #GDBusServer
84  * or #GDBusConnection - use g_dbus_is_supported_address() to do more
85  * checks.
86  *
87  * Returns: %TRUE if @string is a valid D-Bus address, %FALSE otherwise.
88  *
89  * Since: 2.26
90  */
91 gboolean
92 g_dbus_is_address (const gchar *string)
93 {
94   guint n;
95   gchar **a;
96   gboolean ret;
97
98   ret = FALSE;
99
100   g_return_val_if_fail (string != NULL, FALSE);
101
102   a = g_strsplit (string, ";", 0);
103   if (a[0] == NULL)
104     goto out;
105
106   for (n = 0; a[n] != NULL; n++)
107     {
108       if (!_g_dbus_address_parse_entry (a[n],
109                                         NULL,
110                                         NULL,
111                                         NULL))
112         goto out;
113     }
114
115   ret = TRUE;
116
117  out:
118   g_strfreev (a);
119   return ret;
120 }
121
122 static gboolean
123 is_valid_unix (const gchar  *address_entry,
124                GHashTable   *key_value_pairs,
125                GError      **error)
126 {
127   gboolean ret;
128   GList *keys;
129   GList *l;
130   const gchar *path;
131   const gchar *dir;
132   const gchar *tmpdir;
133   const gchar *abstract;
134
135   ret = FALSE;
136   keys = NULL;
137   path = NULL;
138   dir = NULL;
139   tmpdir = NULL;
140   abstract = NULL;
141
142   keys = g_hash_table_get_keys (key_value_pairs);
143   for (l = keys; l != NULL; l = l->next)
144     {
145       const gchar *key = l->data;
146       if (g_strcmp0 (key, "path") == 0)
147         path = g_hash_table_lookup (key_value_pairs, key);
148       else if (g_strcmp0 (key, "dir") == 0)
149         dir = g_hash_table_lookup (key_value_pairs, key);
150       else if (g_strcmp0 (key, "tmpdir") == 0)
151         tmpdir = g_hash_table_lookup (key_value_pairs, key);
152       else if (g_strcmp0 (key, "abstract") == 0)
153         abstract = g_hash_table_lookup (key_value_pairs, key);
154       else if (g_strcmp0 (key, "guid") != 0)
155         {
156           g_set_error (error,
157                        G_IO_ERROR,
158                        G_IO_ERROR_INVALID_ARGUMENT,
159                        _("Unsupported key “%s” in address entry “%s”"),
160                        key,
161                        address_entry);
162           goto out;
163         }
164     }
165
166   /* Exactly one key must be set */
167   if ((path != NULL) + (dir != NULL) + (tmpdir != NULL) + (abstract != NULL) > 1)
168     {
169       g_set_error (error,
170              G_IO_ERROR,
171              G_IO_ERROR_INVALID_ARGUMENT,
172              _("Meaningless key/value pair combination in address entry “%s”"),
173              address_entry);
174       goto out;
175     }
176   else if (path == NULL && dir == NULL && tmpdir == NULL && abstract == NULL)
177     {
178       g_set_error (error,
179                    G_IO_ERROR,
180                    G_IO_ERROR_INVALID_ARGUMENT,
181                    _("Address “%s” is invalid (need exactly one of path, dir, tmpdir, or abstract keys)"),
182                    address_entry);
183       goto out;
184     }
185
186   ret = TRUE;
187
188  out:
189   g_list_free (keys);
190
191   return ret;
192 }
193
194 static gboolean
195 is_valid_nonce_tcp (const gchar  *address_entry,
196                     GHashTable   *key_value_pairs,
197                     GError      **error)
198 {
199   gboolean ret;
200   GList *keys;
201   GList *l;
202   const gchar *host;
203   const gchar *port;
204   const gchar *family;
205   const gchar *nonce_file;
206   gint port_num;
207   gchar *endp;
208
209   ret = FALSE;
210   keys = NULL;
211   host = NULL;
212   port = NULL;
213   family = NULL;
214   nonce_file = NULL;
215
216   keys = g_hash_table_get_keys (key_value_pairs);
217   for (l = keys; l != NULL; l = l->next)
218     {
219       const gchar *key = l->data;
220       if (g_strcmp0 (key, "host") == 0)
221         host = g_hash_table_lookup (key_value_pairs, key);
222       else if (g_strcmp0 (key, "port") == 0)
223         port = g_hash_table_lookup (key_value_pairs, key);
224       else if (g_strcmp0 (key, "family") == 0)
225         family = g_hash_table_lookup (key_value_pairs, key);
226       else if (g_strcmp0 (key, "noncefile") == 0)
227         nonce_file = g_hash_table_lookup (key_value_pairs, key);
228       else if (g_strcmp0 (key, "guid") != 0)
229         {
230           g_set_error (error,
231                        G_IO_ERROR,
232                        G_IO_ERROR_INVALID_ARGUMENT,
233                        _("Unsupported key “%s” in address entry “%s”"),
234                        key,
235                        address_entry);
236           goto out;
237         }
238     }
239
240   if (port != NULL)
241     {
242       port_num = strtol (port, &endp, 10);
243       if ((*port == '\0' || *endp != '\0') || port_num < 0 || port_num >= 65536)
244         {
245           g_set_error (error,
246                        G_IO_ERROR,
247                        G_IO_ERROR_INVALID_ARGUMENT,
248                        _("Error in address “%s” — the port attribute is malformed"),
249                        address_entry);
250           goto out;
251         }
252     }
253
254   if (family != NULL && !(g_strcmp0 (family, "ipv4") == 0 || g_strcmp0 (family, "ipv6") == 0))
255     {
256       g_set_error (error,
257                    G_IO_ERROR,
258                    G_IO_ERROR_INVALID_ARGUMENT,
259                    _("Error in address “%s” — the family attribute is malformed"),
260                    address_entry);
261       goto out;
262     }
263
264   if (host != NULL)
265     {
266       /* TODO: validate host */
267     }
268
269   nonce_file = nonce_file; /* To avoid -Wunused-but-set-variable */
270
271   ret= TRUE;
272
273  out:
274   g_list_free (keys);
275
276   return ret;
277 }
278
279 static gboolean
280 is_valid_tcp (const gchar  *address_entry,
281               GHashTable   *key_value_pairs,
282               GError      **error)
283 {
284   gboolean ret;
285   GList *keys;
286   GList *l;
287   const gchar *host;
288   const gchar *port;
289   const gchar *family;
290   gint port_num;
291   gchar *endp;
292
293   ret = FALSE;
294   keys = NULL;
295   host = NULL;
296   port = NULL;
297   family = NULL;
298
299   keys = g_hash_table_get_keys (key_value_pairs);
300   for (l = keys; l != NULL; l = l->next)
301     {
302       const gchar *key = l->data;
303       if (g_strcmp0 (key, "host") == 0)
304         host = g_hash_table_lookup (key_value_pairs, key);
305       else if (g_strcmp0 (key, "port") == 0)
306         port = g_hash_table_lookup (key_value_pairs, key);
307       else if (g_strcmp0 (key, "family") == 0)
308         family = g_hash_table_lookup (key_value_pairs, key);
309       else if (g_strcmp0 (key, "guid") != 0)
310         {
311           g_set_error (error,
312                        G_IO_ERROR,
313                        G_IO_ERROR_INVALID_ARGUMENT,
314                        _("Unsupported key “%s” in address entry “%s”"),
315                        key,
316                        address_entry);
317           goto out;
318         }
319     }
320
321   if (port != NULL)
322     {
323       port_num = strtol (port, &endp, 10);
324       if ((*port == '\0' || *endp != '\0') || port_num < 0 || port_num >= 65536)
325         {
326           g_set_error (error,
327                        G_IO_ERROR,
328                        G_IO_ERROR_INVALID_ARGUMENT,
329                        _("Error in address “%s” — the port attribute is malformed"),
330                        address_entry);
331           goto out;
332         }
333     }
334
335   if (family != NULL && !(g_strcmp0 (family, "ipv4") == 0 || g_strcmp0 (family, "ipv6") == 0))
336     {
337       g_set_error (error,
338                    G_IO_ERROR,
339                    G_IO_ERROR_INVALID_ARGUMENT,
340                    _("Error in address “%s” — the family attribute is malformed"),
341                    address_entry);
342       goto out;
343     }
344
345   if (host != NULL)
346     {
347       /* TODO: validate host */
348     }
349
350   ret= TRUE;
351
352  out:
353   g_list_free (keys);
354
355   return ret;
356 }
357
358 /**
359  * g_dbus_is_supported_address:
360  * @string: A string.
361  * @error: Return location for error or %NULL.
362  *
363  * Like g_dbus_is_address() but also checks if the library supports the
364  * transports in @string and that key/value pairs for each transport
365  * are valid. See the specification of the
366  * [D-Bus address format](https://dbus.freedesktop.org/doc/dbus-specification.html#addresses).
367  *
368  * Returns: %TRUE if @string is a valid D-Bus address that is
369  * supported by this library, %FALSE if @error is set.
370  *
371  * Since: 2.26
372  */
373 gboolean
374 g_dbus_is_supported_address (const gchar  *string,
375                              GError      **error)
376 {
377   guint n;
378   gchar **a;
379   gboolean ret;
380
381   ret = FALSE;
382
383   g_return_val_if_fail (string != NULL, FALSE);
384   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
385
386   a = g_strsplit (string, ";", 0);
387   for (n = 0; a[n] != NULL; n++)
388     {
389       gchar *transport_name;
390       GHashTable *key_value_pairs;
391       gboolean supported;
392
393       if (!_g_dbus_address_parse_entry (a[n],
394                                         &transport_name,
395                                         &key_value_pairs,
396                                         error))
397         goto out;
398
399       supported = FALSE;
400       if (g_strcmp0 (transport_name, "unix") == 0)
401         supported = is_valid_unix (a[n], key_value_pairs, error);
402       else if (g_strcmp0 (transport_name, "tcp") == 0)
403         supported = is_valid_tcp (a[n], key_value_pairs, error);
404       else if (g_strcmp0 (transport_name, "nonce-tcp") == 0)
405         supported = is_valid_nonce_tcp (a[n], key_value_pairs, error);
406       else if (g_strcmp0 (a[n], "autolaunch:") == 0)
407         supported = TRUE;
408       else
409         g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
410                      _("Unknown or unsupported transport “%s” for address “%s”"),
411                      transport_name, a[n]);
412
413       g_free (transport_name);
414       g_hash_table_unref (key_value_pairs);
415
416       if (!supported)
417         goto out;
418     }
419
420   ret = TRUE;
421
422  out:
423   g_strfreev (a);
424
425   g_assert (ret || (!ret && (error == NULL || *error != NULL)));
426
427   return ret;
428 }
429
430 gboolean
431 _g_dbus_address_parse_entry (const gchar  *address_entry,
432                              gchar       **out_transport_name,
433                              GHashTable  **out_key_value_pairs,
434                              GError      **error)
435 {
436   gboolean ret;
437   GHashTable *key_value_pairs;
438   gchar *transport_name;
439   gchar **kv_pairs;
440   const gchar *s;
441   guint n;
442
443   ret = FALSE;
444   kv_pairs = NULL;
445   transport_name = NULL;
446   key_value_pairs = NULL;
447
448   s = strchr (address_entry, ':');
449   if (s == NULL)
450     {
451       g_set_error (error,
452                    G_IO_ERROR,
453                    G_IO_ERROR_INVALID_ARGUMENT,
454                    _("Address element “%s” does not contain a colon (:)"),
455                    address_entry);
456       goto out;
457     }
458   else if (s == address_entry)
459     {
460       g_set_error (error,
461                    G_IO_ERROR,
462                    G_IO_ERROR_INVALID_ARGUMENT,
463                    _("Transport name in address element “%s” must not be empty"),
464                    address_entry);
465       goto out;
466     }
467
468   transport_name = g_strndup (address_entry, s - address_entry);
469   key_value_pairs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
470
471   kv_pairs = g_strsplit (s + 1, ",", 0);
472   for (n = 0; kv_pairs[n] != NULL; n++)
473     {
474       const gchar *kv_pair = kv_pairs[n];
475       gchar *key;
476       gchar *value;
477
478       s = strchr (kv_pair, '=');
479       if (s == NULL)
480         {
481           g_set_error (error,
482                        G_IO_ERROR,
483                        G_IO_ERROR_INVALID_ARGUMENT,
484                        _("Key/Value pair %d, “%s”, in address element “%s” does not contain an equal sign"),
485                        n,
486                        kv_pair,
487                        address_entry);
488           goto out;
489         }
490       else if (s == kv_pair)
491         {
492           g_set_error (error,
493                        G_IO_ERROR,
494                        G_IO_ERROR_INVALID_ARGUMENT,
495                        _("Key/Value pair %d, “%s”, in address element “%s” must not have an empty key"),
496                        n,
497                        kv_pair,
498                        address_entry);
499           goto out;
500         }
501
502       key = g_uri_unescape_segment (kv_pair, s, NULL);
503       value = g_uri_unescape_segment (s + 1, kv_pair + strlen (kv_pair), NULL);
504       if (key == NULL || value == NULL)
505         {
506           g_set_error (error,
507                        G_IO_ERROR,
508                        G_IO_ERROR_INVALID_ARGUMENT,
509                        _("Error unescaping key or value in Key/Value pair %d, “%s”, in address element “%s”"),
510                        n,
511                        kv_pair,
512                        address_entry);
513           g_free (key);
514           g_free (value);
515           goto out;
516         }
517       g_hash_table_insert (key_value_pairs, key, value);
518     }
519
520   ret = TRUE;
521
522 out:
523   if (ret)
524     {
525       if (out_transport_name != NULL)
526         *out_transport_name = g_steal_pointer (&transport_name);
527       if (out_key_value_pairs != NULL)
528         *out_key_value_pairs = g_steal_pointer (&key_value_pairs);
529     }
530
531   g_clear_pointer (&key_value_pairs, g_hash_table_unref);
532   g_free (transport_name);
533   g_strfreev (kv_pairs);
534
535   return ret;
536 }
537
538 /* ---------------------------------------------------------------------------------------------------- */
539
540 static GObject *
541 g_dbus_address_try_connect_one (const gchar   *address_entry,
542                                 gboolean       kdbus_okay,
543                                 gchar        **out_guid,
544                                 GCancellable  *cancellable,
545                                 GError       **error);
546
547 /* TODO: Declare an extension point called GDBusTransport (or similar)
548  * and move code below to extensions implementing said extension
549  * point. That way we can implement a D-Bus transport over X11 without
550  * making libgio link to libX11...
551  */
552 static GObject *
553 g_dbus_address_connect (const gchar   *address_entry,
554                         const gchar   *transport_name,
555                         gboolean       kdbus_okay,
556                         GHashTable    *key_value_pairs,
557                         GCancellable  *cancellable,
558                         GError       **error)
559 {
560   GObject *ret;
561   GSocketConnectable *connectable;
562   const gchar *nonce_file;
563
564   connectable = NULL;
565   ret = NULL;
566   nonce_file = NULL;
567
568   if (FALSE)
569     {
570     }
571 #ifdef G_OS_UNIX
572 #ifdef KDBUS
573   else if (kdbus_okay && g_str_equal (transport_name, "kernel"))
574     {
575       GKDBusWorker *worker;
576       const gchar *path;
577
578       path = g_hash_table_lookup (key_value_pairs, "path");
579
580       if (path == NULL)
581         {
582           g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
583                        _("Error in address '%s' - the kernel transport requires a path"),
584                        address_entry);
585         }
586       else
587         {
588           worker = _g_kdbus_worker_new (path, error);
589
590           if (worker == NULL)
591             return NULL;
592
593           return G_OBJECT (worker);
594         }
595     }
596 #endif
597   else if (g_strcmp0 (transport_name, "unix") == 0)
598     {
599       const gchar *path;
600       const gchar *abstract;
601       path = g_hash_table_lookup (key_value_pairs, "path");
602       abstract = g_hash_table_lookup (key_value_pairs, "abstract");
603       if ((path == NULL && abstract == NULL) || (path != NULL && abstract != NULL))
604         {
605           g_set_error (error,
606                        G_IO_ERROR,
607                        G_IO_ERROR_INVALID_ARGUMENT,
608                        _("Error in address “%s” — the unix transport requires exactly one of the "
609                          "keys “path” or “abstract” to be set"),
610                        address_entry);
611         }
612       else if (path != NULL)
613         {
614           connectable = G_SOCKET_CONNECTABLE (g_unix_socket_address_new (path));
615         }
616       else if (abstract != NULL)
617         {
618           connectable = G_SOCKET_CONNECTABLE (g_unix_socket_address_new_with_type (abstract,
619                                                                                    -1,
620                                                                                    G_UNIX_SOCKET_ADDRESS_ABSTRACT));
621         }
622       else
623         {
624           g_assert_not_reached ();
625         }
626     }
627 #endif
628   else if (g_strcmp0 (transport_name, "tcp") == 0 || g_strcmp0 (transport_name, "nonce-tcp") == 0)
629     {
630       const gchar *s;
631       const gchar *host;
632       glong port;
633       gchar *endp;
634       gboolean is_nonce;
635
636       is_nonce = (g_strcmp0 (transport_name, "nonce-tcp") == 0);
637
638       host = g_hash_table_lookup (key_value_pairs, "host");
639       if (host == NULL)
640         {
641           g_set_error (error,
642                        G_IO_ERROR,
643                        G_IO_ERROR_INVALID_ARGUMENT,
644                        _("Error in address “%s” — the host attribute is missing or malformed"),
645                        address_entry);
646           goto out;
647         }
648
649       s = g_hash_table_lookup (key_value_pairs, "port");
650       if (s == NULL)
651         s = "0";
652       port = strtol (s, &endp, 10);
653       if ((*s == '\0' || *endp != '\0') || port < 0 || port >= 65536)
654         {
655           g_set_error (error,
656                        G_IO_ERROR,
657                        G_IO_ERROR_INVALID_ARGUMENT,
658                        _("Error in address “%s” — the port attribute is missing or malformed"),
659                        address_entry);
660           goto out;
661         }
662
663
664       if (is_nonce)
665         {
666           nonce_file = g_hash_table_lookup (key_value_pairs, "noncefile");
667           if (nonce_file == NULL)
668             {
669               g_set_error (error,
670                            G_IO_ERROR,
671                            G_IO_ERROR_INVALID_ARGUMENT,
672                            _("Error in address “%s” — the noncefile attribute is missing or malformed"),
673                            address_entry);
674               goto out;
675             }
676         }
677
678       /* TODO: deal with family key/value-pair */
679       connectable = g_network_address_new (host, port);
680     }
681   else if (g_strcmp0 (address_entry, "autolaunch:") == 0)
682     {
683       gchar *autolaunch_address;
684       autolaunch_address = get_session_address_dbus_launch (error);
685       if (autolaunch_address != NULL)
686         {
687           ret = g_dbus_address_try_connect_one (autolaunch_address, kdbus_okay, NULL, cancellable, error);
688           g_free (autolaunch_address);
689           goto out;
690         }
691       else
692         {
693           g_prefix_error (error, _("Error auto-launching: "));
694         }
695     }
696   else
697     {
698       g_set_error (error,
699                    G_IO_ERROR,
700                    G_IO_ERROR_INVALID_ARGUMENT,
701                    _("Unknown or unsupported transport “%s” for address “%s”"),
702                    transport_name,
703                    address_entry);
704     }
705
706   if (connectable != NULL)
707     {
708       GSocketClient *client;
709       GSocketConnection *connection;
710
711       g_assert (ret == NULL);
712       client = g_socket_client_new ();
713
714       /* Disable proxy support to prevent a deadlock on startup, since loading a
715        * proxy resolver causes the GIO modules to be loaded, and there will
716        * almost certainly be one of them which then tries to use GDBus.
717        * See: https://bugzilla.gnome.org/show_bug.cgi?id=792499 */
718       g_socket_client_set_enable_proxy (client, FALSE);
719
720       connection = g_socket_client_connect (client,
721                                             connectable,
722                                             cancellable,
723                                             error);
724       g_object_unref (connectable);
725       g_object_unref (client);
726       if (connection == NULL)
727         goto out;
728
729       ret = G_OBJECT (connection);
730
731       if (nonce_file != NULL)
732         {
733           gchar nonce_contents[16 + 1];
734           size_t num_bytes_read;
735           FILE *f;
736           int errsv;
737
738           /* be careful to read only 16 bytes - we also check that the file is only 16 bytes long */
739           f = fopen (nonce_file, "rb");
740           errsv = errno;
741           if (f == NULL)
742             {
743               g_set_error (error,
744                            G_IO_ERROR,
745                            G_IO_ERROR_INVALID_ARGUMENT,
746                            _("Error opening nonce file “%s”: %s"),
747                            nonce_file,
748                            g_strerror (errsv));
749               g_object_unref (ret);
750               ret = NULL;
751               goto out;
752             }
753           num_bytes_read = fread (nonce_contents,
754                                   sizeof (gchar),
755                                   16 + 1,
756                                   f);
757           errsv = errno;
758           if (num_bytes_read != 16)
759             {
760               if (num_bytes_read == 0)
761                 {
762                   g_set_error (error,
763                                G_IO_ERROR,
764                                G_IO_ERROR_INVALID_ARGUMENT,
765                                _("Error reading from nonce file “%s”: %s"),
766                                nonce_file,
767                                g_strerror (errsv));
768                 }
769               else
770                 {
771                   g_set_error (error,
772                                G_IO_ERROR,
773                                G_IO_ERROR_INVALID_ARGUMENT,
774                                _("Error reading from nonce file “%s”, expected 16 bytes, got %d"),
775                                nonce_file,
776                                (gint) num_bytes_read);
777                 }
778               g_object_unref (ret);
779               ret = NULL;
780               fclose (f);
781               goto out;
782             }
783           fclose (f);
784
785           if (!g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (connection)),
786                                           nonce_contents,
787                                           16,
788                                           NULL,
789                                           cancellable,
790                                           error))
791             {
792               g_prefix_error (error, _("Error writing contents of nonce file “%s” to stream:"), nonce_file);
793               g_object_unref (ret);
794               ret = NULL;
795               goto out;
796             }
797         }
798     }
799
800  out:
801
802   return ret;
803 }
804
805 static GObject *
806 g_dbus_address_try_connect_one (const gchar   *address_entry,
807                                 gboolean       kdbus_okay,
808                                 gchar        **out_guid,
809                                 GCancellable  *cancellable,
810                                 GError       **error)
811 {
812   GObject *ret;
813   GHashTable *key_value_pairs;
814   gchar *transport_name;
815   const gchar *guid;
816
817   ret = NULL;
818   transport_name = NULL;
819   key_value_pairs = NULL;
820
821   if (!_g_dbus_address_parse_entry (address_entry,
822                                     &transport_name,
823                                     &key_value_pairs,
824                                     error))
825     goto out;
826
827   ret = g_dbus_address_connect (address_entry,
828                                 transport_name,
829                                 kdbus_okay,
830                                 key_value_pairs,
831                                 cancellable,
832                                 error);
833   if (ret == NULL)
834     goto out;
835
836   guid = g_hash_table_lookup (key_value_pairs, "guid");
837   if (guid != NULL && out_guid != NULL)
838     *out_guid = g_strdup (guid);
839
840 out:
841   g_free (transport_name);
842   if (key_value_pairs != NULL)
843     g_hash_table_unref (key_value_pairs);
844   return ret;
845 }
846
847
848 /* ---------------------------------------------------------------------------------------------------- */
849
850 typedef struct {
851   gchar *address;
852   gchar *guid;
853 } GetStreamData;
854
855 static void
856 get_stream_data_free (GetStreamData *data)
857 {
858   g_free (data->address);
859   g_free (data->guid);
860   g_free (data);
861 }
862
863 static void
864 get_stream_thread_func (GTask         *task,
865                         gpointer       source_object,
866                         gpointer       task_data,
867                         GCancellable  *cancellable)
868 {
869   GetStreamData *data = task_data;
870   GIOStream *stream;
871   GError *error = NULL;
872
873   stream = g_dbus_address_get_stream_sync (data->address,
874                                            &data->guid,
875                                            cancellable,
876                                            &error);
877   if (stream)
878     g_task_return_pointer (task, stream, g_object_unref);
879   else
880     g_task_return_error (task, error);
881 }
882
883 /**
884  * g_dbus_address_get_stream:
885  * @address: A valid D-Bus address.
886  * @cancellable: (nullable): A #GCancellable or %NULL.
887  * @callback: A #GAsyncReadyCallback to call when the request is satisfied.
888  * @user_data: Data to pass to @callback.
889  *
890  * Asynchronously connects to an endpoint specified by @address and
891  * sets up the connection so it is in a state to run the client-side
892  * of the D-Bus authentication conversation. @address must be in the
893  * [D-Bus address format](https://dbus.freedesktop.org/doc/dbus-specification.html#addresses).
894  *
895  * When the operation is finished, @callback will be invoked. You can
896  * then call g_dbus_address_get_stream_finish() to get the result of
897  * the operation.
898  *
899  * This is an asynchronous failable function. See
900  * g_dbus_address_get_stream_sync() for the synchronous version.
901  *
902  * Since: 2.26
903  */
904 void
905 g_dbus_address_get_stream (const gchar         *address,
906                            GCancellable        *cancellable,
907                            GAsyncReadyCallback  callback,
908                            gpointer             user_data)
909 {
910   GTask *task;
911   GetStreamData *data;
912
913   g_return_if_fail (address != NULL);
914
915   data = g_new0 (GetStreamData, 1);
916   data->address = g_strdup (address);
917
918   task = g_task_new (NULL, cancellable, callback, user_data);
919   g_task_set_source_tag (task, g_dbus_address_get_stream);
920   g_task_set_task_data (task, data, (GDestroyNotify) get_stream_data_free);
921   g_task_run_in_thread (task, get_stream_thread_func);
922   g_object_unref (task);
923 }
924
925 /**
926  * g_dbus_address_get_stream_finish:
927  * @res: A #GAsyncResult obtained from the GAsyncReadyCallback passed to g_dbus_address_get_stream().
928  * @out_guid: (optional) (out): %NULL or return location to store the GUID extracted from @address, if any.
929  * @error: Return location for error or %NULL.
930  *
931  * Finishes an operation started with g_dbus_address_get_stream().
932  *
933  * Returns: (transfer full): A #GIOStream or %NULL if @error is set.
934  *
935  * Since: 2.26
936  */
937 GIOStream *
938 g_dbus_address_get_stream_finish (GAsyncResult        *res,
939                                   gchar              **out_guid,
940                                   GError             **error)
941 {
942   GTask *task;
943   GetStreamData *data;
944   GIOStream *ret;
945
946   g_return_val_if_fail (g_task_is_valid (res, NULL), NULL);
947   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
948
949   task = G_TASK (res);
950   ret = g_task_propagate_pointer (task, error);
951
952   if (ret != NULL && out_guid != NULL)
953     {
954       data = g_task_get_task_data (task);
955       *out_guid = data->guid;
956       data->guid = NULL;
957     }
958
959   return ret;
960 }
961
962 /**
963  * g_dbus_address_get_stream_sync:
964  * @address: A valid D-Bus address.
965  * @out_guid: (optional) (out): %NULL or return location to store the GUID extracted from @address, if any.
966  * @cancellable: (nullable): A #GCancellable or %NULL.
967  * @error: Return location for error or %NULL.
968  *
969  * Synchronously connects to an endpoint specified by @address and
970  * sets up the connection so it is in a state to run the client-side
971  * of the D-Bus authentication conversation. @address must be in the
972  * [D-Bus address format](https://dbus.freedesktop.org/doc/dbus-specification.html#addresses).
973  *
974  * This is a synchronous failable function. See
975  * g_dbus_address_get_stream() for the asynchronous version.
976  *
977  * Returns: (transfer full): A #GIOStream or %NULL if @error is set.
978  *
979  * Since: 2.26
980  */
981 GIOStream *
982 g_dbus_address_get_stream_sync (const gchar   *address,
983                                 gchar        **out_guid,
984                                 GCancellable  *cancellable,
985                                 GError       **error)
986 {
987   GObject *result;
988
989   result = g_dbus_address_get_stream_internal (address, FALSE, out_guid, cancellable, error);
990   g_assert (result == NULL || G_IS_IO_STREAM (result));
991
992   if (result)
993     return G_IO_STREAM (result);
994
995   return NULL;
996 }
997
998 GObject *
999 g_dbus_address_get_stream_internal (const gchar   *address,
1000                                     gboolean       kdbus_okay,
1001                                     gchar        **out_guid,
1002                                     GCancellable  *cancellable,
1003                                     GError       **error)
1004 {
1005   GObject *ret;
1006   gchar **addr_array;
1007   guint n;
1008   GError *last_error;
1009
1010   g_return_val_if_fail (address != NULL, NULL);
1011   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1012
1013   ret = NULL;
1014   last_error = NULL;
1015
1016   addr_array = g_strsplit (address, ";", 0);
1017   if (addr_array[0] == NULL)
1018     {
1019       last_error = g_error_new_literal (G_IO_ERROR,
1020                                         G_IO_ERROR_INVALID_ARGUMENT,
1021                                         _("The given address is empty"));
1022       goto out;
1023     }
1024
1025   for (n = 0; addr_array != NULL && addr_array[n] != NULL; n++)
1026     {
1027       const gchar *addr = addr_array[n];
1028       GError *this_error;
1029
1030       this_error = NULL;
1031       ret = g_dbus_address_try_connect_one (addr,
1032                                             kdbus_okay,
1033                                             out_guid,
1034                                             cancellable,
1035                                             &this_error);
1036       if (ret != NULL)
1037         {
1038           goto out;
1039         }
1040       else
1041         {
1042           g_assert (this_error != NULL);
1043           if (last_error != NULL)
1044             g_error_free (last_error);
1045           last_error = this_error;
1046         }
1047     }
1048
1049  out:
1050   if (ret != NULL)
1051     {
1052       if (last_error != NULL)
1053         g_error_free (last_error);
1054     }
1055   else
1056     {
1057       g_assert (last_error != NULL);
1058       g_propagate_error (error, last_error);
1059     }
1060
1061   g_strfreev (addr_array);
1062   return ret;
1063 }
1064
1065 /* ---------------------------------------------------------------------------------------------------- */
1066
1067 /*
1068  * Return the address of XDG_RUNTIME_DIR/bus if it exists, belongs to
1069  * us, and is a socket, and we are on Unix.
1070  */
1071 static gchar *
1072 get_session_address_xdg (void)
1073 {
1074 #ifdef G_OS_UNIX
1075   gchar *ret = NULL;
1076   gchar *bus;
1077   gchar *tmp;
1078   GStatBuf buf;
1079
1080   bus = g_build_filename (g_get_user_runtime_dir (), "bus", NULL);
1081
1082   /* if ENOENT, EPERM, etc., quietly don't use it */
1083   if (g_stat (bus, &buf) < 0)
1084     goto out;
1085
1086   /* if it isn't ours, we have incorrectly inherited someone else's
1087    * XDG_RUNTIME_DIR; silently don't use it
1088    */
1089   if (buf.st_uid != geteuid ())
1090     goto out;
1091
1092   /* if it isn't a socket, silently don't use it */
1093   if ((buf.st_mode & S_IFMT) != S_IFSOCK)
1094     goto out;
1095
1096   tmp = g_dbus_address_escape_value (bus);
1097   ret = g_strconcat ("unix:path=", tmp, NULL);
1098   g_free (tmp);
1099
1100 out:
1101   g_free (bus);
1102   return ret;
1103 #else
1104   return NULL;
1105 #endif
1106 }
1107
1108 static gchar *
1109 get_session_address_kdbus (void)
1110 {
1111 #ifdef G_OS_UNIX
1112   gchar *ret = NULL;
1113   gchar *bus;
1114   GStatBuf buf;
1115
1116   bus = g_strdup_printf ("/sys/fs/kdbus/%d-user/bus", getuid());
1117
1118   /* if ENOENT, EPERM, etc., quietly don't use it */
1119   if (g_stat (bus, &buf) < 0)
1120     goto out;
1121
1122   ret = g_strconcat ("kernel:path=", bus, NULL);
1123
1124 out:
1125   g_free (bus);
1126   return ret;
1127 #else
1128   return NULL;
1129 #endif
1130 }
1131
1132 /* ---------------------------------------------------------------------------------------------------- */
1133
1134 #ifdef G_OS_UNIX
1135 static gchar *
1136 get_session_address_dbus_launch (GError **error)
1137 {
1138   gchar *ret;
1139   gchar *machine_id;
1140   gchar *command_line;
1141   gchar *launch_stdout;
1142   gchar *launch_stderr;
1143   gint exit_status;
1144   gchar *old_dbus_verbose;
1145   gboolean restore_dbus_verbose;
1146
1147   ret = NULL;
1148   machine_id = NULL;
1149   command_line = NULL;
1150   launch_stdout = NULL;
1151   launch_stderr = NULL;
1152   restore_dbus_verbose = FALSE;
1153   old_dbus_verbose = NULL;
1154
1155   /* Don't run binaries as root if we're setuid. */
1156   if (GLIB_PRIVATE_CALL (g_check_setuid) ())
1157     {
1158       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1159                    _("Cannot spawn a message bus when setuid"));
1160       goto out;
1161     }
1162
1163   machine_id = _g_dbus_get_machine_id (error);
1164   if (machine_id == NULL)
1165     {
1166       g_prefix_error (error, _("Cannot spawn a message bus without a machine-id: "));
1167       goto out;
1168     }
1169
1170   if (g_getenv ("DISPLAY") == NULL)
1171     {
1172       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1173                    _("Cannot autolaunch D-Bus without X11 $DISPLAY"));
1174       goto out;
1175     }
1176
1177   /* We're using private libdbus facilities here. When everything
1178    * (X11, Mac OS X, Windows) is spec'ed out correctly (not even the
1179    * X11 property is correctly documented right now) we should
1180    * consider using the spec instead of dbus-launch.
1181    *
1182    *   --autolaunch=MACHINEID
1183    *          This option implies that dbus-launch should scan  for  a  previ‐
1184    *          ously-started  session  and  reuse the values found there. If no
1185    *          session is found, it will start a new session. The  --exit-with-
1186    *          session option is implied if --autolaunch is given.  This option
1187    *          is for the exclusive use of libdbus, you do not want to  use  it
1188    *          manually. It may change in the future.
1189    */
1190
1191   /* TODO: maybe provide a variable for where to look for the dbus-launch binary? */
1192   command_line = g_strdup_printf ("dbus-launch --autolaunch=%s --binary-syntax --close-stderr", machine_id);
1193
1194   if (G_UNLIKELY (_g_dbus_debug_address ()))
1195     {
1196       _g_dbus_debug_print_lock ();
1197       g_print ("GDBus-debug:Address: Running '%s' to get bus address (possibly autolaunching)\n", command_line);
1198       old_dbus_verbose = g_strdup (g_getenv ("DBUS_VERBOSE"));
1199       restore_dbus_verbose = TRUE;
1200       g_setenv ("DBUS_VERBOSE", "1", TRUE);
1201       _g_dbus_debug_print_unlock ();
1202     }
1203
1204   if (!g_spawn_command_line_sync (command_line,
1205                                   &launch_stdout,
1206                                   &launch_stderr,
1207                                   &exit_status,
1208                                   error))
1209     {
1210       goto out;
1211     }
1212
1213   if (!g_spawn_check_exit_status (exit_status, error))
1214     {
1215       g_prefix_error (error, _("Error spawning command line “%s”: "), command_line);
1216       goto out;
1217     }
1218
1219   /* From the dbus-launch(1) man page:
1220    *
1221    *   --binary-syntax Write to stdout a nul-terminated bus address,
1222    *   then the bus PID as a binary integer of size sizeof(pid_t),
1223    *   then the bus X window ID as a binary integer of size
1224    *   sizeof(long).  Integers are in the machine's byte order, not
1225    *   network byte order or any other canonical byte order.
1226    */
1227   ret = g_strdup (launch_stdout);
1228
1229  out:
1230   if (G_UNLIKELY (_g_dbus_debug_address ()))
1231     {
1232       gchar *s;
1233       _g_dbus_debug_print_lock ();
1234       g_print ("GDBus-debug:Address: dbus-launch output:");
1235       if (launch_stdout != NULL)
1236         {
1237           s = _g_dbus_hexdump (launch_stdout, strlen (launch_stdout) + 1 + sizeof (pid_t) + sizeof (long), 2);
1238           g_print ("\n%s", s);
1239           g_free (s);
1240         }
1241       else
1242         {
1243           g_print (" (none)\n");
1244         }
1245       g_print ("GDBus-debug:Address: dbus-launch stderr output:");
1246       if (launch_stderr != NULL)
1247         g_print ("\n%s", launch_stderr);
1248       else
1249         g_print (" (none)\n");
1250       _g_dbus_debug_print_unlock ();
1251     }
1252
1253   g_free (machine_id);
1254   g_free (command_line);
1255   g_free (launch_stdout);
1256   g_free (launch_stderr);
1257   if (G_UNLIKELY (restore_dbus_verbose))
1258     {
1259       if (old_dbus_verbose != NULL)
1260         g_setenv ("DBUS_VERBOSE", old_dbus_verbose, TRUE);
1261       else
1262         g_unsetenv ("DBUS_VERBOSE");
1263     }
1264   g_free (old_dbus_verbose);
1265   return ret;
1266 }
1267
1268 /* end of G_OS_UNIX case */
1269 #elif defined(G_OS_WIN32)
1270
1271 static gchar *
1272 get_session_address_dbus_launch (GError **error)
1273 {
1274   return _g_dbus_win32_get_session_address_dbus_launch (error);
1275 }
1276
1277 #else /* neither G_OS_UNIX nor G_OS_WIN32 */
1278 static gchar *
1279 get_session_address_dbus_launch (GError **error)
1280 {
1281   g_set_error (error,
1282                G_IO_ERROR,
1283                G_IO_ERROR_FAILED,
1284                _("Cannot determine session bus address (not implemented for this OS)"));
1285   return NULL;
1286 }
1287 #endif /* neither G_OS_UNIX nor G_OS_WIN32 */
1288
1289 /* ---------------------------------------------------------------------------------------------------- */
1290
1291 static gchar *
1292 get_session_address_platform_specific (GError **error)
1293 {
1294   gchar *ret;
1295
1296   /* Use XDG_RUNTIME_DIR/bus if it exists and is suitable. This is appropriate
1297    * for systems using the "a session is a user-session" model described in
1298    * <http://lists.freedesktop.org/archives/dbus/2015-January/016522.html>,
1299    * and implemented in dbus >= 1.9.14 and sd-bus.
1300    *
1301    * On systems following the more traditional "a session is a login-session"
1302    * model, this will fail and we'll fall through to X11 autolaunching
1303    * (dbus-launch) below.
1304    */
1305   ret = get_session_address_xdg ();
1306
1307   if (ret != NULL)
1308     return ret;
1309
1310   /* TODO (#694472): try launchd on OS X, like
1311    * _dbus_lookup_session_address_launchd() does, since
1312    * 'dbus-launch --autolaunch' probably won't work there
1313    */
1314
1315   /* As a last resort, try the "autolaunch:" transport. On Unix this means
1316    * X11 autolaunching; on Windows this means a different autolaunching
1317    * mechanism based on shared memory.
1318    */
1319   return get_session_address_dbus_launch (error);
1320 }
1321
1322 /* ---------------------------------------------------------------------------------------------------- */
1323
1324 /**
1325  * g_dbus_address_get_for_bus_sync:
1326  * @bus_type: a #GBusType
1327  * @cancellable: (nullable): a #GCancellable or %NULL
1328  * @error: return location for error or %NULL
1329  *
1330  * Synchronously looks up the D-Bus address for the well-known message
1331  * bus instance specified by @bus_type. This may involve using various
1332  * platform specific mechanisms.
1333  *
1334  * The returned address will be in the
1335  * [D-Bus address format](https://dbus.freedesktop.org/doc/dbus-specification.html#addresses).
1336  *
1337  * Returns: (transfer full): a valid D-Bus address string for @bus_type or
1338  *     %NULL if @error is set
1339  *
1340  * Since: 2.26
1341  */
1342 gchar *
1343 g_dbus_address_get_for_bus_sync (GBusType       bus_type,
1344                                  GCancellable  *cancellable,
1345                                  GError       **error)
1346 {
1347   gchar *ret, *s = NULL;
1348   const gchar *starter_bus;
1349   GError *local_error;
1350
1351   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1352
1353   ret = NULL;
1354   local_error = NULL;
1355
1356   if (G_UNLIKELY (_g_dbus_debug_address ()))
1357     {
1358       guint n;
1359       gchar *s;
1360       _g_dbus_debug_print_lock ();
1361       s = _g_dbus_enum_to_string (G_TYPE_BUS_TYPE, bus_type);
1362       g_print ("GDBus-debug:Address: In g_dbus_address_get_for_bus_sync() for bus type '%s'\n",
1363                s);
1364       g_free (s);
1365       for (n = 0; n < 3; n++)
1366         {
1367           const gchar *k;
1368           const gchar *v;
1369           switch (n)
1370             {
1371             case 0: k = "DBUS_SESSION_BUS_ADDRESS"; break;
1372             case 1: k = "DBUS_SYSTEM_BUS_ADDRESS"; break;
1373             case 2: k = "DBUS_STARTER_BUS_TYPE"; break;
1374             default: g_assert_not_reached ();
1375             }
1376           v = g_getenv (k);
1377           g_print ("GDBus-debug:Address: env var %s", k);
1378           if (v != NULL)
1379             g_print ("='%s'\n", v);
1380           else
1381             g_print (" is not set\n");
1382         }
1383       _g_dbus_debug_print_unlock ();
1384     }
1385
1386   switch (bus_type)
1387     {
1388     case G_BUS_TYPE_SYSTEM:
1389       ret = g_strdup (g_getenv ("DBUS_SYSTEM_BUS_ADDRESS"));
1390       if (ret == NULL)
1391         {
1392           ret = g_strdup ("kernel:path=/sys/fs/kdbus/0-system/bus;unix:path=/var/run/dbus/system_bus_socket");
1393         }
1394       break;
1395
1396     case G_BUS_TYPE_SESSION:
1397       ret = g_strdup (g_getenv ("DBUS_SESSION_BUS_ADDRESS"));
1398       if (ret == NULL)
1399         {
1400           ret = get_session_address_kdbus ();
1401           if (ret == NULL)
1402             ret = get_session_address_platform_specific (&local_error);
1403           if (ret == NULL)
1404             goto out;
1405         }
1406       break;
1407
1408     case G_BUS_TYPE_STARTER:
1409       starter_bus = g_getenv ("DBUS_STARTER_BUS_TYPE");
1410       if (g_strcmp0 (starter_bus, "session") == 0)
1411         {
1412           ret = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SESSION, cancellable, &local_error);
1413           goto out;
1414         }
1415       else if (g_strcmp0 (starter_bus, "system") == 0)
1416         {
1417           ret = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SYSTEM, cancellable, &local_error);
1418           goto out;
1419         }
1420       else
1421         {
1422           if (starter_bus != NULL)
1423             {
1424               g_set_error (&local_error,
1425                            G_IO_ERROR,
1426                            G_IO_ERROR_FAILED,
1427                            _("Cannot determine bus address from DBUS_STARTER_BUS_TYPE environment variable"
1428                              " — unknown value “%s”"),
1429                            starter_bus);
1430             }
1431           else
1432             {
1433               g_set_error_literal (&local_error,
1434                                    G_IO_ERROR,
1435                                    G_IO_ERROR_FAILED,
1436                                    _("Cannot determine bus address because the DBUS_STARTER_BUS_TYPE environment "
1437                                      "variable is not set"));
1438             }
1439         }
1440       break;
1441
1442     default:
1443       g_set_error (&local_error,
1444                    G_IO_ERROR,
1445                    G_IO_ERROR_FAILED,
1446                    _("Unknown bus type %d"),
1447                    bus_type);
1448       break;
1449     }
1450
1451  out:
1452   if (G_UNLIKELY (_g_dbus_debug_address ()))
1453     {
1454       _g_dbus_debug_print_lock ();
1455       s = _g_dbus_enum_to_string (G_TYPE_BUS_TYPE, bus_type);
1456       if (ret != NULL)
1457         {
1458           g_print ("GDBus-debug:Address: Returning address '%s' for bus type '%s'\n",
1459                    ret, s);
1460         }
1461       else
1462         {
1463           g_print ("GDBus-debug:Address: Cannot look-up address bus type '%s': %s\n",
1464                    s, local_error ? local_error->message : "");
1465         }
1466       g_free (s);
1467       _g_dbus_debug_print_unlock ();
1468     }
1469
1470   if (local_error != NULL)
1471     g_propagate_error (error, local_error);
1472
1473   return ret;
1474 }
1475
1476 /**
1477  * g_dbus_address_escape_value:
1478  * @string: an unescaped string to be included in a D-Bus address
1479  *     as the value in a key-value pair
1480  *
1481  * Escape @string so it can appear in a D-Bus address as the value
1482  * part of a key-value pair.
1483  *
1484  * For instance, if @string is `/run/bus-for-:0`,
1485  * this function would return `/run/bus-for-%3A0`,
1486  * which could be used in a D-Bus address like
1487  * `unix:nonce-tcp:host=127.0.0.1,port=42,noncefile=/run/bus-for-%3A0`.
1488  *
1489  * Returns: (transfer full): a copy of @string with all
1490  *     non-optionally-escaped bytes escaped
1491  *
1492  * Since: 2.36
1493  */
1494 gchar *
1495 g_dbus_address_escape_value (const gchar *string)
1496 {
1497   GString *s;
1498   gsize i;
1499
1500   g_return_val_if_fail (string != NULL, NULL);
1501
1502   /* There will often not be anything needing escaping at all. */
1503   s = g_string_sized_new (strlen (string));
1504
1505   /* D-Bus address escaping is mostly the same as URI escaping... */
1506   g_string_append_uri_escaped (s, string, "\\/", FALSE);
1507
1508   /* ... but '~' is an unreserved character in URIs, but a
1509    * non-optionally-escaped character in D-Bus addresses. */
1510   for (i = 0; i < s->len; i++)
1511     {
1512       if (G_UNLIKELY (s->str[i] == '~'))
1513         {
1514           s->str[i] = '%';
1515           g_string_insert (s, i + 1, "7E");
1516           i += 2;
1517         }
1518     }
1519
1520   return g_string_free (s, FALSE);
1521 }