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