Silence a bunch of -Wunused-but-set-variable warnings
[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 "gdbusprivate.h"
41
42 #ifdef G_OS_UNIX
43 #include <gio/gunixsocketaddress.h>
44 #include <sys/wait.h>
45 #endif
46
47 #include "glibintl.h"
48
49 /**
50  * SECTION:gdbusaddress
51  * @title: D-Bus Addresses
52  * @short_description: D-Bus connection endpoints
53  * @include: gio/gio.h
54  *
55  * Routines for working with D-Bus addresses. A D-Bus address is a string
56  * like "unix:tmpdir=/tmp/my-app-name". The exact format of addresses
57  * is explained in detail in the <link linkend="http://dbus.freedesktop.org/doc/dbus-specification.html&num;addresses">D-Bus specification</link>.
58  */
59
60 static gchar *get_session_address_platform_specific (GError **error);
61
62 /* ---------------------------------------------------------------------------------------------------- */
63
64 /**
65  * g_dbus_is_address:
66  * @string: A string.
67  *
68  * Checks if @string is a D-Bus address.
69  *
70  * This doesn't check if @string is actually supported by #GDBusServer
71  * or #GDBusConnection - use g_dbus_is_supported_address() to do more
72  * checks.
73  *
74  * Returns: %TRUE if @string is a valid D-Bus address, %FALSE otherwise.
75  *
76  * Since: 2.26
77  */
78 gboolean
79 g_dbus_is_address (const gchar *string)
80 {
81   guint n;
82   gchar **a;
83   gboolean ret;
84
85   ret = FALSE;
86
87   g_return_val_if_fail (string != NULL, FALSE);
88
89   a = g_strsplit (string, ";", 0);
90   if (a[0] == NULL)
91     goto out;
92
93   for (n = 0; a[n] != NULL; n++)
94     {
95       if (!_g_dbus_address_parse_entry (a[n],
96                                         NULL,
97                                         NULL,
98                                         NULL))
99         goto out;
100     }
101
102   ret = TRUE;
103
104  out:
105   g_strfreev (a);
106   return ret;
107 }
108
109 static gboolean
110 is_valid_unix (const gchar  *address_entry,
111                GHashTable   *key_value_pairs,
112                GError      **error)
113 {
114   gboolean ret;
115   GList *keys;
116   GList *l;
117   const gchar *path;
118   const gchar *tmpdir;
119   const gchar *abstract;
120
121   ret = FALSE;
122   keys = NULL;
123   path = NULL;
124   tmpdir = NULL;
125   abstract = NULL;
126
127   keys = g_hash_table_get_keys (key_value_pairs);
128   for (l = keys; l != NULL; l = l->next)
129     {
130       const gchar *key = l->data;
131       if (g_strcmp0 (key, "path") == 0)
132         path = g_hash_table_lookup (key_value_pairs, key);
133       else if (g_strcmp0 (key, "tmpdir") == 0)
134         tmpdir = g_hash_table_lookup (key_value_pairs, key);
135       else if (g_strcmp0 (key, "abstract") == 0)
136         abstract = g_hash_table_lookup (key_value_pairs, key);
137       else
138         {
139           g_set_error (error,
140                        G_IO_ERROR,
141                        G_IO_ERROR_INVALID_ARGUMENT,
142                        _("Unsupported key `%s' in address entry `%s'"),
143                        key,
144                        address_entry);
145           goto out;
146         }
147     }
148
149   if (path != NULL)
150     {
151       if (tmpdir != NULL || abstract != NULL)
152         goto meaningless;
153     }
154   else if (tmpdir != NULL)
155     {
156       if (path != NULL || abstract != NULL)
157         goto meaningless;
158     }
159   else if (abstract != NULL)
160     {
161       if (path != NULL || tmpdir != NULL)
162         goto meaningless;
163     }
164   else
165     {
166       g_set_error (error,
167                    G_IO_ERROR,
168                    G_IO_ERROR_INVALID_ARGUMENT,
169                    _("Address `%s' is invalid (need exactly one of path, tmpdir or abstract keys)"),
170                    address_entry);
171       goto out;
172     }
173
174
175   ret= TRUE;
176   goto out;
177
178  meaningless:
179   g_set_error (error,
180                G_IO_ERROR,
181                G_IO_ERROR_INVALID_ARGUMENT,
182                _("Meaningless key/value pair combination in address entry `%s'"),
183                address_entry);
184
185  out:
186   g_list_free (keys);
187
188   return ret;
189 }
190
191 static gboolean
192 is_valid_nonce_tcp (const gchar  *address_entry,
193                     GHashTable   *key_value_pairs,
194                     GError      **error)
195 {
196   gboolean ret;
197   GList *keys;
198   GList *l;
199   const gchar *host;
200   const gchar *port;
201   const gchar *family;
202   const gchar *nonce_file;
203   gint port_num;
204   gchar *endp;
205
206   ret = FALSE;
207   keys = NULL;
208   host = NULL;
209   port = NULL;
210   family = NULL;
211   nonce_file = NULL;
212
213   keys = g_hash_table_get_keys (key_value_pairs);
214   for (l = keys; l != NULL; l = l->next)
215     {
216       const gchar *key = l->data;
217       if (g_strcmp0 (key, "host") == 0)
218         host = g_hash_table_lookup (key_value_pairs, key);
219       else if (g_strcmp0 (key, "port") == 0)
220         port = g_hash_table_lookup (key_value_pairs, key);
221       else if (g_strcmp0 (key, "family") == 0)
222         family = g_hash_table_lookup (key_value_pairs, key);
223       else if (g_strcmp0 (key, "noncefile") == 0)
224         nonce_file = g_hash_table_lookup (key_value_pairs, key);
225       else
226         {
227           g_set_error (error,
228                        G_IO_ERROR,
229                        G_IO_ERROR_INVALID_ARGUMENT,
230                        _("Unsupported key `%s' in address entry `%s'"),
231                        key,
232                        address_entry);
233           goto out;
234         }
235     }
236
237   if (port != NULL)
238     {
239       port_num = strtol (port, &endp, 10);
240       if ((*port == '\0' || *endp != '\0') || port_num < 0 || port_num >= 65536)
241         {
242           g_set_error (error,
243                        G_IO_ERROR,
244                        G_IO_ERROR_INVALID_ARGUMENT,
245                        _("Error in address `%s' - the port attribute is malformed"),
246                        address_entry);
247           goto out;
248         }
249     }
250
251   if (family != NULL && !(g_strcmp0 (family, "ipv4") == 0 || g_strcmp0 (family, "ipv6") == 0))
252     {
253       g_set_error (error,
254                    G_IO_ERROR,
255                    G_IO_ERROR_INVALID_ARGUMENT,
256                    _("Error in address `%s' - the family attribute is malformed"),
257                    address_entry);
258       goto out;
259     }
260
261   if (nonce_file == NULL)
262     {
263       g_set_error (error,
264                    G_IO_ERROR,
265                    G_IO_ERROR_INVALID_ARGUMENT,
266                    _("Error in address `%s' - missing noncefile attribute"),
267                    address_entry);
268       goto out;
269     }
270   if (host == NULL)
271     {
272       g_set_error (error,
273                    G_IO_ERROR,
274                    G_IO_ERROR_INVALID_ARGUMENT,
275                    _("Error in address `%s' - missing host attribute"),
276                    address_entry);
277       goto out;
278     }
279
280   ret= TRUE;
281
282  out:
283   g_list_free (keys);
284
285   return ret;
286 }
287
288 static gboolean
289 is_valid_tcp (const gchar  *address_entry,
290               GHashTable   *key_value_pairs,
291               GError      **error)
292 {
293   gboolean ret;
294   GList *keys;
295   GList *l;
296   const gchar *host;
297   const gchar *port;
298   const gchar *family;
299   gint port_num;
300   gchar *endp;
301
302   ret = FALSE;
303   keys = NULL;
304   host = NULL;
305   port = NULL;
306   family = NULL;
307
308   keys = g_hash_table_get_keys (key_value_pairs);
309   for (l = keys; l != NULL; l = l->next)
310     {
311       const gchar *key = l->data;
312       if (g_strcmp0 (key, "host") == 0)
313         host = g_hash_table_lookup (key_value_pairs, key);
314       else if (g_strcmp0 (key, "port") == 0)
315         port = g_hash_table_lookup (key_value_pairs, key);
316       else if (g_strcmp0 (key, "family") == 0)
317         family = g_hash_table_lookup (key_value_pairs, key);
318       else
319         {
320           g_set_error (error,
321                        G_IO_ERROR,
322                        G_IO_ERROR_INVALID_ARGUMENT,
323                        _("Unsupported key `%s' in address entry `%s'"),
324                        key,
325                        address_entry);
326           goto out;
327         }
328     }
329
330   if (port != NULL)
331     {
332       port_num = strtol (port, &endp, 10);
333       if ((*port == '\0' || *endp != '\0') || port_num < 0 || port_num >= 65536)
334         {
335           g_set_error (error,
336                        G_IO_ERROR,
337                        G_IO_ERROR_INVALID_ARGUMENT,
338                        _("Error in address `%s' - the port attribute is malformed"),
339                        address_entry);
340           goto out;
341         }
342     }
343
344   if (family != NULL && !(g_strcmp0 (family, "ipv4") == 0 || g_strcmp0 (family, "ipv6") == 0))
345     {
346       g_set_error (error,
347                    G_IO_ERROR,
348                    G_IO_ERROR_INVALID_ARGUMENT,
349                    _("Error in address `%s' - the family attribute is malformed"),
350                    address_entry);
351       goto out;
352     }
353
354   if (host == NULL)
355     {
356       g_set_error (error,
357                    G_IO_ERROR,
358                    G_IO_ERROR_INVALID_ARGUMENT,
359                    _("Error in address `%s' - missing host attribute"),
360                    address_entry);
361       goto out;
362     }
363
364   ret= TRUE;
365
366  out:
367   g_list_free (keys);
368
369   return ret;
370 }
371
372 /**
373  * g_dbus_is_supported_address:
374  * @string: A string.
375  * @error: Return location for error or %NULL.
376  *
377  * Like g_dbus_is_address() but also checks if the library suppors the
378  * transports in @string and that key/value pairs for each transport
379  * are valid.
380  *
381  * Returns: %TRUE if @string is a valid D-Bus address that is
382  * supported by this library, %FALSE if @error is set.
383  *
384  * Since: 2.26
385  */
386 gboolean
387 g_dbus_is_supported_address (const gchar  *string,
388                              GError      **error)
389 {
390   guint n;
391   gchar **a;
392   gboolean ret;
393
394   ret = FALSE;
395
396   g_return_val_if_fail (string != NULL, FALSE);
397   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
398
399   a = g_strsplit (string, ";", 0);
400   for (n = 0; a[n] != NULL; n++)
401     {
402       gchar *transport_name;
403       GHashTable *key_value_pairs;
404       gboolean supported;
405
406       if (!_g_dbus_address_parse_entry (a[n],
407                                         &transport_name,
408                                         &key_value_pairs,
409                                         error))
410         goto out;
411
412       supported = FALSE;
413       if (g_strcmp0 (transport_name, "unix") == 0)
414         supported = is_valid_unix (a[n], key_value_pairs, error);
415       else if (g_strcmp0 (transport_name, "tcp") == 0)
416         supported = is_valid_tcp (a[n], key_value_pairs, error);
417       else if (g_strcmp0 (transport_name, "nonce-tcp") == 0)
418         supported = is_valid_nonce_tcp (a[n], key_value_pairs, error);
419       else if (g_strcmp0 (a[n], "autolaunch:") == 0)
420         supported = TRUE;
421
422       g_free (transport_name);
423       g_hash_table_unref (key_value_pairs);
424
425       if (!supported)
426         goto out;
427     }
428
429   ret = TRUE;
430
431  out:
432   g_strfreev (a);
433
434   g_assert (ret || (!ret && (error == NULL || *error != NULL)));
435
436   return ret;
437 }
438
439 gboolean
440 _g_dbus_address_parse_entry (const gchar  *address_entry,
441                              gchar       **out_transport_name,
442                              GHashTable  **out_key_value_pairs,
443                              GError      **error)
444 {
445   gboolean ret;
446   GHashTable *key_value_pairs;
447   gchar *transport_name;
448   gchar **kv_pairs;
449   const gchar *s;
450   guint n;
451
452   ret = FALSE;
453   kv_pairs = NULL;
454   transport_name = NULL;
455   key_value_pairs = NULL;
456
457   s = strchr (address_entry, ':');
458   if (s == NULL)
459     {
460       g_set_error (error,
461                    G_IO_ERROR,
462                    G_IO_ERROR_INVALID_ARGUMENT,
463                    _("Address element `%s', does not contain a colon (:)"),
464                    address_entry);
465       goto out;
466     }
467
468   transport_name = g_strndup (address_entry, s - address_entry);
469   key_value_pairs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
470
471   kv_pairs = g_strsplit (s + 1, ",", 0);
472   for (n = 0; kv_pairs != NULL && kv_pairs[n] != NULL; n++)
473     {
474       const gchar *kv_pair = kv_pairs[n];
475       gchar *key;
476       gchar *value;
477
478       s = strchr (kv_pair, '=');
479       if (s == NULL)
480         {
481           g_set_error (error,
482                        G_IO_ERROR,
483                        G_IO_ERROR_INVALID_ARGUMENT,
484                        _("Key/Value pair %d, `%s', in address element `%s', does not contain an equal sign"),
485                        n,
486                        kv_pair,
487                        address_entry);
488           goto out;
489         }
490
491       key = g_uri_unescape_segment (kv_pair, s, NULL);
492       value = g_uri_unescape_segment (s + 1, kv_pair + strlen (kv_pair), NULL);
493       if (key == NULL || value == NULL)
494         {
495           g_set_error (error,
496                        G_IO_ERROR,
497                        G_IO_ERROR_INVALID_ARGUMENT,
498                        _("Error unescaping key or value in Key/Value pair %d, `%s', in address element `%s'"),
499                        n,
500                        kv_pair,
501                        address_entry);
502           g_free (key);
503           g_free (value);
504           goto out;
505         }
506       g_hash_table_insert (key_value_pairs, key, value);
507     }
508
509   ret = TRUE;
510
511 out:
512   g_strfreev (kv_pairs);
513   if (ret)
514     {
515       if (out_transport_name != NULL)
516         *out_transport_name = transport_name;
517       else
518         g_free (transport_name);
519       if (out_key_value_pairs != NULL)
520         *out_key_value_pairs = key_value_pairs;
521       else if (key_value_pairs != NULL)
522         g_hash_table_unref (key_value_pairs);
523     }
524   else
525     {
526       g_free (transport_name);
527       if (key_value_pairs != NULL)
528         g_hash_table_unref (key_value_pairs);
529     }
530   return ret;
531 }
532
533 /* ---------------------------------------------------------------------------------------------------- */
534
535 static GIOStream *
536 g_dbus_address_try_connect_one (const gchar   *address_entry,
537                                 gchar        **out_guid,
538                                 GCancellable  *cancellable,
539                                 GError       **error);
540
541 /* TODO: Declare an extension point called GDBusTransport (or similar)
542  * and move code below to extensions implementing said extension
543  * point. That way we can implement a D-Bus transport over X11 without
544  * making libgio link to libX11...
545  */
546 static GIOStream *
547 g_dbus_address_connect (const gchar   *address_entry,
548                         const gchar   *transport_name,
549                         GHashTable    *key_value_pairs,
550                         GCancellable  *cancellable,
551                         GError       **error)
552 {
553   GIOStream *ret;
554   GSocketConnectable *connectable;
555   const gchar *nonce_file;
556
557   connectable = NULL;
558   ret = NULL;
559   nonce_file = NULL;
560
561   if (FALSE)
562     {
563     }
564 #ifdef G_OS_UNIX
565   else if (g_strcmp0 (transport_name, "unix") == 0)
566     {
567       const gchar *path;
568       const gchar *abstract;
569       path = g_hash_table_lookup (key_value_pairs, "path");
570       abstract = g_hash_table_lookup (key_value_pairs, "abstract");
571       if ((path == NULL && abstract == NULL) || (path != NULL && abstract != NULL))
572         {
573           g_set_error (error,
574                        G_IO_ERROR,
575                        G_IO_ERROR_INVALID_ARGUMENT,
576                        _("Error in address `%s' - the unix transport requires exactly one of the "
577                          "keys `path' or `abstract' to be set"),
578                        address_entry);
579         }
580       else if (path != NULL)
581         {
582           connectable = G_SOCKET_CONNECTABLE (g_unix_socket_address_new (path));
583         }
584       else if (abstract != NULL)
585         {
586           connectable = G_SOCKET_CONNECTABLE (g_unix_socket_address_new_with_type (abstract,
587                                                                                    -1,
588                                                                                    G_UNIX_SOCKET_ADDRESS_ABSTRACT));
589         }
590       else
591         {
592           g_assert_not_reached ();
593         }
594     }
595 #endif
596   else if (g_strcmp0 (transport_name, "tcp") == 0 || g_strcmp0 (transport_name, "nonce-tcp") == 0)
597     {
598       const gchar *s;
599       const gchar *host;
600       guint port;
601       gchar *endp;
602       gboolean is_nonce;
603
604       is_nonce = (g_strcmp0 (transport_name, "nonce-tcp") == 0);
605
606       host = g_hash_table_lookup (key_value_pairs, "host");
607       if (host == NULL)
608         {
609           g_set_error (error,
610                        G_IO_ERROR,
611                        G_IO_ERROR_INVALID_ARGUMENT,
612                        _("Error in address `%s' - the host attribute is missing or malformed"),
613                        address_entry);
614           goto out;
615         }
616
617       s = g_hash_table_lookup (key_value_pairs, "port");
618       if (s == NULL)
619         s = "0";
620       port = strtol (s, &endp, 10);
621       if ((*s == '\0' || *endp != '\0') || port < 0 || port >= 65536)
622         {
623           g_set_error (error,
624                        G_IO_ERROR,
625                        G_IO_ERROR_INVALID_ARGUMENT,
626                        _("Error in address `%s' - the port attribute is missing or malformed"),
627                        address_entry);
628           goto out;
629         }
630
631
632       if (is_nonce)
633         {
634           nonce_file = g_hash_table_lookup (key_value_pairs, "noncefile");
635           if (nonce_file == NULL)
636             {
637               g_set_error (error,
638                            G_IO_ERROR,
639                            G_IO_ERROR_INVALID_ARGUMENT,
640                            _("Error in address `%s' - the noncefile attribute is missing or malformed"),
641                            address_entry);
642               goto out;
643             }
644         }
645
646       /* TODO: deal with family key/value-pair */
647       connectable = g_network_address_new (host, port);
648     }
649   else if (g_strcmp0 (address_entry, "autolaunch:") == 0)
650     {
651       gchar *autolaunch_address;
652       autolaunch_address = get_session_address_platform_specific (error);
653       if (autolaunch_address != NULL)
654         {
655           ret = g_dbus_address_try_connect_one (autolaunch_address, NULL, cancellable, error);
656           g_free (autolaunch_address);
657           goto out;
658         }
659       else
660         {
661           g_prefix_error (error, _("Error auto-launching: "));
662         }
663     }
664   else
665     {
666       g_set_error (error,
667                    G_IO_ERROR,
668                    G_IO_ERROR_INVALID_ARGUMENT,
669                    _("Unknown or unsupported transport `%s' for address `%s'"),
670                    transport_name,
671                    address_entry);
672     }
673
674   if (connectable != NULL)
675     {
676       GSocketClient *client;
677       GSocketConnection *connection;
678
679       g_assert (ret == NULL);
680       client = g_socket_client_new ();
681       connection = g_socket_client_connect (client,
682                                             connectable,
683                                             cancellable,
684                                             error);
685       g_object_unref (connectable);
686       g_object_unref (client);
687       if (connection == NULL)
688         goto out;
689
690       ret = G_IO_STREAM (connection);
691
692       if (nonce_file != NULL)
693         {
694           gchar nonce_contents[16 + 1];
695           size_t num_bytes_read;
696           FILE *f;
697
698           /* be careful to read only 16 bytes - we also check that the file is only 16 bytes long */
699           f = fopen (nonce_file, "rb");
700           if (f == NULL)
701             {
702               g_set_error (error,
703                            G_IO_ERROR,
704                            G_IO_ERROR_INVALID_ARGUMENT,
705                            _("Error opening nonce file `%s': %s"),
706                            nonce_file,
707                            g_strerror (errno));
708               g_object_unref (ret);
709               ret = NULL;
710               goto out;
711             }
712           num_bytes_read = fread (nonce_contents,
713                                   sizeof (gchar),
714                                   16 + 1,
715                                   f);
716           if (num_bytes_read != 16)
717             {
718               if (num_bytes_read == 0)
719                 {
720                   g_set_error (error,
721                                G_IO_ERROR,
722                                G_IO_ERROR_INVALID_ARGUMENT,
723                                _("Error reading from nonce file `%s': %s"),
724                                nonce_file,
725                                g_strerror (errno));
726                 }
727               else
728                 {
729                   g_set_error (error,
730                                G_IO_ERROR,
731                                G_IO_ERROR_INVALID_ARGUMENT,
732                                _("Error reading from nonce file `%s', expected 16 bytes, got %d"),
733                                nonce_file,
734                                (gint) num_bytes_read);
735                 }
736               g_object_unref (ret);
737               ret = NULL;
738               fclose (f);
739               goto out;
740             }
741           fclose (f);
742
743           if (!g_output_stream_write_all (g_io_stream_get_output_stream (ret),
744                                           nonce_contents,
745                                           16,
746                                           NULL,
747                                           cancellable,
748                                           error))
749             {
750               g_prefix_error (error, _("Error writing contents of nonce file `%s' to stream:"), nonce_file);
751               g_object_unref (ret);
752               ret = NULL;
753               goto out;
754             }
755         }
756     }
757
758  out:
759
760   return ret;
761 }
762
763 static GIOStream *
764 g_dbus_address_try_connect_one (const gchar   *address_entry,
765                                 gchar        **out_guid,
766                                 GCancellable  *cancellable,
767                                 GError       **error)
768 {
769   GIOStream *ret;
770   GHashTable *key_value_pairs;
771   gchar *transport_name;
772   const gchar *guid;
773
774   ret = NULL;
775   transport_name = NULL;
776   key_value_pairs = NULL;
777
778   if (!_g_dbus_address_parse_entry (address_entry,
779                                     &transport_name,
780                                     &key_value_pairs,
781                                     error))
782     goto out;
783
784   ret = g_dbus_address_connect (address_entry,
785                                 transport_name,
786                                 key_value_pairs,
787                                 cancellable,
788                                 error);
789   if (ret == NULL)
790     goto out;
791
792   guid = g_hash_table_lookup (key_value_pairs, "guid");
793   if (guid != NULL && out_guid != NULL)
794     *out_guid = g_strdup (guid);
795
796 out:
797   g_free (transport_name);
798   if (key_value_pairs != NULL)
799     g_hash_table_unref (key_value_pairs);
800   return ret;
801 }
802
803
804 /* ---------------------------------------------------------------------------------------------------- */
805
806 typedef struct {
807   gchar *address;
808   GIOStream *stream;
809   gchar *guid;
810 } GetStreamData;
811
812 static void
813 get_stream_data_free (GetStreamData *data)
814 {
815   g_free (data->address);
816   if (data->stream != NULL)
817     g_object_unref (data->stream);
818   g_free (data->guid);
819   g_free (data);
820 }
821
822 static void
823 get_stream_thread_func (GSimpleAsyncResult *res,
824                         GObject            *object,
825                         GCancellable       *cancellable)
826 {
827   GetStreamData *data;
828   GError *error;
829
830   data = g_simple_async_result_get_op_res_gpointer (res);
831
832   error = NULL;
833   data->stream = g_dbus_address_get_stream_sync (data->address,
834                                                  &data->guid,
835                                                  cancellable,
836                                                  &error);
837   if (data->stream == NULL)
838     g_simple_async_result_take_error (res, error);
839 }
840
841 /**
842  * g_dbus_address_get_stream:
843  * @address: A valid D-Bus address.
844  * @cancellable: A #GCancellable or %NULL.
845  * @callback: A #GAsyncReadyCallback to call when the request is satisfied.
846  * @user_data: Data to pass to @callback.
847  *
848  * Asynchronously connects to an endpoint specified by @address and
849  * sets up the connection so it is in a state to run the client-side
850  * of the D-Bus authentication conversation.
851  *
852  * When the operation is finished, @callback will be invoked. You can
853  * then call g_dbus_address_get_stream_finish() to get the result of
854  * the operation.
855  *
856  * This is an asynchronous failable function. See
857  * g_dbus_address_get_stream_sync() for the synchronous version.
858  *
859  * Since: 2.26
860  */
861 void
862 g_dbus_address_get_stream (const gchar         *address,
863                            GCancellable        *cancellable,
864                            GAsyncReadyCallback  callback,
865                            gpointer             user_data)
866 {
867   GSimpleAsyncResult *res;
868   GetStreamData *data;
869
870   g_return_if_fail (address != NULL);
871
872   res = g_simple_async_result_new (NULL,
873                                    callback,
874                                    user_data,
875                                    g_dbus_address_get_stream);
876   data = g_new0 (GetStreamData, 1);
877   data->address = g_strdup (address);
878   g_simple_async_result_set_op_res_gpointer (res,
879                                              data,
880                                              (GDestroyNotify) get_stream_data_free);
881   g_simple_async_result_run_in_thread (res,
882                                        get_stream_thread_func,
883                                        G_PRIORITY_DEFAULT,
884                                        cancellable);
885   g_object_unref (res);
886 }
887
888 /**
889  * g_dbus_address_get_stream_finish:
890  * @res: A #GAsyncResult obtained from the GAsyncReadyCallback passed to g_dbus_address_get_stream().
891  * @out_guid: %NULL or return location to store the GUID extracted from @address, if any.
892  * @error: Return location for error or %NULL.
893  *
894  * Finishes an operation started with g_dbus_address_get_stream().
895  *
896  * Returns: (transfer full): A #GIOStream or %NULL if @error is set.
897  *
898  * Since: 2.26
899  */
900 GIOStream *
901 g_dbus_address_get_stream_finish (GAsyncResult        *res,
902                                   gchar              **out_guid,
903                                   GError             **error)
904 {
905   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
906   GetStreamData *data;
907   GIOStream *ret;
908
909   g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
910   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
911
912   g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_dbus_address_get_stream);
913
914   ret = NULL;
915
916   data = g_simple_async_result_get_op_res_gpointer (simple);
917   if (g_simple_async_result_propagate_error (simple, error))
918     goto out;
919
920   ret = g_object_ref (data->stream);
921   if (out_guid != NULL)
922     *out_guid = g_strdup (data->guid);
923
924  out:
925   return ret;
926 }
927
928 /**
929  * g_dbus_address_get_stream_sync:
930  * @address: A valid D-Bus address.
931  * @out_guid: %NULL or return location to store the GUID extracted from @address, if any.
932  * @cancellable: A #GCancellable or %NULL.
933  * @error: Return location for error or %NULL.
934  *
935  * Synchronously connects to an endpoint specified by @address and
936  * sets up the connection so it is in a state to run the client-side
937  * of the D-Bus authentication conversation.
938  *
939  * This is a synchronous failable function. See
940  * g_dbus_address_get_stream() for the asynchronous version.
941  *
942  * Returns: (transfer full): A #GIOStream or %NULL if @error is set.
943  *
944  * Since: 2.26
945  */
946 GIOStream *
947 g_dbus_address_get_stream_sync (const gchar   *address,
948                                 gchar        **out_guid,
949                                 GCancellable  *cancellable,
950                                 GError       **error)
951 {
952   GIOStream *ret;
953   gchar **addr_array;
954   guint n;
955   GError *last_error;
956
957   g_return_val_if_fail (address != NULL, NULL);
958   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
959
960   ret = NULL;
961   last_error = NULL;
962
963   addr_array = g_strsplit (address, ";", 0);
964   if (addr_array[0] == NULL)
965     {
966       last_error = g_error_new_literal (G_IO_ERROR,
967                                         G_IO_ERROR_INVALID_ARGUMENT,
968                                         _("The given address is empty"));
969       goto out;
970     }
971
972   for (n = 0; addr_array != NULL && addr_array[n] != NULL; n++)
973     {
974       const gchar *addr = addr_array[n];
975       GError *this_error;
976
977       this_error = NULL;
978       ret = g_dbus_address_try_connect_one (addr,
979                                             out_guid,
980                                             cancellable,
981                                             &this_error);
982       if (ret != NULL)
983         {
984           goto out;
985         }
986       else
987         {
988           g_assert (this_error != NULL);
989           if (last_error != NULL)
990             g_error_free (last_error);
991           last_error = this_error;
992         }
993     }
994
995  out:
996   if (ret != NULL)
997     {
998       if (last_error != NULL)
999         g_error_free (last_error);
1000     }
1001   else
1002     {
1003       g_assert (last_error != NULL);
1004       g_propagate_error (error, last_error);
1005     }
1006
1007   g_strfreev (addr_array);
1008   return ret;
1009 }
1010
1011 /* ---------------------------------------------------------------------------------------------------- */
1012
1013 #ifdef G_OS_UNIX
1014 static gchar *
1015 get_session_address_dbus_launch (GError **error)
1016 {
1017   gchar *ret;
1018   gchar *machine_id;
1019   gchar *command_line;
1020   gchar *launch_stdout;
1021   gchar *launch_stderr;
1022   gint exit_status;
1023   gchar *old_dbus_verbose;
1024   gboolean restore_dbus_verbose;
1025
1026   ret = NULL;
1027   machine_id = NULL;
1028   command_line = NULL;
1029   launch_stdout = NULL;
1030   launch_stderr = NULL;
1031   restore_dbus_verbose = FALSE;
1032   old_dbus_verbose = NULL;
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       g_prefix_error (error, _("Error spawning command line `%s': "), command_line);
1075       goto out;
1076     }
1077
1078   if (!WIFEXITED (exit_status))
1079     {
1080       gchar *escaped_stderr;
1081       escaped_stderr = g_strescape (launch_stderr, "");
1082       g_set_error (error,
1083                    G_IO_ERROR,
1084                    G_IO_ERROR_FAILED,
1085                    _("Abnormal program termination spawning command line `%s': %s"),
1086                    command_line,
1087                    escaped_stderr);
1088       g_free (escaped_stderr);
1089       goto out;
1090     }
1091
1092   if (WEXITSTATUS (exit_status) != 0)
1093     {
1094       gchar *escaped_stderr;
1095       escaped_stderr = g_strescape (launch_stderr, "");
1096       g_set_error (error,
1097                    G_IO_ERROR,
1098                    G_IO_ERROR_FAILED,
1099                    _("Command line `%s' exited with non-zero exit status %d: %s"),
1100                    command_line,
1101                    WEXITSTATUS (exit_status),
1102                    escaped_stderr);
1103       g_free (escaped_stderr);
1104       goto out;
1105     }
1106
1107   /* From the dbus-launch(1) man page:
1108    *
1109    *   --binary-syntax Write to stdout a nul-terminated bus address,
1110    *   then the bus PID as a binary integer of size sizeof(pid_t),
1111    *   then the bus X window ID as a binary integer of size
1112    *   sizeof(long).  Integers are in the machine's byte order, not
1113    *   network byte order or any other canonical byte order.
1114    */
1115   ret = g_strdup (launch_stdout);
1116
1117  out:
1118   if (G_UNLIKELY (_g_dbus_debug_address ()))
1119     {
1120       gchar *s;
1121       _g_dbus_debug_print_lock ();
1122       g_print ("GDBus-debug:Address: dbus-launch output:");
1123       if (launch_stdout != NULL)
1124         {
1125           s = _g_dbus_hexdump (launch_stdout, strlen (launch_stdout) + 1 + sizeof (pid_t) + sizeof (long), 2);
1126           g_print ("\n%s", s);
1127           g_free (s);
1128         }
1129       else
1130         {
1131           g_print (" (none)\n");
1132         }
1133       g_print ("GDBus-debug:Address: dbus-launch stderr output:");
1134       if (launch_stderr != NULL)
1135         g_print ("\n%s", launch_stderr);
1136       else
1137         g_print (" (none)\n");
1138       _g_dbus_debug_print_unlock ();
1139     }
1140
1141   g_free (machine_id);
1142   g_free (command_line);
1143   g_free (launch_stdout);
1144   g_free (launch_stderr);
1145   if (G_UNLIKELY (restore_dbus_verbose))
1146     {
1147       if (old_dbus_verbose != NULL)
1148         g_setenv ("DBUS_VERBOSE", old_dbus_verbose, TRUE);
1149       else
1150         g_unsetenv ("DBUS_VERBOSE");
1151     }
1152   g_free (old_dbus_verbose);
1153   return ret;
1154 }
1155 #endif
1156
1157 /* ---------------------------------------------------------------------------------------------------- */
1158
1159 static gchar *
1160 get_session_address_platform_specific (GError **error)
1161 {
1162   gchar *ret;
1163 #ifdef G_OS_UNIX
1164   /* need to handle OS X in a different way since `dbus-launch --autolaunch' probably won't work there */
1165   ret = get_session_address_dbus_launch (error);
1166 #else
1167   /* TODO: implement for UNIX, Win32 and OS X */
1168   ret = NULL;
1169   g_set_error (error,
1170                G_IO_ERROR,
1171                G_IO_ERROR_FAILED,
1172                _("Cannot determine session bus address (not implemented for this OS)"));
1173 #endif
1174   return ret;
1175 }
1176
1177 /* ---------------------------------------------------------------------------------------------------- */
1178
1179 /**
1180  * g_dbus_address_get_for_bus_sync:
1181  * @bus_type: A #GBusType.
1182  * @cancellable: A #GCancellable or %NULL.
1183  * @error: Return location for error or %NULL.
1184  *
1185  * Synchronously looks up the D-Bus address for the well-known message
1186  * bus instance specified by @bus_type. This may involve using various
1187  * platform specific mechanisms.
1188  *
1189  * Returns: A valid D-Bus address string for @bus_type or %NULL if @error is set.
1190  *
1191  * Since: 2.26
1192  */
1193 gchar *
1194 g_dbus_address_get_for_bus_sync (GBusType       bus_type,
1195                                  GCancellable  *cancellable,
1196                                  GError       **error)
1197 {
1198   gchar *ret;
1199   const gchar *starter_bus;
1200   GError *local_error;
1201
1202   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1203
1204   ret = NULL;
1205   local_error = NULL;
1206
1207   if (G_UNLIKELY (_g_dbus_debug_address ()))
1208     {
1209       guint n;
1210       _g_dbus_debug_print_lock ();
1211       g_print ("GDBus-debug:Address: In g_dbus_address_get_for_bus_sync() for bus type `%s'\n",
1212                _g_dbus_enum_to_string (G_TYPE_BUS_TYPE, bus_type));
1213       for (n = 0; n < 3; n++)
1214         {
1215           const gchar *k;
1216           const gchar *v;
1217           switch (n)
1218             {
1219             case 0: k = "DBUS_SESSION_BUS_ADDRESS"; break;
1220             case 1: k = "DBUS_SYSTEM_BUS_ADDRESS"; break;
1221             case 2: k = "DBUS_STARTER_BUS_TYPE"; break;
1222             default: g_assert_not_reached ();
1223             }
1224           v = g_getenv (k);
1225           g_print ("GDBus-debug:Address: env var %s", k);
1226           if (v != NULL)
1227             g_print ("=`%s'\n", v);
1228           else
1229             g_print (" is not set\n");
1230         }
1231       _g_dbus_debug_print_unlock ();
1232     }
1233
1234   switch (bus_type)
1235     {
1236     case G_BUS_TYPE_SYSTEM:
1237       ret = g_strdup (g_getenv ("DBUS_SYSTEM_BUS_ADDRESS"));
1238       if (ret == NULL)
1239         {
1240           ret = g_strdup ("unix:path=/var/run/dbus/system_bus_socket");
1241         }
1242       break;
1243
1244     case G_BUS_TYPE_SESSION:
1245       ret = g_strdup (g_getenv ("DBUS_SESSION_BUS_ADDRESS"));
1246       if (ret == NULL)
1247         {
1248           ret = get_session_address_platform_specific (&local_error);
1249         }
1250       break;
1251
1252     case G_BUS_TYPE_STARTER:
1253       starter_bus = g_getenv ("DBUS_STARTER_BUS_TYPE");
1254       if (g_strcmp0 (starter_bus, "session") == 0)
1255         {
1256           ret = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SESSION, cancellable, &local_error);
1257           goto out;
1258         }
1259       else if (g_strcmp0 (starter_bus, "system") == 0)
1260         {
1261           ret = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SYSTEM, cancellable, &local_error);
1262           goto out;
1263         }
1264       else
1265         {
1266           if (starter_bus != NULL)
1267             {
1268               g_set_error (&local_error,
1269                            G_IO_ERROR,
1270                            G_IO_ERROR_FAILED,
1271                            _("Cannot determine bus address from DBUS_STARTER_BUS_TYPE environment variable"
1272                              " - unknown value `%s'"),
1273                            starter_bus);
1274             }
1275           else
1276             {
1277               g_set_error_literal (&local_error,
1278                                    G_IO_ERROR,
1279                                    G_IO_ERROR_FAILED,
1280                                    _("Cannot determine bus address because the DBUS_STARTER_BUS_TYPE environment "
1281                                      "variable is not set"));
1282             }
1283         }
1284       break;
1285
1286     default:
1287       g_set_error (&local_error,
1288                    G_IO_ERROR,
1289                    G_IO_ERROR_FAILED,
1290                    _("Unknown bus type %d"),
1291                    bus_type);
1292       break;
1293     }
1294
1295  out:
1296   if (G_UNLIKELY (_g_dbus_debug_address ()))
1297     {
1298       _g_dbus_debug_print_lock ();
1299       if (ret != NULL)
1300         {
1301           g_print ("GDBus-debug:Address: Returning address `%s' for bus type `%s'\n",
1302                    ret,
1303                    _g_dbus_enum_to_string (G_TYPE_BUS_TYPE, bus_type));
1304         }
1305       else
1306         {
1307           g_print ("GDBus-debug:Address: Cannot look-up address bus type `%s': %s\n",
1308                    _g_dbus_enum_to_string (G_TYPE_BUS_TYPE, bus_type),
1309                    local_error->message);
1310         }
1311       _g_dbus_debug_print_unlock ();
1312     }
1313
1314   if (local_error != NULL)
1315     g_propagate_error (error, local_error);
1316
1317   return ret;
1318 }