Add "Since: 2.26" to all new GDBus API
[platform/upstream/glib.git] / gio / gdbusaddress.c
1 /* GDBus - GLib D-Bus Library
2  *
3  * Copyright (C) 2008-2009 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
27 #include <glib/gi18n.h>
28
29 #include "gdbusutils.h"
30 #include "gdbusaddress.h"
31 #include "gdbuserror.h"
32 #include "gioenumtypes.h"
33 #include "gdbusprivate.h"
34
35 #ifdef G_OS_UNIX
36 #include <gio/gunixsocketaddress.h>
37 #endif
38
39 /**
40  * SECTION:gdbusaddress
41  * @title: D-Bus Addresses
42  * @short_description: D-Bus connection endpoints
43  * @include: gio/gio.h
44  *
45  * Routines for working with D-Bus addresses.
46  */
47
48 /* ---------------------------------------------------------------------------------------------------- */
49
50 /**
51  * g_dbus_is_address:
52  * @string: A string.
53  *
54  * Checks if @string is a D-Bus address.
55  *
56  * This doesn't check if @string is actually supported by #GDBusServer
57  * or #GDBusConnection - use g_dbus_is_supported_address() to do more
58  * checks.
59  *
60  * Returns: %TRUE if @string is a valid D-Bus address, %FALSE otherwise.
61  *
62  * Since: 2.26
63  */
64 gboolean
65 g_dbus_is_address (const gchar *string)
66 {
67   guint n;
68   gchar **a;
69   gboolean ret;
70
71   ret = FALSE;
72
73   g_return_val_if_fail (string != NULL, FALSE);
74
75   a = g_strsplit (string, ";", 0);
76   for (n = 0; a[n] != NULL; n++)
77     {
78       if (!_g_dbus_address_parse_entry (a[n],
79                                         NULL,
80                                         NULL,
81                                         NULL))
82         goto out;
83     }
84
85   ret = TRUE;
86
87  out:
88   g_strfreev (a);
89   return ret;
90 }
91
92 static gboolean
93 is_valid_unix (const gchar  *address_entry,
94                GHashTable   *key_value_pairs,
95                GError      **error)
96 {
97   gboolean ret;
98   GList *keys;
99   GList *l;
100   const gchar *path;
101   const gchar *tmpdir;
102   const gchar *abstract;
103
104   ret = FALSE;
105   keys = NULL;
106   path = NULL;
107   tmpdir = NULL;
108   abstract = NULL;
109
110   keys = g_hash_table_get_keys (key_value_pairs);
111   for (l = keys; l != NULL; l = l->next)
112     {
113       const gchar *key = l->data;
114       if (g_strcmp0 (key, "path") == 0)
115         path = g_hash_table_lookup (key_value_pairs, key);
116       else if (g_strcmp0 (key, "tmpdir") == 0)
117         tmpdir = g_hash_table_lookup (key_value_pairs, key);
118       else if (g_strcmp0 (key, "abstract") == 0)
119         abstract = g_hash_table_lookup (key_value_pairs, key);
120       else
121         {
122           g_set_error (error,
123                        G_IO_ERROR,
124                        G_IO_ERROR_INVALID_ARGUMENT,
125                        _("Unsupported key `%s' in address entry `%s'"),
126                        key,
127                        address_entry);
128           goto out;
129         }
130     }
131
132   if (path != NULL)
133     {
134       if (tmpdir != NULL || abstract != NULL)
135         goto meaningless;
136       /* TODO: validate path */
137     }
138   else if (tmpdir != NULL)
139     {
140       if (path != NULL || abstract != NULL)
141         goto meaningless;
142       /* TODO: validate tmpdir */
143     }
144   else if (abstract != NULL)
145     {
146       if (path != NULL || tmpdir != NULL)
147         goto meaningless;
148       /* TODO: validate abstract */
149     }
150   else
151     {
152       g_set_error (error,
153                    G_IO_ERROR,
154                    G_IO_ERROR_INVALID_ARGUMENT,
155                    _("Address `%s' is invalid (need exactly one of path, tmpdir or abstract keys"),
156                    address_entry);
157       goto out;
158     }
159
160
161   ret= TRUE;
162   goto out;
163
164  meaningless:
165   g_set_error (error,
166                G_IO_ERROR,
167                G_IO_ERROR_INVALID_ARGUMENT,
168                _("Meaningless key/value pair combination in address entry `%s'"),
169                address_entry);
170
171  out:
172   g_list_free (keys);
173
174   return ret;
175 }
176
177 static gboolean
178 is_valid_nonce_tcp (const gchar  *address_entry,
179                     GHashTable   *key_value_pairs,
180                     GError      **error)
181 {
182   gboolean ret;
183   GList *keys;
184   GList *l;
185   const gchar *host;
186   const gchar *port;
187   const gchar *family;
188   const gchar *nonce_file;
189   gint port_num;
190   gchar *endp;
191
192   ret = FALSE;
193   keys = NULL;
194   host = NULL;
195   port = NULL;
196   family = NULL;
197   nonce_file = NULL;
198
199   keys = g_hash_table_get_keys (key_value_pairs);
200   for (l = keys; l != NULL; l = l->next)
201     {
202       const gchar *key = l->data;
203       if (g_strcmp0 (key, "host") == 0)
204         host = g_hash_table_lookup (key_value_pairs, key);
205       else if (g_strcmp0 (key, "port") == 0)
206         port = g_hash_table_lookup (key_value_pairs, key);
207       else if (g_strcmp0 (key, "family") == 0)
208         family = g_hash_table_lookup (key_value_pairs, key);
209       else if (g_strcmp0 (key, "noncefile") == 0)
210         nonce_file = g_hash_table_lookup (key_value_pairs, key);
211       else
212         {
213           g_set_error (error,
214                        G_IO_ERROR,
215                        G_IO_ERROR_INVALID_ARGUMENT,
216                        _("Unsupported key `%s' in address entry `%s'"),
217                        key,
218                        address_entry);
219           goto out;
220         }
221     }
222
223   if (port != NULL)
224     {
225       port_num = strtol (port, &endp, 10);
226       if ((*port == '\0' || *endp != '\0') || port_num < 0 || port_num >= 65536)
227         {
228           g_set_error (error,
229                        G_IO_ERROR,
230                        G_IO_ERROR_INVALID_ARGUMENT,
231                        _("Error in address `%s' - the port attribute is malformed"),
232                        address_entry);
233           goto out;
234         }
235     }
236
237   if (family != NULL && !(g_strcmp0 (family, "ipv4") == 0 || g_strcmp0 (family, "ipv6") == 0))
238     {
239       g_set_error (error,
240                    G_IO_ERROR,
241                    G_IO_ERROR_INVALID_ARGUMENT,
242                    _("Error in address `%s' - the family attribute is malformed"),
243                    address_entry);
244       goto out;
245     }
246
247   ret= TRUE;
248
249  out:
250   g_list_free (keys);
251
252   return ret;
253 }
254
255 static gboolean
256 is_valid_tcp (const gchar  *address_entry,
257               GHashTable   *key_value_pairs,
258               GError      **error)
259 {
260   gboolean ret;
261   GList *keys;
262   GList *l;
263   const gchar *host;
264   const gchar *port;
265   const gchar *family;
266   gint port_num;
267   gchar *endp;
268
269   ret = FALSE;
270   keys = NULL;
271   host = NULL;
272   port = NULL;
273   family = NULL;
274
275   keys = g_hash_table_get_keys (key_value_pairs);
276   for (l = keys; l != NULL; l = l->next)
277     {
278       const gchar *key = l->data;
279       if (g_strcmp0 (key, "host") == 0)
280         host = g_hash_table_lookup (key_value_pairs, key);
281       else if (g_strcmp0 (key, "port") == 0)
282         port = g_hash_table_lookup (key_value_pairs, key);
283       else if (g_strcmp0 (key, "family") == 0)
284         family = g_hash_table_lookup (key_value_pairs, key);
285       else
286         {
287           g_set_error (error,
288                        G_IO_ERROR,
289                        G_IO_ERROR_INVALID_ARGUMENT,
290                        _("Unsupported key `%s' in address entry `%s'"),
291                        key,
292                        address_entry);
293           goto out;
294         }
295     }
296
297   if (port != NULL)
298     {
299       port_num = strtol (port, &endp, 10);
300       if ((*port == '\0' || *endp != '\0') || port_num < 0 || port_num >= 65536)
301         {
302           g_set_error (error,
303                        G_IO_ERROR,
304                        G_IO_ERROR_INVALID_ARGUMENT,
305                        _("Error in address `%s' - the port attribute is malformed"),
306                        address_entry);
307           goto out;
308         }
309     }
310
311   if (family != NULL && !(g_strcmp0 (family, "ipv4") == 0 || g_strcmp0 (family, "ipv6") == 0))
312     {
313       g_set_error (error,
314                    G_IO_ERROR,
315                    G_IO_ERROR_INVALID_ARGUMENT,
316                    _("Error in address `%s' - the family attribute is malformed"),
317                    address_entry);
318       goto out;
319     }
320
321   ret= TRUE;
322
323  out:
324   g_list_free (keys);
325
326   return ret;
327 }
328
329 /**
330  * g_dbus_is_supported_address:
331  * @string: A string.
332  * @error: Return location for error or %NULL.
333  *
334  * Like g_dbus_is_address() but also checks if the library suppors the
335  * transports in @string and that key/value pairs for each transport
336  * are valid.
337  *
338  * Returns: %TRUE if @string is a valid D-Bus address that is
339  * supported by this library, %FALSE if @error is set.
340  *
341  * Since: 2.26
342  */
343 gboolean
344 g_dbus_is_supported_address (const gchar  *string,
345                              GError      **error)
346 {
347   guint n;
348   gchar **a;
349   gboolean ret;
350
351   ret = FALSE;
352
353   g_return_val_if_fail (string != NULL, FALSE);
354   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
355
356   a = g_strsplit (string, ";", 0);
357   for (n = 0; a[n] != NULL; n++)
358     {
359       gchar *transport_name;
360       GHashTable *key_value_pairs;
361       gboolean supported;
362
363       if (!_g_dbus_address_parse_entry (a[n],
364                                         &transport_name,
365                                         &key_value_pairs,
366                                         error))
367         goto out;
368
369       supported = FALSE;
370       if (g_strcmp0 (transport_name, "unix") == 0)
371         supported = is_valid_unix (a[n], key_value_pairs, error);
372       else if (g_strcmp0 (transport_name, "tcp") == 0)
373         supported = is_valid_tcp (a[n], key_value_pairs, error);
374       else if (g_strcmp0 (transport_name, "nonce-tcp") == 0)
375         supported = is_valid_nonce_tcp (a[n], key_value_pairs, error);
376
377       g_free (transport_name);
378       g_hash_table_unref (key_value_pairs);
379
380       if (!supported)
381         goto out;
382     }
383
384   ret = TRUE;
385
386  out:
387   g_strfreev (a);
388
389   g_assert (ret || (!ret && (error == NULL || *error != NULL)));
390
391   return ret;
392 }
393
394 gboolean
395 _g_dbus_address_parse_entry (const gchar   *address_entry,
396                              gchar        **out_transport_name,
397                              GHashTable   **out_key_value_pairs,
398                              GError       **error)
399 {
400   gboolean ret;
401   GHashTable *key_value_pairs;
402   gchar *transport_name;
403   gchar **kv_pairs;
404   const gchar *s;
405   guint n;
406
407   ret = FALSE;
408   kv_pairs = NULL;
409   transport_name = NULL;
410   key_value_pairs = NULL;
411
412   s = strchr (address_entry, ':');
413   if (s == NULL)
414     {
415       g_set_error (error,
416                    G_IO_ERROR,
417                    G_IO_ERROR_INVALID_ARGUMENT,
418                    _("Address element `%s', does not contain a colon (:)"),
419                    address_entry);
420       goto out;
421     }
422
423   transport_name = g_strndup (address_entry, s - address_entry);
424   key_value_pairs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
425
426   kv_pairs = g_strsplit (s + 1, ",", 0);
427   for (n = 0; kv_pairs != NULL && kv_pairs[n] != NULL; n++)
428     {
429       const gchar *kv_pair = kv_pairs[n];
430       gchar *key;
431       gchar *value;
432
433       s = strchr (kv_pair, '=');
434       if (s == NULL)
435         {
436           g_set_error (error,
437                        G_IO_ERROR,
438                        G_IO_ERROR_INVALID_ARGUMENT,
439                        _("Key/Value pair %d, `%s', in address element `%s', does not contain an equal sign"),
440                        n,
441                        kv_pair,
442                        address_entry);
443           goto out;
444         }
445
446       /* TODO: actually validate that no illegal characters are present before and after then '=' sign */
447       key = g_uri_unescape_segment (kv_pair, s, NULL);
448       value = g_uri_unescape_segment (s + 1, kv_pair + strlen (kv_pair), NULL);
449       g_hash_table_insert (key_value_pairs, key, value);
450     }
451
452   ret = TRUE;
453
454 out:
455   g_strfreev (kv_pairs);
456   if (ret)
457     {
458       if (out_transport_name != NULL)
459         *out_transport_name = transport_name;
460       else
461         g_free (transport_name);
462       if (out_key_value_pairs != NULL)
463         *out_key_value_pairs = key_value_pairs;
464       else if (key_value_pairs != NULL)
465         g_hash_table_unref (key_value_pairs);
466     }
467   else
468     {
469       g_free (transport_name);
470       if (key_value_pairs != NULL)
471         g_hash_table_unref (key_value_pairs);
472     }
473   return ret;
474 }
475
476 /* ---------------------------------------------------------------------------------------------------- */
477
478 /* TODO: Declare an extension point called GDBusTransport (or similar)
479  * and move code below to extensions implementing said extension
480  * point. That way we can implement a D-Bus transport over X11 without
481  * making libgio link to libX11...
482  */
483 static GIOStream *
484 g_dbus_address_connect (const gchar    *address_entry,
485                         const gchar    *transport_name,
486                         GHashTable     *key_value_pairs,
487                         GCancellable   *cancellable,
488                         GError        **error)
489 {
490   GIOStream *ret;
491   GSocketConnectable *connectable;
492   const gchar *nonce_file;
493
494   connectable = NULL;
495   ret = NULL;
496   nonce_file = NULL;
497
498   if (FALSE)
499     {
500     }
501 #ifdef G_OS_UNIX
502   else if (g_strcmp0 (transport_name, "unix") == 0)
503     {
504       const gchar *path;
505       const gchar *abstract;
506       path = g_hash_table_lookup (key_value_pairs, "path");
507       abstract = g_hash_table_lookup (key_value_pairs, "abstract");
508       if ((path == NULL && abstract == NULL) || (path != NULL && abstract != NULL))
509         {
510           g_set_error (error,
511                        G_IO_ERROR,
512                        G_IO_ERROR_INVALID_ARGUMENT,
513                        _("Error in address `%s' - the unix transport requires exactly one of the "
514                          "keys `path' or `abstract' to be set"),
515                        address_entry);
516         }
517       else if (path != NULL)
518         {
519           connectable = G_SOCKET_CONNECTABLE (g_unix_socket_address_new (path));
520         }
521       else if (abstract != NULL)
522         {
523           connectable = G_SOCKET_CONNECTABLE (g_unix_socket_address_new_with_type (abstract,
524                                                                                    -1,
525                                                                                    G_UNIX_SOCKET_ADDRESS_ABSTRACT));
526         }
527       else
528         {
529           g_assert_not_reached ();
530         }
531     }
532 #endif
533   else if (g_strcmp0 (transport_name, "tcp") == 0 || g_strcmp0 (transport_name, "nonce-tcp") == 0)
534     {
535       const gchar *s;
536       const gchar *host;
537       guint port;
538       gchar *endp;
539       gboolean is_nonce;
540
541       is_nonce = (g_strcmp0 (transport_name, "nonce-tcp") == 0);
542
543       host = g_hash_table_lookup (key_value_pairs, "host");
544       if (host == NULL)
545         {
546           g_set_error (error,
547                        G_IO_ERROR,
548                        G_IO_ERROR_INVALID_ARGUMENT,
549                        _("Error in address `%s' - the host attribute is missing or malformed"),
550                        address_entry);
551           goto out;
552         }
553
554       s = g_hash_table_lookup (key_value_pairs, "port");
555       if (s == NULL)
556         s = "0";
557       port = strtol (s, &endp, 10);
558       if ((*s == '\0' || *endp != '\0') || port < 0 || port >= 65536)
559         {
560           g_set_error (error,
561                        G_IO_ERROR,
562                        G_IO_ERROR_INVALID_ARGUMENT,
563                        _("Error in address `%s' - the port attribute is missing or malformed"),
564                        address_entry);
565           goto out;
566         }
567
568
569       if (is_nonce)
570         {
571           nonce_file = g_hash_table_lookup (key_value_pairs, "noncefile");
572           if (nonce_file == NULL)
573             {
574               g_set_error (error,
575                            G_IO_ERROR,
576                            G_IO_ERROR_INVALID_ARGUMENT,
577                            _("Error in address `%s' - the noncefile attribute is missing or malformed"),
578                            address_entry);
579               goto out;
580             }
581         }
582
583       /* TODO: deal with family */
584       connectable = g_network_address_new (host, port);
585     }
586   else
587     {
588       g_set_error (error,
589                    G_IO_ERROR,
590                    G_IO_ERROR_INVALID_ARGUMENT,
591                    _("Unknown or unsupported transport `%s' for address `%s'"),
592                    transport_name,
593                    address_entry);
594     }
595
596   if (connectable != NULL)
597     {
598       GSocketClient *client;
599       GSocketConnection *connection;
600
601       g_assert (ret == NULL);
602       client = g_socket_client_new ();
603       connection = g_socket_client_connect (client,
604                                             connectable,
605                                             cancellable,
606                                             error);
607       g_object_unref (connectable);
608       g_object_unref (client);
609       if (connection == NULL)
610         goto out;
611
612       ret = G_IO_STREAM (connection);
613
614       if (nonce_file != NULL)
615         {
616           gchar *nonce_contents;
617           gsize nonce_length;
618
619           /* TODO: too dangerous to read the entire file? (think denial-of-service etc.) */
620           if (!g_file_get_contents (nonce_file,
621                                     &nonce_contents,
622                                     &nonce_length,
623                                     error))
624             {
625               g_prefix_error (error, _("Error reading nonce file `%s':"), nonce_file);
626               g_object_unref (ret);
627               ret = NULL;
628               goto out;
629             }
630
631           if (nonce_length != 16)
632             {
633               g_set_error (error,
634                            G_IO_ERROR,
635                            G_IO_ERROR_INVALID_ARGUMENT,
636                            _("The nonce-file `%s' was %" G_GSIZE_FORMAT " bytes. Expected 16 bytes."),
637                            nonce_file,
638                            nonce_length);
639               g_free (nonce_contents);
640               g_object_unref (ret);
641               ret = NULL;
642               goto out;
643             }
644
645           if (!g_output_stream_write_all (g_io_stream_get_output_stream (ret),
646                                           nonce_contents,
647                                           nonce_length,
648                                           NULL,
649                                           cancellable,
650                                           error))
651             {
652               g_prefix_error (error, _("Error write contents of nonce file `%s' to stream:"), nonce_file);
653               g_object_unref (ret);
654               ret = NULL;
655               g_free (nonce_contents);
656               goto out;
657             }
658           g_free (nonce_contents);
659         }
660     }
661
662  out:
663
664   return ret;
665 }
666
667 static GIOStream *
668 g_dbus_address_try_connect_one (const gchar         *address_entry,
669                                 gchar              **out_guid,
670                                 GCancellable        *cancellable,
671                                 GError             **error)
672 {
673   GIOStream *ret;
674   GHashTable *key_value_pairs;
675   gchar *transport_name;
676   const gchar *guid;
677
678   ret = NULL;
679   transport_name = NULL;
680   key_value_pairs = NULL;
681
682   if (!_g_dbus_address_parse_entry (address_entry,
683                                     &transport_name,
684                                     &key_value_pairs,
685                                     error))
686     goto out;
687
688   ret = g_dbus_address_connect (address_entry,
689                                 transport_name,
690                                 key_value_pairs,
691                                 cancellable,
692                                 error);
693   if (ret == NULL)
694     goto out;
695
696   /* TODO: validate that guid is of correct format */
697   guid = g_hash_table_lookup (key_value_pairs, "guid");
698   if (guid != NULL && out_guid != NULL)
699     *out_guid = g_strdup (guid);
700
701 out:
702   g_free (transport_name);
703   if (key_value_pairs != NULL)
704     g_hash_table_unref (key_value_pairs);
705   return ret;
706 }
707
708
709 /* ---------------------------------------------------------------------------------------------------- */
710
711 typedef struct {
712   gchar *address;
713   GIOStream *stream;
714   gchar *guid;
715 } GetStreamData;
716
717 static void
718 get_stream_data_free (GetStreamData *data)
719 {
720   g_free (data->address);
721   if (data->stream != NULL)
722     g_object_unref (data->stream);
723   g_free (data->guid);
724   g_free (data);
725 }
726
727 static void
728 get_stream_thread_func (GSimpleAsyncResult *res,
729                         GObject            *object,
730                         GCancellable       *cancellable)
731 {
732   GetStreamData *data;
733   GError *error;
734
735   data = g_simple_async_result_get_op_res_gpointer (res);
736
737   error = NULL;
738   data->stream = g_dbus_address_get_stream_sync (data->address,
739                                                  &data->guid,
740                                                  cancellable,
741                                                  &error);
742   if (data->stream == NULL)
743     {
744       g_simple_async_result_set_from_error (res, error);
745       g_error_free (error);
746     }
747 }
748
749 /**
750  * g_dbus_address_get_stream:
751  * @address: A valid D-Bus address.
752  * @cancellable: A #GCancellable or %NULL.
753  * @callback: A #GAsyncReadyCallback to call when the request is satisfied.
754  * @user_data: Data to pass to @callback.
755  *
756  * Asynchronously connects to an endpoint specified by @address and
757  * sets up the connection so it is in a state to run the client-side
758  * of the D-Bus authentication conversation.
759  *
760  * When the operation is finished, @callback will be invoked. You can
761  * then call g_dbus_address_get_stream_finish() to get the result of
762  * the operation.
763  *
764  * This is an asynchronous failable function. See
765  * g_dbus_address_get_stream_sync() for the synchronous version.
766  *
767  * Since: 2.26
768  */
769 void
770 g_dbus_address_get_stream (const gchar         *address,
771                            GCancellable        *cancellable,
772                            GAsyncReadyCallback  callback,
773                            gpointer             user_data)
774 {
775   GSimpleAsyncResult *res;
776   GetStreamData *data;
777
778   g_return_if_fail (address != NULL);
779
780   res = g_simple_async_result_new (NULL,
781                                    callback,
782                                    user_data,
783                                    g_dbus_address_get_stream);
784   data = g_new0 (GetStreamData, 1);
785   data->address = g_strdup (address);
786   g_simple_async_result_set_op_res_gpointer (res,
787                                              data,
788                                              (GDestroyNotify) get_stream_data_free);
789   g_simple_async_result_run_in_thread (res,
790                                        get_stream_thread_func,
791                                        G_PRIORITY_DEFAULT,
792                                        cancellable);
793   g_object_unref (res);
794 }
795
796 /**
797  * g_dbus_address_get_stream_finish:
798  * @res: A #GAsyncResult obtained from the GAsyncReadyCallback passed to g_dbus_address_get_stream().
799  * @out_guid: %NULL or return location to store the GUID extracted from @address, if any.
800  * @error: Return location for error or %NULL.
801  *
802  * Finishes an operation started with g_dbus_address_get_stream().
803  *
804  * Returns: A #GIOStream or %NULL if @error is set.
805  *
806  * Since: 2.26
807  */
808 GIOStream *
809 g_dbus_address_get_stream_finish (GAsyncResult        *res,
810                                   gchar              **out_guid,
811                                   GError             **error)
812 {
813   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
814   GetStreamData *data;
815   GIOStream *ret;
816
817   g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
818   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
819
820   g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_dbus_address_get_stream);
821
822   ret = NULL;
823
824   data = g_simple_async_result_get_op_res_gpointer (simple);
825   if (g_simple_async_result_propagate_error (simple, error))
826     goto out;
827
828   ret = g_object_ref (data->stream);
829   if (out_guid != NULL)
830     *out_guid = g_strdup (data->guid);
831
832  out:
833   return ret;
834 }
835
836 /**
837  * g_dbus_address_get_stream_sync:
838  * @address: A valid D-Bus address.
839  * @out_guid: %NULL or return location to store the GUID extracted from @address, if any.
840  * @cancellable: A #GCancellable or %NULL.
841  * @error: Return location for error or %NULL.
842  *
843  * Synchronously connects to an endpoint specified by @address and
844  * sets up the connection so it is in a state to run the client-side
845  * of the D-Bus authentication conversation.
846  *
847  * This is a synchronous failable function. See
848  * g_dbus_address_get_stream() for the asynchronous version.
849  *
850  * Returns: A #GIOStream or %NULL if @error is set.
851  *
852  * Since: 2.26
853  */
854 GIOStream *
855 g_dbus_address_get_stream_sync (const gchar         *address,
856                                 gchar              **out_guid,
857                                 GCancellable        *cancellable,
858                                 GError             **error)
859 {
860   GIOStream *ret;
861   gchar **addr_array;
862   guint n;
863   GError *last_error;
864
865   g_return_val_if_fail (address != NULL, NULL);
866   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
867
868   ret = NULL;
869   last_error = NULL;
870
871   addr_array = g_strsplit (address, ";", 0);
872   last_error = NULL;
873   for (n = 0; addr_array != NULL && addr_array[n] != NULL; n++)
874     {
875       const gchar *addr = addr_array[n];
876       GError *this_error;
877       this_error = NULL;
878       ret = g_dbus_address_try_connect_one (addr,
879                                             out_guid,
880                                             cancellable,
881                                             &this_error);
882       if (ret != NULL)
883         {
884           goto out;
885         }
886       else
887         {
888           g_assert (this_error != NULL);
889           if (last_error != NULL)
890             g_error_free (last_error);
891           last_error = this_error;
892         }
893     }
894
895  out:
896   if (ret != NULL)
897     {
898       if (last_error != NULL)
899         g_error_free (last_error);
900     }
901   else
902     {
903       g_assert (last_error != NULL);
904       g_propagate_error (error, last_error);
905     }
906   return ret;
907 }
908
909 /* ---------------------------------------------------------------------------------------------------- */
910
911 /* TODO: implement for UNIX, Win32 and OS X */
912 static gchar *
913 get_session_address_platform_specific (void)
914 {
915   return NULL;
916 }
917
918 /* ---------------------------------------------------------------------------------------------------- */
919
920 /**
921  * g_dbus_address_get_for_bus_sync:
922  * @bus_type: A #GBusType.
923  * @cancellable: A #GCancellable or %NULL.
924  * @error: Return location for error or %NULL.
925  *
926  * Synchronously looks up the D-Bus address for the well-known message
927  * bus instance specified by @bus_type. This may involve using various
928  * platform specific mechanisms.
929  *
930  * Returns: A valid D-Bus address string for @bus_type or %NULL if @error is set.
931  *
932  * Since: 2.26
933  */
934 gchar *
935 g_dbus_address_get_for_bus_sync (GBusType       bus_type,
936                                  GCancellable  *cancellable,
937                                  GError       **error)
938 {
939   gchar *ret;
940   const gchar *starter_bus;
941
942   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
943
944   ret = NULL;
945
946   switch (bus_type)
947     {
948     case G_BUS_TYPE_SYSTEM:
949       ret = g_strdup (g_getenv ("DBUS_SYSTEM_BUS_ADDRESS"));
950       if (ret == NULL)
951         {
952           ret = g_strdup ("unix:path=/var/run/dbus/system_bus_socket");
953         }
954       break;
955
956     case G_BUS_TYPE_SESSION:
957       ret = g_strdup (g_getenv ("DBUS_SESSION_BUS_ADDRESS"));
958       if (ret == NULL)
959         {
960           ret = get_session_address_platform_specific ();
961           if (ret == NULL)
962             {
963               g_set_error (error,
964                            G_IO_ERROR,
965                            G_IO_ERROR_FAILED,
966                            _("Cannot determine session bus address (TODO: run dbus-launch to find out)"));
967             }
968         }
969       break;
970
971     case G_BUS_TYPE_STARTER:
972       starter_bus = g_getenv ("DBUS_STARTER_BUS_TYPE");
973       if (g_strcmp0 (starter_bus, "session") == 0)
974         {
975           ret = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SESSION, cancellable, error);
976           goto out;
977         }
978       else if (g_strcmp0 (starter_bus, "system") == 0)
979         {
980           ret = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SYSTEM, cancellable, error);
981           goto out;
982         }
983       else
984         {
985           if (starter_bus != NULL)
986             {
987               g_set_error (error,
988                            G_IO_ERROR,
989                            G_IO_ERROR_FAILED,
990                            _("Cannot determine bus address from DBUS_STARTER_BUS_TYPE environment variable"
991                              " - unknown value `%s'"),
992                            starter_bus);
993             }
994           else
995             {
996               g_set_error_literal (error,
997                                    G_IO_ERROR,
998                                    G_IO_ERROR_FAILED,
999                                    _("Cannot determine bus address because the DBUS_STARTER_BUS_TYPE environment "
1000                                      "variable is not set"));
1001             }
1002         }
1003       break;
1004
1005     default:
1006       g_set_error (error,
1007                    G_IO_ERROR,
1008                    G_IO_ERROR_FAILED,
1009                    _("Unknown bus type %d"),
1010                    bus_type);
1011       break;
1012     }
1013
1014  out:
1015   return ret;
1016 }