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