[kdbus] Make SipHash function as a private utility function to kdbus
[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 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 "gsimpleasyncresult.h"
38 #include "glib-private.h"
39 #include "gdbusprivate.h"
40 #include "giomodule-priv.h"
41 #include "gdbusdaemon.h"
42 #include "gkdbus.h"
43
44 #ifdef G_OS_UNIX
45 #include <gio/gunixsocketaddress.h>
46 #endif
47
48 #ifdef G_OS_WIN32
49 #include <windows.h>
50 #include <io.h>
51 #include <conio.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 [D-Bus specification](http://dbus.freedesktop.org/doc/dbus-specification.html\#addresses).
65  */
66
67 static gchar *get_session_address_platform_specific (GError **error);
68
69 /* ---------------------------------------------------------------------------------------------------- */
70
71 /**
72  * g_dbus_is_address:
73  * @string: A string.
74  *
75  * Checks if @string is a D-Bus address.
76  *
77  * This doesn't check if @string is actually supported by #GDBusServer
78  * or #GDBusConnection - use g_dbus_is_supported_address() to do more
79  * checks.
80  *
81  * Returns: %TRUE if @string is a valid D-Bus address, %FALSE otherwise.
82  *
83  * Since: 2.26
84  */
85 gboolean
86 g_dbus_is_address (const gchar *string)
87 {
88   guint n;
89   gchar **a;
90   gboolean ret;
91
92   ret = FALSE;
93
94   g_return_val_if_fail (string != NULL, FALSE);
95
96   a = g_strsplit (string, ";", 0);
97   if (a[0] == NULL)
98     goto out;
99
100   for (n = 0; a[n] != NULL; n++)
101     {
102       if (!_g_dbus_address_parse_entry (a[n],
103                                         NULL,
104                                         NULL,
105                                         NULL))
106         goto out;
107     }
108
109   ret = TRUE;
110
111  out:
112   g_strfreev (a);
113   return ret;
114 }
115
116 static gboolean
117 is_valid_unix (const gchar  *address_entry,
118                GHashTable   *key_value_pairs,
119                GError      **error)
120 {
121   gboolean ret;
122   GList *keys;
123   GList *l;
124   const gchar *path;
125   const gchar *tmpdir;
126   const gchar *abstract;
127
128   ret = FALSE;
129   keys = NULL;
130   path = NULL;
131   tmpdir = NULL;
132   abstract = NULL;
133
134   keys = g_hash_table_get_keys (key_value_pairs);
135   for (l = keys; l != NULL; l = l->next)
136     {
137       const gchar *key = l->data;
138       if (g_strcmp0 (key, "path") == 0)
139         path = g_hash_table_lookup (key_value_pairs, key);
140       else if (g_strcmp0 (key, "tmpdir") == 0)
141         tmpdir = g_hash_table_lookup (key_value_pairs, key);
142       else if (g_strcmp0 (key, "abstract") == 0)
143         abstract = g_hash_table_lookup (key_value_pairs, key);
144       else
145         {
146           g_set_error (error,
147                        G_IO_ERROR,
148                        G_IO_ERROR_INVALID_ARGUMENT,
149                        _("Unsupported key '%s' in address entry '%s'"),
150                        key,
151                        address_entry);
152           goto out;
153         }
154     }
155
156   if (path != NULL)
157     {
158       if (tmpdir != NULL || abstract != NULL)
159         goto meaningless;
160     }
161   else if (tmpdir != NULL)
162     {
163       if (path != NULL || abstract != NULL)
164         goto meaningless;
165     }
166   else if (abstract != NULL)
167     {
168       if (path != NULL || tmpdir != NULL)
169         goto meaningless;
170     }
171   else
172     {
173       g_set_error (error,
174                    G_IO_ERROR,
175                    G_IO_ERROR_INVALID_ARGUMENT,
176                    _("Address '%s' is invalid (need exactly one of path, tmpdir or abstract keys)"),
177                    address_entry);
178       goto out;
179     }
180
181
182   ret= TRUE;
183   goto out;
184
185  meaningless:
186   g_set_error (error,
187                G_IO_ERROR,
188                G_IO_ERROR_INVALID_ARGUMENT,
189                _("Meaningless key/value pair combination in address entry '%s'"),
190                address_entry);
191
192  out:
193   g_list_free (keys);
194
195   return ret;
196 }
197
198 static gboolean
199 is_valid_nonce_tcp (const gchar  *address_entry,
200                     GHashTable   *key_value_pairs,
201                     GError      **error)
202 {
203   gboolean ret;
204   GList *keys;
205   GList *l;
206   const gchar *host;
207   const gchar *port;
208   const gchar *family;
209   const gchar *nonce_file;
210   gint port_num;
211   gchar *endp;
212
213   ret = FALSE;
214   keys = NULL;
215   host = NULL;
216   port = NULL;
217   family = NULL;
218   nonce_file = NULL;
219
220   keys = g_hash_table_get_keys (key_value_pairs);
221   for (l = keys; l != NULL; l = l->next)
222     {
223       const gchar *key = l->data;
224       if (g_strcmp0 (key, "host") == 0)
225         host = g_hash_table_lookup (key_value_pairs, key);
226       else if (g_strcmp0 (key, "port") == 0)
227         port = g_hash_table_lookup (key_value_pairs, key);
228       else if (g_strcmp0 (key, "family") == 0)
229         family = g_hash_table_lookup (key_value_pairs, key);
230       else if (g_strcmp0 (key, "noncefile") == 0)
231         nonce_file = g_hash_table_lookup (key_value_pairs, key);
232       else
233         {
234           g_set_error (error,
235                        G_IO_ERROR,
236                        G_IO_ERROR_INVALID_ARGUMENT,
237                        _("Unsupported key '%s' in address entry '%s'"),
238                        key,
239                        address_entry);
240           goto out;
241         }
242     }
243
244   if (port != NULL)
245     {
246       port_num = strtol (port, &endp, 10);
247       if ((*port == '\0' || *endp != '\0') || port_num < 0 || port_num >= 65536)
248         {
249           g_set_error (error,
250                        G_IO_ERROR,
251                        G_IO_ERROR_INVALID_ARGUMENT,
252                        _("Error in address '%s' - the port attribute is malformed"),
253                        address_entry);
254           goto out;
255         }
256     }
257
258   if (family != NULL && !(g_strcmp0 (family, "ipv4") == 0 || g_strcmp0 (family, "ipv6") == 0))
259     {
260       g_set_error (error,
261                    G_IO_ERROR,
262                    G_IO_ERROR_INVALID_ARGUMENT,
263                    _("Error in address '%s' - the family attribute is malformed"),
264                    address_entry);
265       goto out;
266     }
267
268   if (host != NULL)
269     {
270       /* TODO: validate host */
271     }
272
273   nonce_file = nonce_file; /* To avoid -Wunused-but-set-variable */
274
275   ret= TRUE;
276
277  out:
278   g_list_free (keys);
279
280   return ret;
281 }
282
283 static gboolean
284 is_valid_tcp (const gchar  *address_entry,
285               GHashTable   *key_value_pairs,
286               GError      **error)
287 {
288   gboolean ret;
289   GList *keys;
290   GList *l;
291   const gchar *host;
292   const gchar *port;
293   const gchar *family;
294   gint port_num;
295   gchar *endp;
296
297   ret = FALSE;
298   keys = NULL;
299   host = NULL;
300   port = NULL;
301   family = NULL;
302
303   keys = g_hash_table_get_keys (key_value_pairs);
304   for (l = keys; l != NULL; l = l->next)
305     {
306       const gchar *key = l->data;
307       if (g_strcmp0 (key, "host") == 0)
308         host = g_hash_table_lookup (key_value_pairs, key);
309       else if (g_strcmp0 (key, "port") == 0)
310         port = g_hash_table_lookup (key_value_pairs, key);
311       else if (g_strcmp0 (key, "family") == 0)
312         family = g_hash_table_lookup (key_value_pairs, key);
313       else
314         {
315           g_set_error (error,
316                        G_IO_ERROR,
317                        G_IO_ERROR_INVALID_ARGUMENT,
318                        _("Unsupported key '%s' in address entry '%s'"),
319                        key,
320                        address_entry);
321           goto out;
322         }
323     }
324
325   if (port != NULL)
326     {
327       port_num = strtol (port, &endp, 10);
328       if ((*port == '\0' || *endp != '\0') || port_num < 0 || port_num >= 65536)
329         {
330           g_set_error (error,
331                        G_IO_ERROR,
332                        G_IO_ERROR_INVALID_ARGUMENT,
333                        _("Error in address '%s' - the port attribute is malformed"),
334                        address_entry);
335           goto out;
336         }
337     }
338
339   if (family != NULL && !(g_strcmp0 (family, "ipv4") == 0 || g_strcmp0 (family, "ipv6") == 0))
340     {
341       g_set_error (error,
342                    G_IO_ERROR,
343                    G_IO_ERROR_INVALID_ARGUMENT,
344                    _("Error in address '%s' - the family attribute is malformed"),
345                    address_entry);
346       goto out;
347     }
348
349   if (host != NULL)
350     {
351       /* TODO: validate host */
352     }
353
354   ret= TRUE;
355
356  out:
357   g_list_free (keys);
358
359   return ret;
360 }
361
362 /**
363  * g_dbus_is_supported_address:
364  * @string: A string.
365  * @error: Return location for error or %NULL.
366  *
367  * Like g_dbus_is_address() but also checks if the library suppors the
368  * transports in @string and that key/value pairs for each transport
369  * are valid.
370  *
371  * Returns: %TRUE if @string is a valid D-Bus address that is
372  * supported by this library, %FALSE if @error is set.
373  *
374  * Since: 2.26
375  */
376 gboolean
377 g_dbus_is_supported_address (const gchar  *string,
378                              GError      **error)
379 {
380   guint n;
381   gchar **a;
382   gboolean ret;
383
384   ret = FALSE;
385
386   g_return_val_if_fail (string != NULL, FALSE);
387   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
388
389   a = g_strsplit (string, ";", 0);
390   for (n = 0; a[n] != NULL; n++)
391     {
392       gchar *transport_name;
393       GHashTable *key_value_pairs;
394       gboolean supported;
395
396       if (!_g_dbus_address_parse_entry (a[n],
397                                         &transport_name,
398                                         &key_value_pairs,
399                                         error))
400         goto out;
401
402       supported = FALSE;
403       if (g_strcmp0 (transport_name, "unix") == 0)
404         supported = is_valid_unix (a[n], key_value_pairs, error);
405       else if (g_strcmp0 (transport_name, "tcp") == 0)
406         supported = is_valid_tcp (a[n], key_value_pairs, error);
407       else if (g_strcmp0 (transport_name, "nonce-tcp") == 0)
408         supported = is_valid_nonce_tcp (a[n], key_value_pairs, error);
409       else if (g_strcmp0 (a[n], "autolaunch:") == 0)
410         supported = TRUE;
411
412       g_free (transport_name);
413       g_hash_table_unref (key_value_pairs);
414
415       if (!supported)
416         goto out;
417     }
418
419   ret = TRUE;
420
421  out:
422   g_strfreev (a);
423
424   g_assert (ret || (!ret && (error == NULL || *error != NULL)));
425
426   return ret;
427 }
428
429 gboolean
430 _g_dbus_address_parse_entry (const gchar  *address_entry,
431                              gchar       **out_transport_name,
432                              GHashTable  **out_key_value_pairs,
433                              GError      **error)
434 {
435   gboolean ret;
436   GHashTable *key_value_pairs;
437   gchar *transport_name;
438   gchar **kv_pairs;
439   const gchar *s;
440   guint n;
441
442   ret = FALSE;
443   kv_pairs = NULL;
444   transport_name = NULL;
445   key_value_pairs = NULL;
446
447   s = strchr (address_entry, ':');
448   if (s == NULL)
449     {
450       g_set_error (error,
451                    G_IO_ERROR,
452                    G_IO_ERROR_INVALID_ARGUMENT,
453                    _("Address element '%s' does not contain a colon (:)"),
454                    address_entry);
455       goto out;
456     }
457
458   transport_name = g_strndup (address_entry, s - address_entry);
459   key_value_pairs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
460
461   kv_pairs = g_strsplit (s + 1, ",", 0);
462   for (n = 0; kv_pairs != NULL && kv_pairs[n] != NULL; n++)
463     {
464       const gchar *kv_pair = kv_pairs[n];
465       gchar *key;
466       gchar *value;
467
468       s = strchr (kv_pair, '=');
469       if (s == NULL)
470         {
471           g_set_error (error,
472                        G_IO_ERROR,
473                        G_IO_ERROR_INVALID_ARGUMENT,
474                        _("Key/Value pair %d, '%s', in address element '%s' does not contain an equal sign"),
475                        n,
476                        kv_pair,
477                        address_entry);
478           goto out;
479         }
480
481       key = g_uri_unescape_segment (kv_pair, s, NULL);
482       value = g_uri_unescape_segment (s + 1, kv_pair + strlen (kv_pair), NULL);
483       if (key == NULL || value == NULL)
484         {
485           g_set_error (error,
486                        G_IO_ERROR,
487                        G_IO_ERROR_INVALID_ARGUMENT,
488                        _("Error unescaping key or value in Key/Value pair %d, '%s', in address element '%s'"),
489                        n,
490                        kv_pair,
491                        address_entry);
492           g_free (key);
493           g_free (value);
494           goto out;
495         }
496       g_hash_table_insert (key_value_pairs, key, value);
497     }
498
499   ret = TRUE;
500
501 out:
502   g_strfreev (kv_pairs);
503   if (ret)
504     {
505       if (out_transport_name != NULL)
506         *out_transport_name = transport_name;
507       else
508         g_free (transport_name);
509       if (out_key_value_pairs != NULL)
510         *out_key_value_pairs = key_value_pairs;
511       else if (key_value_pairs != NULL)
512         g_hash_table_unref (key_value_pairs);
513     }
514   else
515     {
516       g_free (transport_name);
517       if (key_value_pairs != NULL)
518         g_hash_table_unref (key_value_pairs);
519     }
520   return ret;
521 }
522
523 /* ---------------------------------------------------------------------------------------------------- */
524
525 static GObject *
526 g_dbus_address_try_connect_one (const gchar   *address_entry,
527                                 gboolean       kdbus_okay,
528                                 gchar        **out_guid,
529                                 GCancellable  *cancellable,
530                                 GError       **error);
531
532 /* TODO: Declare an extension point called GDBusTransport (or similar)
533  * and move code below to extensions implementing said extension
534  * point. That way we can implement a D-Bus transport over X11 without
535  * making libgio link to libX11...
536  */
537 static GObject *
538 g_dbus_address_connect (const gchar   *address_entry,
539                         const gchar   *transport_name,
540                         gboolean       kdbus_okay,
541                         GHashTable    *key_value_pairs,
542                         GCancellable  *cancellable,
543                         GError       **error)
544 {
545   GObject *ret;
546   GSocketConnectable *connectable;
547   const gchar *nonce_file;
548
549   connectable = NULL;
550   ret = NULL;
551   nonce_file = NULL;
552
553   if (FALSE)
554     {
555     }
556 #ifdef G_OS_UNIX
557   else if (kdbus_okay && g_str_equal (transport_name, "kernel"))
558     {
559       GKDBusWorker *worker;
560       const gchar *path;
561
562       path = g_hash_table_lookup (key_value_pairs, "path");
563
564       if (path == NULL)
565         {
566           g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
567                        _("Error in address '%s' - the kernel transport requires a path"),
568                        address_entry);
569         }
570       else
571         {
572           worker = g_kdbus_worker_new (path, error);
573
574           if (worker == NULL)
575             return NULL;
576
577           return G_OBJECT (worker);
578         }
579     }
580   else if (g_strcmp0 (transport_name, "unix") == 0)
581     {
582       const gchar *path;
583       const gchar *abstract;
584       path = g_hash_table_lookup (key_value_pairs, "path");
585       abstract = g_hash_table_lookup (key_value_pairs, "abstract");
586       if ((path == NULL && abstract == NULL) || (path != NULL && abstract != NULL))
587         {
588           g_set_error (error,
589                        G_IO_ERROR,
590                        G_IO_ERROR_INVALID_ARGUMENT,
591                        _("Error in address '%s' - the unix transport requires exactly one of the "
592                          "keys 'path' or 'abstract' to be set"),
593                        address_entry);
594         }
595       else if (path != NULL)
596         {
597           connectable = G_SOCKET_CONNECTABLE (g_unix_socket_address_new (path));
598         }
599       else if (abstract != NULL)
600         {
601           connectable = G_SOCKET_CONNECTABLE (g_unix_socket_address_new_with_type (abstract,
602                                                                                    -1,
603                                                                                    G_UNIX_SOCKET_ADDRESS_ABSTRACT));
604         }
605       else
606         {
607           g_assert_not_reached ();
608         }
609     }
610 #endif
611   else if (g_strcmp0 (transport_name, "tcp") == 0 || g_strcmp0 (transport_name, "nonce-tcp") == 0)
612     {
613       const gchar *s;
614       const gchar *host;
615       glong port;
616       gchar *endp;
617       gboolean is_nonce;
618
619       is_nonce = (g_strcmp0 (transport_name, "nonce-tcp") == 0);
620
621       host = g_hash_table_lookup (key_value_pairs, "host");
622       if (host == NULL)
623         {
624           g_set_error (error,
625                        G_IO_ERROR,
626                        G_IO_ERROR_INVALID_ARGUMENT,
627                        _("Error in address '%s' - the host attribute is missing or malformed"),
628                        address_entry);
629           goto out;
630         }
631
632       s = g_hash_table_lookup (key_value_pairs, "port");
633       if (s == NULL)
634         s = "0";
635       port = strtol (s, &endp, 10);
636       if ((*s == '\0' || *endp != '\0') || port < 0 || port >= 65536)
637         {
638           g_set_error (error,
639                        G_IO_ERROR,
640                        G_IO_ERROR_INVALID_ARGUMENT,
641                        _("Error in address '%s' - the port attribute is missing or malformed"),
642                        address_entry);
643           goto out;
644         }
645
646
647       if (is_nonce)
648         {
649           nonce_file = g_hash_table_lookup (key_value_pairs, "noncefile");
650           if (nonce_file == NULL)
651             {
652               g_set_error (error,
653                            G_IO_ERROR,
654                            G_IO_ERROR_INVALID_ARGUMENT,
655                            _("Error in address '%s' - the noncefile attribute is missing or malformed"),
656                            address_entry);
657               goto out;
658             }
659         }
660
661       /* TODO: deal with family key/value-pair */
662       connectable = g_network_address_new (host, port);
663     }
664   else if (g_strcmp0 (address_entry, "autolaunch:") == 0)
665     {
666       gchar *autolaunch_address;
667       autolaunch_address = get_session_address_platform_specific (error);
668       if (autolaunch_address != NULL)
669         {
670           ret = g_dbus_address_try_connect_one (autolaunch_address, kdbus_okay, NULL, cancellable, error);
671           g_free (autolaunch_address);
672           goto out;
673         }
674       else
675         {
676           g_prefix_error (error, _("Error auto-launching: "));
677         }
678     }
679   else
680     {
681       g_set_error (error,
682                    G_IO_ERROR,
683                    G_IO_ERROR_INVALID_ARGUMENT,
684                    _("Unknown or unsupported transport '%s' for address '%s'"),
685                    transport_name,
686                    address_entry);
687     }
688
689   if (connectable != NULL)
690     {
691       GSocketClient *client;
692       GSocketConnection *connection;
693
694       g_assert (ret == NULL);
695       client = g_socket_client_new ();
696       connection = g_socket_client_connect (client,
697                                             connectable,
698                                             cancellable,
699                                             error);
700       g_object_unref (connectable);
701       g_object_unref (client);
702       if (connection == NULL)
703         goto out;
704
705       ret = G_OBJECT (connection);
706
707       if (nonce_file != NULL)
708         {
709           gchar nonce_contents[16 + 1];
710           size_t num_bytes_read;
711           FILE *f;
712
713           /* be careful to read only 16 bytes - we also check that the file is only 16 bytes long */
714           f = fopen (nonce_file, "rb");
715           if (f == NULL)
716             {
717               g_set_error (error,
718                            G_IO_ERROR,
719                            G_IO_ERROR_INVALID_ARGUMENT,
720                            _("Error opening nonce file '%s': %s"),
721                            nonce_file,
722                            g_strerror (errno));
723               g_object_unref (ret);
724               ret = NULL;
725               goto out;
726             }
727           num_bytes_read = fread (nonce_contents,
728                                   sizeof (gchar),
729                                   16 + 1,
730                                   f);
731           if (num_bytes_read != 16)
732             {
733               if (num_bytes_read == 0)
734                 {
735                   g_set_error (error,
736                                G_IO_ERROR,
737                                G_IO_ERROR_INVALID_ARGUMENT,
738                                _("Error reading from nonce file '%s': %s"),
739                                nonce_file,
740                                g_strerror (errno));
741                 }
742               else
743                 {
744                   g_set_error (error,
745                                G_IO_ERROR,
746                                G_IO_ERROR_INVALID_ARGUMENT,
747                                _("Error reading from nonce file '%s', expected 16 bytes, got %d"),
748                                nonce_file,
749                                (gint) num_bytes_read);
750                 }
751               g_object_unref (ret);
752               ret = NULL;
753               fclose (f);
754               goto out;
755             }
756           fclose (f);
757
758           if (!g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (connection)),
759                                           nonce_contents,
760                                           16,
761                                           NULL,
762                                           cancellable,
763                                           error))
764             {
765               g_prefix_error (error, _("Error writing contents of nonce file '%s' to stream:"), nonce_file);
766               g_object_unref (ret);
767               ret = NULL;
768               goto out;
769             }
770         }
771     }
772
773  out:
774
775   return ret;
776 }
777
778 static GObject *
779 g_dbus_address_try_connect_one (const gchar   *address_entry,
780                                 gboolean       kdbus_okay,
781                                 gchar        **out_guid,
782                                 GCancellable  *cancellable,
783                                 GError       **error)
784 {
785   GObject *ret;
786   GHashTable *key_value_pairs;
787   gchar *transport_name;
788   const gchar *guid;
789
790   ret = NULL;
791   transport_name = NULL;
792   key_value_pairs = NULL;
793
794   if (!_g_dbus_address_parse_entry (address_entry,
795                                     &transport_name,
796                                     &key_value_pairs,
797                                     error))
798     goto out;
799
800   ret = g_dbus_address_connect (address_entry,
801                                 transport_name,
802                                 kdbus_okay,
803                                 key_value_pairs,
804                                 cancellable,
805                                 error);
806   if (ret == NULL)
807     goto out;
808
809   guid = g_hash_table_lookup (key_value_pairs, "guid");
810   if (guid != NULL && out_guid != NULL)
811     *out_guid = g_strdup (guid);
812
813 out:
814   g_free (transport_name);
815   if (key_value_pairs != NULL)
816     g_hash_table_unref (key_value_pairs);
817   return ret;
818 }
819
820
821 /* ---------------------------------------------------------------------------------------------------- */
822
823 typedef struct {
824   gchar *address;
825   GIOStream *stream;
826   gchar *guid;
827 } GetStreamData;
828
829 static void
830 get_stream_data_free (GetStreamData *data)
831 {
832   g_free (data->address);
833   if (data->stream != NULL)
834     g_object_unref (data->stream);
835   g_free (data->guid);
836   g_free (data);
837 }
838
839 static void
840 get_stream_thread_func (GSimpleAsyncResult *res,
841                         GObject            *object,
842                         GCancellable       *cancellable)
843 {
844   GetStreamData *data;
845   GError *error;
846
847   data = g_simple_async_result_get_op_res_gpointer (res);
848
849   error = NULL;
850   data->stream = g_dbus_address_get_stream_sync (data->address,
851                                                  &data->guid,
852                                                  cancellable,
853                                                  &error);
854   if (data->stream == NULL)
855     g_simple_async_result_take_error (res, error);
856 }
857
858 /**
859  * g_dbus_address_get_stream:
860  * @address: A valid D-Bus address.
861  * @cancellable: (allow-none): A #GCancellable or %NULL.
862  * @callback: A #GAsyncReadyCallback to call when the request is satisfied.
863  * @user_data: Data to pass to @callback.
864  *
865  * Asynchronously connects to an endpoint specified by @address and
866  * sets up the connection so it is in a state to run the client-side
867  * of the D-Bus authentication conversation.
868  *
869  * When the operation is finished, @callback will be invoked. You can
870  * then call g_dbus_address_get_stream_finish() to get the result of
871  * the operation.
872  *
873  * This is an asynchronous failable function. See
874  * g_dbus_address_get_stream_sync() for the synchronous version.
875  *
876  * Since: 2.26
877  */
878 void
879 g_dbus_address_get_stream (const gchar         *address,
880                            GCancellable        *cancellable,
881                            GAsyncReadyCallback  callback,
882                            gpointer             user_data)
883 {
884   GSimpleAsyncResult *res;
885   GetStreamData *data;
886
887   g_return_if_fail (address != NULL);
888
889   res = g_simple_async_result_new (NULL,
890                                    callback,
891                                    user_data,
892                                    g_dbus_address_get_stream);
893   g_simple_async_result_set_check_cancellable (res, cancellable);
894   data = g_new0 (GetStreamData, 1);
895   data->address = g_strdup (address);
896   g_simple_async_result_set_op_res_gpointer (res,
897                                              data,
898                                              (GDestroyNotify) get_stream_data_free);
899   g_simple_async_result_run_in_thread (res,
900                                        get_stream_thread_func,
901                                        G_PRIORITY_DEFAULT,
902                                        cancellable);
903   g_object_unref (res);
904 }
905
906 /**
907  * g_dbus_address_get_stream_finish:
908  * @res: A #GAsyncResult obtained from the GAsyncReadyCallback passed to g_dbus_address_get_stream().
909  * @out_guid: %NULL or return location to store the GUID extracted from @address, if any.
910  * @error: Return location for error or %NULL.
911  *
912  * Finishes an operation started with g_dbus_address_get_stream().
913  *
914  * Returns: (transfer full): A #GIOStream or %NULL if @error is set.
915  *
916  * Since: 2.26
917  */
918 GIOStream *
919 g_dbus_address_get_stream_finish (GAsyncResult        *res,
920                                   gchar              **out_guid,
921                                   GError             **error)
922 {
923   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
924   GetStreamData *data;
925   GIOStream *ret;
926
927   g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
928   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
929
930   g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_dbus_address_get_stream);
931
932   ret = NULL;
933
934   data = g_simple_async_result_get_op_res_gpointer (simple);
935   if (g_simple_async_result_propagate_error (simple, error))
936     goto out;
937
938   ret = g_object_ref (data->stream);
939   if (out_guid != NULL)
940     *out_guid = g_strdup (data->guid);
941
942  out:
943   return ret;
944 }
945
946 /**
947  * g_dbus_address_get_stream_sync:
948  * @address: A valid D-Bus address.
949  * @out_guid: %NULL or return location to store the GUID extracted from @address, if any.
950  * @cancellable: (allow-none): A #GCancellable or %NULL.
951  * @error: Return location for error or %NULL.
952  *
953  * Synchronously connects to an endpoint specified by @address and
954  * sets up the connection so it is in a state to run the client-side
955  * of the D-Bus authentication conversation.
956  *
957  * This is a synchronous failable function. See
958  * g_dbus_address_get_stream() for the asynchronous version.
959  *
960  * Returns: (transfer full): A #GIOStream or %NULL if @error is set.
961  *
962  * Since: 2.26
963  */
964 GIOStream *
965 g_dbus_address_get_stream_sync (const gchar   *address,
966                                 gchar        **out_guid,
967                                 GCancellable  *cancellable,
968                                 GError       **error)
969 {
970   GObject *result;
971
972   result = g_dbus_address_get_stream_internal (address, FALSE, out_guid, cancellable, error);
973   g_assert (result == NULL || G_IS_IO_STREAM (result));
974
975   if (result)
976     return G_IO_STREAM (result);
977
978   return NULL;
979 }
980
981 GObject *
982 g_dbus_address_get_stream_internal (const gchar   *address,
983                                     gboolean       kdbus_okay,
984                                     gchar        **out_guid,
985                                     GCancellable  *cancellable,
986                                     GError       **error)
987 {
988   GObject *ret;
989   gchar **addr_array;
990   guint n;
991   GError *last_error;
992
993   g_return_val_if_fail (address != NULL, NULL);
994   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
995
996   ret = NULL;
997   last_error = NULL;
998
999   addr_array = g_strsplit (address, ";", 0);
1000   if (addr_array != NULL && addr_array[0] == NULL)
1001     {
1002       last_error = g_error_new_literal (G_IO_ERROR,
1003                                         G_IO_ERROR_INVALID_ARGUMENT,
1004                                         _("The given address is empty"));
1005       goto out;
1006     }
1007
1008   for (n = 0; addr_array != NULL && addr_array[n] != NULL; n++)
1009     {
1010       const gchar *addr = addr_array[n];
1011       GError *this_error;
1012
1013       this_error = NULL;
1014       ret = g_dbus_address_try_connect_one (addr,
1015                                             kdbus_okay,
1016                                             out_guid,
1017                                             cancellable,
1018                                             &this_error);
1019       if (ret != NULL)
1020         {
1021           goto out;
1022         }
1023       else
1024         {
1025           g_assert (this_error != NULL);
1026           if (last_error != NULL)
1027             g_error_free (last_error);
1028           last_error = this_error;
1029         }
1030     }
1031
1032  out:
1033   if (ret != NULL)
1034     {
1035       if (last_error != NULL)
1036         g_error_free (last_error);
1037     }
1038   else
1039     {
1040       g_assert (last_error != NULL);
1041       g_propagate_error (error, last_error);
1042     }
1043
1044   g_strfreev (addr_array);
1045   return ret;
1046 }
1047
1048 /* ---------------------------------------------------------------------------------------------------- */
1049
1050 #ifdef G_OS_UNIX
1051 static gchar *
1052 get_session_address_dbus_launch (GError **error)
1053 {
1054   gchar *ret;
1055   gchar *machine_id;
1056   gchar *command_line;
1057   gchar *launch_stdout;
1058   gchar *launch_stderr;
1059   gint exit_status;
1060   gchar *old_dbus_verbose;
1061   gboolean restore_dbus_verbose;
1062
1063   ret = NULL;
1064   machine_id = NULL;
1065   command_line = NULL;
1066   launch_stdout = NULL;
1067   launch_stderr = NULL;
1068   restore_dbus_verbose = FALSE;
1069   old_dbus_verbose = NULL;
1070
1071   /* Don't run binaries as root if we're setuid. */
1072   if (GLIB_PRIVATE_CALL (g_check_setuid) ())
1073     {
1074       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1075                    _("Cannot spawn a message bus when setuid"));
1076       goto out;
1077     }
1078
1079   machine_id = _g_dbus_get_machine_id (error);
1080   if (machine_id == NULL)
1081     {
1082       g_prefix_error (error, _("Cannot spawn a message bus without a machine-id: "));
1083       goto out;
1084     }
1085
1086   /* We're using private libdbus facilities here. When everything
1087    * (X11, Mac OS X, Windows) is spec'ed out correctly (not even the
1088    * X11 property is correctly documented right now) we should
1089    * consider using the spec instead of dbus-launch.
1090    *
1091    *   --autolaunch=MACHINEID
1092    *          This option implies that dbus-launch should scan  for  a  previ‐
1093    *          ously-started  session  and  reuse the values found there. If no
1094    *          session is found, it will start a new session. The  --exit-with-
1095    *          session option is implied if --autolaunch is given.  This option
1096    *          is for the exclusive use of libdbus, you do not want to  use  it
1097    *          manually. It may change in the future.
1098    */
1099
1100   /* TODO: maybe provide a variable for where to look for the dbus-launch binary? */
1101   command_line = g_strdup_printf ("dbus-launch --autolaunch=%s --binary-syntax --close-stderr", machine_id);
1102
1103   if (G_UNLIKELY (_g_dbus_debug_address ()))
1104     {
1105       _g_dbus_debug_print_lock ();
1106       g_print ("GDBus-debug:Address: Running '%s' to get bus address (possibly autolaunching)\n", command_line);
1107       old_dbus_verbose = g_strdup (g_getenv ("DBUS_VERBOSE"));
1108       restore_dbus_verbose = TRUE;
1109       g_setenv ("DBUS_VERBOSE", "1", TRUE);
1110       _g_dbus_debug_print_unlock ();
1111     }
1112
1113   if (!g_spawn_command_line_sync (command_line,
1114                                   &launch_stdout,
1115                                   &launch_stderr,
1116                                   &exit_status,
1117                                   error))
1118     {
1119       goto out;
1120     }
1121
1122   if (!g_spawn_check_exit_status (exit_status, error))
1123     {
1124       g_prefix_error (error, _("Error spawning command line '%s': "), command_line);
1125       goto out;
1126     }
1127
1128   /* From the dbus-launch(1) man page:
1129    *
1130    *   --binary-syntax Write to stdout a nul-terminated bus address,
1131    *   then the bus PID as a binary integer of size sizeof(pid_t),
1132    *   then the bus X window ID as a binary integer of size
1133    *   sizeof(long).  Integers are in the machine's byte order, not
1134    *   network byte order or any other canonical byte order.
1135    */
1136   ret = g_strdup (launch_stdout);
1137
1138  out:
1139   if (G_UNLIKELY (_g_dbus_debug_address ()))
1140     {
1141       gchar *s;
1142       _g_dbus_debug_print_lock ();
1143       g_print ("GDBus-debug:Address: dbus-launch output:");
1144       if (launch_stdout != NULL)
1145         {
1146           s = _g_dbus_hexdump (launch_stdout, strlen (launch_stdout) + 1 + sizeof (pid_t) + sizeof (long), 2);
1147           g_print ("\n%s", s);
1148           g_free (s);
1149         }
1150       else
1151         {
1152           g_print (" (none)\n");
1153         }
1154       g_print ("GDBus-debug:Address: dbus-launch stderr output:");
1155       if (launch_stderr != NULL)
1156         g_print ("\n%s", launch_stderr);
1157       else
1158         g_print (" (none)\n");
1159       _g_dbus_debug_print_unlock ();
1160     }
1161
1162   g_free (machine_id);
1163   g_free (command_line);
1164   g_free (launch_stdout);
1165   g_free (launch_stderr);
1166   if (G_UNLIKELY (restore_dbus_verbose))
1167     {
1168       if (old_dbus_verbose != NULL)
1169         g_setenv ("DBUS_VERBOSE", old_dbus_verbose, TRUE);
1170       else
1171         g_unsetenv ("DBUS_VERBOSE");
1172     }
1173   g_free (old_dbus_verbose);
1174   return ret;
1175 }
1176 #endif
1177
1178 #ifdef G_OS_WIN32
1179
1180 #define DBUS_DAEMON_ADDRESS_INFO "DBusDaemonAddressInfo"
1181 #define DBUS_DAEMON_MUTEX "DBusDaemonMutex"
1182 #define UNIQUE_DBUS_INIT_MUTEX "UniqueDBusInitMutex"
1183 #define DBUS_AUTOLAUNCH_MUTEX "DBusAutolaunchMutex"
1184
1185 static void
1186 release_mutex (HANDLE mutex)
1187 {
1188   ReleaseMutex (mutex);
1189   CloseHandle (mutex);
1190 }
1191
1192 static HANDLE
1193 acquire_mutex (const char *mutexname)
1194 {
1195   HANDLE mutex;
1196   DWORD res;
1197
1198   mutex = CreateMutexA (NULL, FALSE, mutexname);
1199   if (!mutex)
1200     return 0;
1201
1202   res = WaitForSingleObject (mutex, INFINITE);
1203   switch (res)
1204     {
1205     case WAIT_ABANDONED:
1206       release_mutex (mutex);
1207       return 0;
1208     case WAIT_FAILED:
1209     case WAIT_TIMEOUT:
1210       return 0;
1211     }
1212
1213   return mutex;
1214 }
1215
1216 static gboolean
1217 is_mutex_owned (const char *mutexname)
1218 {
1219   HANDLE mutex;
1220   gboolean res = FALSE;
1221
1222   mutex = CreateMutexA (NULL, FALSE, mutexname);
1223   if (WaitForSingleObject (mutex, 10) == WAIT_TIMEOUT)
1224     res = TRUE;
1225   else
1226     ReleaseMutex (mutex);
1227   CloseHandle (mutex);
1228
1229   return res;
1230 }
1231
1232 static char *
1233 read_shm (const char *shm_name)
1234 {
1235   HANDLE shared_mem;
1236   char *shared_data;
1237   char *res;
1238   int i;
1239
1240   res = NULL;
1241
1242   for (i = 0; i < 20; i++)
1243     {
1244       shared_mem = OpenFileMappingA (FILE_MAP_READ, FALSE, shm_name);
1245       if (shared_mem != 0)
1246         break;
1247       Sleep (100);
1248     }
1249
1250   if (shared_mem != 0)
1251     {
1252       shared_data = MapViewOfFile (shared_mem, FILE_MAP_READ, 0, 0, 0);
1253       if (shared_data != NULL)
1254         {
1255           res = g_strdup (shared_data);
1256           UnmapViewOfFile (shared_data);
1257         }
1258       CloseHandle (shared_mem);
1259     }
1260
1261   return res;
1262 }
1263
1264 static HANDLE
1265 set_shm (const char *shm_name, const char *value)
1266 {
1267   HANDLE shared_mem;
1268   char *shared_data;
1269
1270   shared_mem = CreateFileMappingA (INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
1271                                    0, strlen (value) + 1, shm_name);
1272   if (shared_mem == 0)
1273     return 0;
1274
1275   shared_data = MapViewOfFile (shared_mem, FILE_MAP_WRITE, 0, 0, 0 );
1276   if (shared_data == NULL)
1277     return 0;
1278
1279   strcpy (shared_data, value);
1280
1281   UnmapViewOfFile (shared_data);
1282
1283   return shared_mem;
1284 }
1285
1286 /* These keep state between publish_session_bus and unpublish_session_bus */
1287 static HANDLE published_daemon_mutex;
1288 static HANDLE published_shared_mem;
1289
1290 static gboolean
1291 publish_session_bus (const char *address)
1292 {
1293   HANDLE init_mutex;
1294
1295   init_mutex = acquire_mutex (UNIQUE_DBUS_INIT_MUTEX);
1296
1297   published_daemon_mutex = CreateMutexA (NULL, FALSE, DBUS_DAEMON_MUTEX);
1298   if (WaitForSingleObject (published_daemon_mutex, 10 ) != WAIT_OBJECT_0)
1299     {
1300       release_mutex (init_mutex);
1301       CloseHandle (published_daemon_mutex);
1302       published_daemon_mutex = NULL;
1303       return FALSE;
1304     }
1305
1306   published_shared_mem = set_shm (DBUS_DAEMON_ADDRESS_INFO, address);
1307   if (!published_shared_mem)
1308     {
1309       release_mutex (init_mutex);
1310       CloseHandle (published_daemon_mutex);
1311       published_daemon_mutex = NULL;
1312       return FALSE;
1313     }
1314
1315   release_mutex (init_mutex);
1316   return TRUE;
1317 }
1318
1319 static void
1320 unpublish_session_bus (void)
1321 {
1322   HANDLE init_mutex;
1323
1324   init_mutex = acquire_mutex (UNIQUE_DBUS_INIT_MUTEX);
1325
1326   CloseHandle (published_shared_mem);
1327   published_shared_mem = NULL;
1328
1329   release_mutex (published_daemon_mutex);
1330   published_daemon_mutex = NULL;
1331
1332   release_mutex (init_mutex);
1333 }
1334
1335 static void
1336 wait_console_window (void)
1337 {
1338   FILE *console = fopen ("CONOUT$", "w");
1339
1340   SetConsoleTitleW (L"gdbus-daemon output. Type any character to close this window.");
1341   fprintf (console, _("(Type any character to close this window)\n"));
1342   fflush (console);
1343   _getch ();
1344 }
1345
1346 static void
1347 open_console_window (void)
1348 {
1349   if (((HANDLE) _get_osfhandle (fileno (stdout)) == INVALID_HANDLE_VALUE ||
1350        (HANDLE) _get_osfhandle (fileno (stderr)) == INVALID_HANDLE_VALUE) && AllocConsole ())
1351     {
1352       if ((HANDLE) _get_osfhandle (fileno (stdout)) == INVALID_HANDLE_VALUE)
1353         freopen ("CONOUT$", "w", stdout);
1354
1355       if ((HANDLE) _get_osfhandle (fileno (stderr)) == INVALID_HANDLE_VALUE)
1356         freopen ("CONOUT$", "w", stderr);
1357
1358       SetConsoleTitleW (L"gdbus-daemon debug output.");
1359
1360       atexit (wait_console_window);
1361     }
1362 }
1363 static void
1364 idle_timeout_cb (GDBusDaemon *daemon, gpointer user_data)
1365 {
1366   GMainLoop *loop = user_data;
1367   g_main_loop_quit (loop);
1368 }
1369
1370 __declspec(dllexport) void CALLBACK g_win32_run_session_bus (HWND hwnd, HINSTANCE hinst, char *cmdline, int nCmdShow);
1371
1372 __declspec(dllexport) void CALLBACK
1373 g_win32_run_session_bus (HWND hwnd, HINSTANCE hinst, char *cmdline, int nCmdShow)
1374 {
1375   GDBusDaemon *daemon;
1376   GMainLoop *loop;
1377   const char *address;
1378   GError *error = NULL;
1379
1380   if (g_getenv ("GDBUS_DAEMON_DEBUG") != NULL)
1381     open_console_window ();
1382
1383   loop = g_main_loop_new (NULL, FALSE);
1384
1385   address = "nonce-tcp:";
1386   daemon = _g_dbus_daemon_new (address, NULL, &error);
1387   if (daemon == NULL)
1388     {
1389       g_printerr ("Can't init bus: %s\n", error->message);
1390       return;
1391     }
1392
1393   g_signal_connect (daemon, "idle-timeout", G_CALLBACK (idle_timeout_cb), loop);
1394
1395   if ( publish_session_bus (_g_dbus_daemon_get_address (daemon)))
1396     {
1397       g_main_loop_run (loop);
1398
1399       unpublish_session_bus ();
1400     }
1401
1402   g_main_loop_unref (loop);
1403   g_object_unref (daemon);
1404 }
1405
1406 static gchar *
1407 get_session_address_dbus_launch (GError **error)
1408 {
1409   HANDLE autolaunch_mutex, init_mutex;
1410   char *address = NULL;
1411   wchar_t gio_path[MAX_PATH+1+200];
1412
1413   autolaunch_mutex = acquire_mutex (DBUS_AUTOLAUNCH_MUTEX);
1414
1415   init_mutex = acquire_mutex (UNIQUE_DBUS_INIT_MUTEX);
1416
1417   if (is_mutex_owned (DBUS_DAEMON_MUTEX))
1418     address = read_shm (DBUS_DAEMON_ADDRESS_INFO);
1419
1420   release_mutex (init_mutex);
1421
1422   if (address == NULL)
1423     {
1424       gio_path[MAX_PATH] = 0;
1425       if (GetModuleFileNameW (_g_io_win32_get_module (), gio_path, MAX_PATH))
1426         {
1427           PROCESS_INFORMATION pi = { 0 };
1428           STARTUPINFOW si = { 0 };
1429           BOOL res;
1430           wchar_t gio_path_short[MAX_PATH];
1431           wchar_t rundll_path[MAX_PATH*2];
1432           wchar_t args[MAX_PATH*4];
1433
1434           GetShortPathNameW (gio_path, gio_path_short, MAX_PATH);
1435
1436           GetWindowsDirectoryW (rundll_path, MAX_PATH);
1437           wcscat (rundll_path, L"\\rundll32.exe");
1438           if (GetFileAttributesW (rundll_path) == INVALID_FILE_ATTRIBUTES)
1439             {
1440               GetSystemDirectoryW (rundll_path, MAX_PATH);
1441               wcscat (rundll_path, L"\\rundll32.exe");
1442             }
1443
1444           wcscpy (args, L"\"");
1445           wcscat (args, rundll_path);
1446           wcscat (args, L"\" ");
1447           wcscat (args, gio_path_short);
1448 #if defined(_WIN64) || defined(_M_X64) || defined(_M_AMD64)
1449           wcscat (args, L",g_win32_run_session_bus");
1450 #elif defined (_MSC_VER)
1451           wcscat (args, L",_g_win32_run_session_bus@16");
1452 #else
1453           wcscat (args, L",g_win32_run_session_bus@16");
1454 #endif
1455
1456           res = CreateProcessW (rundll_path, args,
1457                                 0, 0, FALSE,
1458                                 NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW | DETACHED_PROCESS,
1459                                 0, NULL /* TODO: Should be root */,
1460                                 &si, &pi);
1461           if (res)
1462             address = read_shm (DBUS_DAEMON_ADDRESS_INFO);
1463         }
1464     }
1465
1466   release_mutex (autolaunch_mutex);
1467
1468   if (address == NULL)
1469     g_set_error (error,
1470                  G_IO_ERROR,
1471                  G_IO_ERROR_FAILED,
1472                  _("Session dbus not running, and autolaunch failed"));
1473
1474   return address;
1475 }
1476 #endif
1477
1478 /* ---------------------------------------------------------------------------------------------------- */
1479
1480 static gchar *
1481 get_session_address_platform_specific (GError **error)
1482 {
1483   gchar *ret;
1484 #if defined (G_OS_UNIX) || defined(G_OS_WIN32)
1485   /* need to handle OS X in a different way since 'dbus-launch --autolaunch' probably won't work there */
1486   ret = get_session_address_dbus_launch (error);
1487 #else
1488   /* TODO: implement for OS X */
1489   ret = NULL;
1490   g_set_error (error,
1491                G_IO_ERROR,
1492                G_IO_ERROR_FAILED,
1493                _("Cannot determine session bus address (not implemented for this OS)"));
1494 #endif
1495   return ret;
1496 }
1497
1498 /* ---------------------------------------------------------------------------------------------------- */
1499
1500 /**
1501  * g_dbus_address_get_for_bus_sync:
1502  * @bus_type: a #GBusType
1503  * @cancellable: (allow-none): a #GCancellable or %NULL
1504  * @error: return location for error or %NULL
1505  *
1506  * Synchronously looks up the D-Bus address for the well-known message
1507  * bus instance specified by @bus_type. This may involve using various
1508  * platform specific mechanisms.
1509  *
1510  * Returns: a valid D-Bus address string for @bus_type or %NULL if
1511  *     @error is set
1512  *
1513  * Since: 2.26
1514  */
1515 gchar *
1516 g_dbus_address_get_for_bus_sync (GBusType       bus_type,
1517                                  GCancellable  *cancellable,
1518                                  GError       **error)
1519 {
1520   gchar *ret;
1521   const gchar *system_bus;
1522   const gchar *session_bus;
1523   const gchar *starter_bus;
1524   GError *local_error;
1525
1526   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1527
1528   ret = NULL;
1529   local_error = NULL;
1530
1531   if (G_UNLIKELY (_g_dbus_debug_address ()))
1532     {
1533       guint n;
1534       _g_dbus_debug_print_lock ();
1535       g_print ("GDBus-debug:Address: In g_dbus_address_get_for_bus_sync() for bus type '%s'\n",
1536                _g_dbus_enum_to_string (G_TYPE_BUS_TYPE, bus_type));
1537       for (n = 0; n < 3; n++)
1538         {
1539           const gchar *k;
1540           const gchar *v;
1541           switch (n)
1542             {
1543             case 0: k = "DBUS_SESSION_BUS_ADDRESS"; break;
1544             case 1: k = "DBUS_SYSTEM_BUS_ADDRESS"; break;
1545             case 2: k = "DBUS_STARTER_BUS_TYPE"; break;
1546             default: g_assert_not_reached ();
1547             }
1548           v = g_getenv (k);
1549           g_print ("GDBus-debug:Address: env var %s", k);
1550           if (v != NULL)
1551             g_print ("='%s'\n", v);
1552           else
1553             g_print (" is not set\n");
1554         }
1555       _g_dbus_debug_print_unlock ();
1556     }
1557
1558   switch (bus_type)
1559     {
1560     case G_BUS_TYPE_SYSTEM:
1561       ret = g_strdup (g_getenv ("DBUS_SYSTEM_BUS_ADDRESS"));
1562       if (ret == NULL)
1563         {
1564           ret = g_strdup ("unix:path=/var/run/dbus/system_bus_socket");
1565         }
1566       break;
1567
1568     case G_BUS_TYPE_SESSION:
1569       ret = g_strdup (g_getenv ("DBUS_SESSION_BUS_ADDRESS"));
1570       if (ret == NULL)
1571         {
1572           ret = get_session_address_platform_specific (&local_error);
1573         }
1574       break;
1575
1576     case G_BUS_TYPE_MACHINE:
1577       system_bus = g_getenv ("DBUS_SYSTEM_BUS_ADDRESS");
1578       if (system_bus == NULL)
1579         ret = g_strdup ("kernel:path=/sys/fs/kdbus/0-system/bus;unix:path=/var/run/dbus/system_bus_socket");
1580       else
1581         ret = g_strdup_printf ("kernel:path=/sys/fs/kdbus/0-system/bus;%s", system_bus);
1582       break;
1583
1584     case G_BUS_TYPE_USER:
1585       session_bus = g_getenv ("DBUS_SESSION_BUS_ADDRESS");
1586       if (session_bus == NULL)
1587         ret = g_strdup_printf ("kernel:path=%s/kdbus;%s", g_get_user_runtime_dir (),
1588                                    get_session_address_platform_specific (&local_error));
1589       else
1590         ret = g_strdup_printf ("kernel:path=%s/kdbus;%s", g_get_user_runtime_dir (), session_bus);
1591       break;
1592
1593     case G_BUS_TYPE_STARTER:
1594       starter_bus = g_getenv ("DBUS_STARTER_BUS_TYPE");
1595       if (g_strcmp0 (starter_bus, "session") == 0)
1596         {
1597           ret = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SESSION, cancellable, &local_error);
1598           goto out;
1599         }
1600       else if (g_strcmp0 (starter_bus, "system") == 0)
1601         {
1602           ret = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SYSTEM, cancellable, &local_error);
1603           goto out;
1604         }
1605       else
1606         {
1607           if (starter_bus != NULL)
1608             {
1609               g_set_error (&local_error,
1610                            G_IO_ERROR,
1611                            G_IO_ERROR_FAILED,
1612                            _("Cannot determine bus address from DBUS_STARTER_BUS_TYPE environment variable"
1613                              " - unknown value '%s'"),
1614                            starter_bus);
1615             }
1616           else
1617             {
1618               g_set_error_literal (&local_error,
1619                                    G_IO_ERROR,
1620                                    G_IO_ERROR_FAILED,
1621                                    _("Cannot determine bus address because the DBUS_STARTER_BUS_TYPE environment "
1622                                      "variable is not set"));
1623             }
1624         }
1625       break;
1626
1627     default:
1628       g_set_error (&local_error,
1629                    G_IO_ERROR,
1630                    G_IO_ERROR_FAILED,
1631                    _("Unknown bus type %d"),
1632                    bus_type);
1633       break;
1634     }
1635
1636  out:
1637   if (G_UNLIKELY (_g_dbus_debug_address ()))
1638     {
1639       _g_dbus_debug_print_lock ();
1640       if (ret != NULL)
1641         {
1642           g_print ("GDBus-debug:Address: Returning address '%s' for bus type '%s'\n",
1643                    ret,
1644                    _g_dbus_enum_to_string (G_TYPE_BUS_TYPE, bus_type));
1645         }
1646       else
1647         {
1648           g_print ("GDBus-debug:Address: Cannot look-up address bus type '%s': %s\n",
1649                    _g_dbus_enum_to_string (G_TYPE_BUS_TYPE, bus_type),
1650                    local_error ? local_error->message : "");
1651         }
1652       _g_dbus_debug_print_unlock ();
1653     }
1654
1655   if (local_error != NULL)
1656     g_propagate_error (error, local_error);
1657
1658   return ret;
1659 }
1660
1661 /**
1662  * g_dbus_address_escape_value:
1663  * @string: an unescaped string to be included in a D-Bus address
1664  *     as the value in a key-value pair
1665  *
1666  * Escape @string so it can appear in a D-Bus address as the value
1667  * part of a key-value pair.
1668  *
1669  * For instance, if @string is "/run/bus-for-:0",
1670  * this function would return "/run/bus-for-%3A0",
1671  * which could be used in a D-Bus address like
1672  * "unix:nonce-tcp:host=127.0.0.1,port=42,noncefile=/run/bus-for-%3A0".
1673  *
1674  * Returns: (transfer full): a copy of @string with all
1675  *     non-optionally-escaped bytes escaped
1676  *
1677  * Since: 2.36
1678  */
1679 gchar *
1680 g_dbus_address_escape_value (const gchar *string)
1681 {
1682   GString *s;
1683   gsize i;
1684
1685   g_return_val_if_fail (string != NULL, NULL);
1686
1687   /* There will often not be anything needing escaping at all. */
1688   s = g_string_sized_new (strlen (string));
1689
1690   /* D-Bus address escaping is mostly the same as URI escaping... */
1691   g_string_append_uri_escaped (s, string, "\\/", FALSE);
1692
1693   /* ... but '~' is an unreserved character in URIs, but a
1694    * non-optionally-escaped character in D-Bus addresses. */
1695   for (i = 0; i < s->len; i++)
1696     {
1697       if (G_UNLIKELY (s->str[i] == '~'))
1698         {
1699           s->str[i] = '%';
1700           g_string_insert (s, i + 1, "7E");
1701           i += 2;
1702         }
1703     }
1704
1705   return g_string_free (s, FALSE);
1706 }