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