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