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