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