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