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