4b815e672097f6a7ae85fac3883bf2a49c3fe2d5
[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 #include <sys/wait.h>
28
29 #include "gioerror.h"
30 #include "gdbusutils.h"
31 #include "gdbusaddress.h"
32 #include "gdbuserror.h"
33 #include "gioenumtypes.h"
34 #include "gnetworkaddress.h"
35 #include "gsocketclient.h"
36 #include "giostream.h"
37 #include "gasyncresult.h"
38 #include "gsimpleasyncresult.h"
39 #include "gdbusprivate.h"
40
41 #ifdef G_OS_UNIX
42 #include <gio/gunixsocketaddress.h>
43 #endif
44
45 #include "glibintl.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 static gchar *get_session_address_platform_specific (GError **error);
59
60 /* ---------------------------------------------------------------------------------------------------- */
61
62 /**
63  * g_dbus_is_address:
64  * @string: A string.
65  *
66  * Checks if @string is a D-Bus address.
67  *
68  * This doesn't check if @string is actually supported by #GDBusServer
69  * or #GDBusConnection - use g_dbus_is_supported_address() to do more
70  * checks.
71  *
72  * Returns: %TRUE if @string is a valid D-Bus address, %FALSE otherwise.
73  *
74  * Since: 2.26
75  */
76 gboolean
77 g_dbus_is_address (const gchar *string)
78 {
79   guint n;
80   gchar **a;
81   gboolean ret;
82
83   ret = FALSE;
84
85   g_return_val_if_fail (string != NULL, FALSE);
86
87   a = g_strsplit (string, ";", 0);
88   if (a[0] == NULL)
89     goto out;
90
91   for (n = 0; a[n] != NULL; n++)
92     {
93       if (!_g_dbus_address_parse_entry (a[n],
94                                         NULL,
95                                         NULL,
96                                         NULL))
97         goto out;
98     }
99
100   ret = TRUE;
101
102  out:
103   g_strfreev (a);
104   return ret;
105 }
106
107 static gboolean
108 is_valid_unix (const gchar  *address_entry,
109                GHashTable   *key_value_pairs,
110                GError      **error)
111 {
112   gboolean ret;
113   GList *keys;
114   GList *l;
115   const gchar *path;
116   const gchar *tmpdir;
117   const gchar *abstract;
118
119   ret = FALSE;
120   keys = NULL;
121   path = NULL;
122   tmpdir = NULL;
123   abstract = NULL;
124
125   keys = g_hash_table_get_keys (key_value_pairs);
126   for (l = keys; l != NULL; l = l->next)
127     {
128       const gchar *key = l->data;
129       if (g_strcmp0 (key, "path") == 0)
130         path = g_hash_table_lookup (key_value_pairs, key);
131       else if (g_strcmp0 (key, "tmpdir") == 0)
132         tmpdir = g_hash_table_lookup (key_value_pairs, key);
133       else if (g_strcmp0 (key, "abstract") == 0)
134         abstract = g_hash_table_lookup (key_value_pairs, key);
135       else
136         {
137           g_set_error (error,
138                        G_IO_ERROR,
139                        G_IO_ERROR_INVALID_ARGUMENT,
140                        _("Unsupported key `%s' in address entry `%s'"),
141                        key,
142                        address_entry);
143           goto out;
144         }
145     }
146
147   if (path != NULL)
148     {
149       if (tmpdir != NULL || abstract != NULL)
150         goto meaningless;
151       /* TODO: validate path */
152     }
153   else if (tmpdir != NULL)
154     {
155       if (path != NULL || abstract != NULL)
156         goto meaningless;
157       /* TODO: validate tmpdir */
158     }
159   else if (abstract != NULL)
160     {
161       if (path != NULL || tmpdir != NULL)
162         goto meaningless;
163       /* TODO: validate abstract */
164     }
165   else
166     {
167       g_set_error (error,
168                    G_IO_ERROR,
169                    G_IO_ERROR_INVALID_ARGUMENT,
170                    _("Address `%s' is invalid (need exactly one of path, tmpdir or abstract keys)"),
171                    address_entry);
172       goto out;
173     }
174
175
176   ret= TRUE;
177   goto out;
178
179  meaningless:
180   g_set_error (error,
181                G_IO_ERROR,
182                G_IO_ERROR_INVALID_ARGUMENT,
183                _("Meaningless key/value pair combination in address entry `%s'"),
184                address_entry);
185
186  out:
187   g_list_free (keys);
188
189   return ret;
190 }
191
192 static gboolean
193 is_valid_nonce_tcp (const gchar  *address_entry,
194                     GHashTable   *key_value_pairs,
195                     GError      **error)
196 {
197   gboolean ret;
198   GList *keys;
199   GList *l;
200   const gchar *host;
201   const gchar *port;
202   const gchar *family;
203   const gchar *nonce_file;
204   gint port_num;
205   gchar *endp;
206
207   ret = FALSE;
208   keys = NULL;
209   host = NULL;
210   port = NULL;
211   family = NULL;
212   nonce_file = NULL;
213
214   keys = g_hash_table_get_keys (key_value_pairs);
215   for (l = keys; l != NULL; l = l->next)
216     {
217       const gchar *key = l->data;
218       if (g_strcmp0 (key, "host") == 0)
219         host = g_hash_table_lookup (key_value_pairs, key);
220       else if (g_strcmp0 (key, "port") == 0)
221         port = g_hash_table_lookup (key_value_pairs, key);
222       else if (g_strcmp0 (key, "family") == 0)
223         family = g_hash_table_lookup (key_value_pairs, key);
224       else if (g_strcmp0 (key, "noncefile") == 0)
225         nonce_file = g_hash_table_lookup (key_value_pairs, key);
226       else
227         {
228           g_set_error (error,
229                        G_IO_ERROR,
230                        G_IO_ERROR_INVALID_ARGUMENT,
231                        _("Unsupported key `%s' in address entry `%s'"),
232                        key,
233                        address_entry);
234           goto out;
235         }
236     }
237
238   if (port != NULL)
239     {
240       port_num = strtol (port, &endp, 10);
241       if ((*port == '\0' || *endp != '\0') || port_num < 0 || port_num >= 65536)
242         {
243           g_set_error (error,
244                        G_IO_ERROR,
245                        G_IO_ERROR_INVALID_ARGUMENT,
246                        _("Error in address `%s' - the port attribute is malformed"),
247                        address_entry);
248           goto out;
249         }
250     }
251
252   if (family != NULL && !(g_strcmp0 (family, "ipv4") == 0 || g_strcmp0 (family, "ipv6") == 0))
253     {
254       g_set_error (error,
255                    G_IO_ERROR,
256                    G_IO_ERROR_INVALID_ARGUMENT,
257                    _("Error in address `%s' - the family attribute is malformed"),
258                    address_entry);
259       goto out;
260     }
261
262   ret= TRUE;
263
264  out:
265   g_list_free (keys);
266
267   return ret;
268 }
269
270 static gboolean
271 is_valid_tcp (const gchar  *address_entry,
272               GHashTable   *key_value_pairs,
273               GError      **error)
274 {
275   gboolean ret;
276   GList *keys;
277   GList *l;
278   const gchar *host;
279   const gchar *port;
280   const gchar *family;
281   gint port_num;
282   gchar *endp;
283
284   ret = FALSE;
285   keys = NULL;
286   host = NULL;
287   port = NULL;
288   family = NULL;
289
290   keys = g_hash_table_get_keys (key_value_pairs);
291   for (l = keys; l != NULL; l = l->next)
292     {
293       const gchar *key = l->data;
294       if (g_strcmp0 (key, "host") == 0)
295         host = g_hash_table_lookup (key_value_pairs, key);
296       else if (g_strcmp0 (key, "port") == 0)
297         port = g_hash_table_lookup (key_value_pairs, key);
298       else if (g_strcmp0 (key, "family") == 0)
299         family = g_hash_table_lookup (key_value_pairs, key);
300       else
301         {
302           g_set_error (error,
303                        G_IO_ERROR,
304                        G_IO_ERROR_INVALID_ARGUMENT,
305                        _("Unsupported key `%s' in address entry `%s'"),
306                        key,
307                        address_entry);
308           goto out;
309         }
310     }
311
312   if (port != NULL)
313     {
314       port_num = strtol (port, &endp, 10);
315       if ((*port == '\0' || *endp != '\0') || port_num < 0 || port_num >= 65536)
316         {
317           g_set_error (error,
318                        G_IO_ERROR,
319                        G_IO_ERROR_INVALID_ARGUMENT,
320                        _("Error in address `%s' - the port attribute is malformed"),
321                        address_entry);
322           goto out;
323         }
324     }
325
326   if (family != NULL && !(g_strcmp0 (family, "ipv4") == 0 || g_strcmp0 (family, "ipv6") == 0))
327     {
328       g_set_error (error,
329                    G_IO_ERROR,
330                    G_IO_ERROR_INVALID_ARGUMENT,
331                    _("Error in address `%s' - the family attribute is malformed"),
332                    address_entry);
333       goto out;
334     }
335
336   ret= TRUE;
337
338  out:
339   g_list_free (keys);
340
341   return ret;
342 }
343
344 /**
345  * g_dbus_is_supported_address:
346  * @string: A string.
347  * @error: Return location for error or %NULL.
348  *
349  * Like g_dbus_is_address() but also checks if the library suppors the
350  * transports in @string and that key/value pairs for each transport
351  * are valid.
352  *
353  * Returns: %TRUE if @string is a valid D-Bus address that is
354  * supported by this library, %FALSE if @error is set.
355  *
356  * Since: 2.26
357  */
358 gboolean
359 g_dbus_is_supported_address (const gchar  *string,
360                              GError      **error)
361 {
362   guint n;
363   gchar **a;
364   gboolean ret;
365
366   ret = FALSE;
367
368   g_return_val_if_fail (string != NULL, FALSE);
369   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
370
371   a = g_strsplit (string, ";", 0);
372   for (n = 0; a[n] != NULL; n++)
373     {
374       gchar *transport_name;
375       GHashTable *key_value_pairs;
376       gboolean supported;
377
378       if (!_g_dbus_address_parse_entry (a[n],
379                                         &transport_name,
380                                         &key_value_pairs,
381                                         error))
382         goto out;
383
384       supported = FALSE;
385       if (g_strcmp0 (transport_name, "unix") == 0)
386         supported = is_valid_unix (a[n], key_value_pairs, error);
387       else if (g_strcmp0 (transport_name, "tcp") == 0)
388         supported = is_valid_tcp (a[n], key_value_pairs, error);
389       else if (g_strcmp0 (transport_name, "nonce-tcp") == 0)
390         supported = is_valid_nonce_tcp (a[n], key_value_pairs, error);
391       else if (g_strcmp0 (a[n], "autolaunch:") == 0)
392         supported = TRUE;
393
394       g_free (transport_name);
395       g_hash_table_unref (key_value_pairs);
396
397       if (!supported)
398         goto out;
399     }
400
401   ret = TRUE;
402
403  out:
404   g_strfreev (a);
405
406   g_assert (ret || (!ret && (error == NULL || *error != NULL)));
407
408   return ret;
409 }
410
411 gboolean
412 _g_dbus_address_parse_entry (const gchar  *address_entry,
413                              gchar       **out_transport_name,
414                              GHashTable  **out_key_value_pairs,
415                              GError      **error)
416 {
417   gboolean ret;
418   GHashTable *key_value_pairs;
419   gchar *transport_name;
420   gchar **kv_pairs;
421   const gchar *s;
422   guint n;
423
424   ret = FALSE;
425   kv_pairs = NULL;
426   transport_name = NULL;
427   key_value_pairs = NULL;
428
429   s = strchr (address_entry, ':');
430   if (s == NULL)
431     {
432       g_set_error (error,
433                    G_IO_ERROR,
434                    G_IO_ERROR_INVALID_ARGUMENT,
435                    _("Address element `%s', does not contain a colon (:)"),
436                    address_entry);
437       goto out;
438     }
439
440   transport_name = g_strndup (address_entry, s - address_entry);
441   key_value_pairs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
442
443   kv_pairs = g_strsplit (s + 1, ",", 0);
444   for (n = 0; kv_pairs != NULL && kv_pairs[n] != NULL; n++)
445     {
446       const gchar *kv_pair = kv_pairs[n];
447       gchar *key;
448       gchar *value;
449
450       s = strchr (kv_pair, '=');
451       if (s == NULL)
452         {
453           g_set_error (error,
454                        G_IO_ERROR,
455                        G_IO_ERROR_INVALID_ARGUMENT,
456                        _("Key/Value pair %d, `%s', in address element `%s', does not contain an equal sign"),
457                        n,
458                        kv_pair,
459                        address_entry);
460           goto out;
461         }
462
463       /* TODO: actually validate that no illegal characters are present before and after then '=' sign */
464       key = g_uri_unescape_segment (kv_pair, s, NULL);
465       value = g_uri_unescape_segment (s + 1, kv_pair + strlen (kv_pair), NULL);
466       g_hash_table_insert (key_value_pairs, key, value);
467     }
468
469   ret = TRUE;
470
471 out:
472   g_strfreev (kv_pairs);
473   if (ret)
474     {
475       if (out_transport_name != NULL)
476         *out_transport_name = transport_name;
477       else
478         g_free (transport_name);
479       if (out_key_value_pairs != NULL)
480         *out_key_value_pairs = key_value_pairs;
481       else if (key_value_pairs != NULL)
482         g_hash_table_unref (key_value_pairs);
483     }
484   else
485     {
486       g_free (transport_name);
487       if (key_value_pairs != NULL)
488         g_hash_table_unref (key_value_pairs);
489     }
490   return ret;
491 }
492
493 /* ---------------------------------------------------------------------------------------------------- */
494
495 static GIOStream *
496 g_dbus_address_try_connect_one (const gchar   *address_entry,
497                                 gchar        **out_guid,
498                                 GCancellable  *cancellable,
499                                 GError       **error);
500
501 /* TODO: Declare an extension point called GDBusTransport (or similar)
502  * and move code below to extensions implementing said extension
503  * point. That way we can implement a D-Bus transport over X11 without
504  * making libgio link to libX11...
505  */
506 static GIOStream *
507 g_dbus_address_connect (const gchar   *address_entry,
508                         const gchar   *transport_name,
509                         GHashTable    *key_value_pairs,
510                         GCancellable  *cancellable,
511                         GError       **error)
512 {
513   GIOStream *ret;
514   GSocketConnectable *connectable;
515   const gchar *nonce_file;
516
517   connectable = NULL;
518   ret = NULL;
519   nonce_file = NULL;
520
521   if (FALSE)
522     {
523     }
524 #ifdef G_OS_UNIX
525   else if (g_strcmp0 (transport_name, "unix") == 0)
526     {
527       const gchar *path;
528       const gchar *abstract;
529       path = g_hash_table_lookup (key_value_pairs, "path");
530       abstract = g_hash_table_lookup (key_value_pairs, "abstract");
531       if ((path == NULL && abstract == NULL) || (path != NULL && abstract != NULL))
532         {
533           g_set_error (error,
534                        G_IO_ERROR,
535                        G_IO_ERROR_INVALID_ARGUMENT,
536                        _("Error in address `%s' - the unix transport requires exactly one of the "
537                          "keys `path' or `abstract' to be set"),
538                        address_entry);
539         }
540       else if (path != NULL)
541         {
542           connectable = G_SOCKET_CONNECTABLE (g_unix_socket_address_new (path));
543         }
544       else if (abstract != NULL)
545         {
546           connectable = G_SOCKET_CONNECTABLE (g_unix_socket_address_new_with_type (abstract,
547                                                                                    -1,
548                                                                                    G_UNIX_SOCKET_ADDRESS_ABSTRACT));
549         }
550       else
551         {
552           g_assert_not_reached ();
553         }
554     }
555 #endif
556   else if (g_strcmp0 (transport_name, "tcp") == 0 || g_strcmp0 (transport_name, "nonce-tcp") == 0)
557     {
558       const gchar *s;
559       const gchar *host;
560       guint port;
561       gchar *endp;
562       gboolean is_nonce;
563
564       is_nonce = (g_strcmp0 (transport_name, "nonce-tcp") == 0);
565
566       host = g_hash_table_lookup (key_value_pairs, "host");
567       if (host == NULL)
568         {
569           g_set_error (error,
570                        G_IO_ERROR,
571                        G_IO_ERROR_INVALID_ARGUMENT,
572                        _("Error in address `%s' - the host attribute is missing or malformed"),
573                        address_entry);
574           goto out;
575         }
576
577       s = g_hash_table_lookup (key_value_pairs, "port");
578       if (s == NULL)
579         s = "0";
580       port = strtol (s, &endp, 10);
581       if ((*s == '\0' || *endp != '\0') || port < 0 || port >= 65536)
582         {
583           g_set_error (error,
584                        G_IO_ERROR,
585                        G_IO_ERROR_INVALID_ARGUMENT,
586                        _("Error in address `%s' - the port attribute is missing or malformed"),
587                        address_entry);
588           goto out;
589         }
590
591
592       if (is_nonce)
593         {
594           nonce_file = g_hash_table_lookup (key_value_pairs, "noncefile");
595           if (nonce_file == NULL)
596             {
597               g_set_error (error,
598                            G_IO_ERROR,
599                            G_IO_ERROR_INVALID_ARGUMENT,
600                            _("Error in address `%s' - the noncefile attribute is missing or malformed"),
601                            address_entry);
602               goto out;
603             }
604         }
605
606       /* TODO: deal with family */
607       connectable = g_network_address_new (host, port);
608     }
609   else if (g_strcmp0 (address_entry, "autolaunch:") == 0)
610     {
611       gchar *autolaunch_address;
612       autolaunch_address = get_session_address_platform_specific (error);
613       if (autolaunch_address != NULL)
614         {
615           ret = g_dbus_address_try_connect_one (autolaunch_address, NULL, cancellable, error);
616           g_free (autolaunch_address);
617           goto out;
618         }
619       else
620         {
621           g_prefix_error (error, _("Error auto-launching: "));
622         }
623     }
624   else
625     {
626       g_set_error (error,
627                    G_IO_ERROR,
628                    G_IO_ERROR_INVALID_ARGUMENT,
629                    _("Unknown or unsupported transport `%s' for address `%s'"),
630                    transport_name,
631                    address_entry);
632     }
633
634   if (connectable != NULL)
635     {
636       GSocketClient *client;
637       GSocketConnection *connection;
638
639       g_assert (ret == NULL);
640       client = g_socket_client_new ();
641       connection = g_socket_client_connect (client,
642                                             connectable,
643                                             cancellable,
644                                             error);
645       g_object_unref (connectable);
646       g_object_unref (client);
647       if (connection == NULL)
648         goto out;
649
650       ret = G_IO_STREAM (connection);
651
652       if (nonce_file != NULL)
653         {
654           gchar *nonce_contents;
655           gsize nonce_length;
656
657           /* TODO: too dangerous to read the entire file? (think denial-of-service etc.) */
658           if (!g_file_get_contents (nonce_file,
659                                     &nonce_contents,
660                                     &nonce_length,
661                                     error))
662             {
663               g_prefix_error (error, _("Error reading nonce file `%s':"), nonce_file);
664               g_object_unref (ret);
665               ret = NULL;
666               goto out;
667             }
668
669           if (nonce_length != 16)
670             {
671               /* G_GSIZE_FORMAT doesn't work with gettext, so we use %lu */
672               g_set_error (error,
673                            G_IO_ERROR,
674                            G_IO_ERROR_INVALID_ARGUMENT,
675                            _("The nonce-file `%s' was %lu bytes. Expected 16 bytes."),
676                            nonce_file,
677                            nonce_length);
678               g_free (nonce_contents);
679               g_object_unref (ret);
680               ret = NULL;
681               goto out;
682             }
683
684           if (!g_output_stream_write_all (g_io_stream_get_output_stream (ret),
685                                           nonce_contents,
686                                           nonce_length,
687                                           NULL,
688                                           cancellable,
689                                           error))
690             {
691               g_prefix_error (error, _("Error write contents of nonce file `%s' to stream:"), nonce_file);
692               g_object_unref (ret);
693               ret = NULL;
694               g_free (nonce_contents);
695               goto out;
696             }
697           g_free (nonce_contents);
698         }
699     }
700
701  out:
702
703   return ret;
704 }
705
706 static GIOStream *
707 g_dbus_address_try_connect_one (const gchar   *address_entry,
708                                 gchar        **out_guid,
709                                 GCancellable  *cancellable,
710                                 GError       **error)
711 {
712   GIOStream *ret;
713   GHashTable *key_value_pairs;
714   gchar *transport_name;
715   const gchar *guid;
716
717   ret = NULL;
718   transport_name = NULL;
719   key_value_pairs = NULL;
720
721   if (!_g_dbus_address_parse_entry (address_entry,
722                                     &transport_name,
723                                     &key_value_pairs,
724                                     error))
725     goto out;
726
727   ret = g_dbus_address_connect (address_entry,
728                                 transport_name,
729                                 key_value_pairs,
730                                 cancellable,
731                                 error);
732   if (ret == NULL)
733     goto out;
734
735   /* TODO: validate that guid is of correct format */
736   guid = g_hash_table_lookup (key_value_pairs, "guid");
737   if (guid != NULL && out_guid != NULL)
738     *out_guid = g_strdup (guid);
739
740 out:
741   g_free (transport_name);
742   if (key_value_pairs != NULL)
743     g_hash_table_unref (key_value_pairs);
744   return ret;
745 }
746
747
748 /* ---------------------------------------------------------------------------------------------------- */
749
750 typedef struct {
751   gchar *address;
752   GIOStream *stream;
753   gchar *guid;
754 } GetStreamData;
755
756 static void
757 get_stream_data_free (GetStreamData *data)
758 {
759   g_free (data->address);
760   if (data->stream != NULL)
761     g_object_unref (data->stream);
762   g_free (data->guid);
763   g_free (data);
764 }
765
766 static void
767 get_stream_thread_func (GSimpleAsyncResult *res,
768                         GObject            *object,
769                         GCancellable       *cancellable)
770 {
771   GetStreamData *data;
772   GError *error;
773
774   data = g_simple_async_result_get_op_res_gpointer (res);
775
776   error = NULL;
777   data->stream = g_dbus_address_get_stream_sync (data->address,
778                                                  &data->guid,
779                                                  cancellable,
780                                                  &error);
781   if (data->stream == NULL)
782     {
783       g_simple_async_result_set_from_error (res, error);
784       g_error_free (error);
785     }
786 }
787
788 /**
789  * g_dbus_address_get_stream:
790  * @address: A valid D-Bus address.
791  * @cancellable: A #GCancellable or %NULL.
792  * @callback: A #GAsyncReadyCallback to call when the request is satisfied.
793  * @user_data: Data to pass to @callback.
794  *
795  * Asynchronously connects to an endpoint specified by @address and
796  * sets up the connection so it is in a state to run the client-side
797  * of the D-Bus authentication conversation.
798  *
799  * When the operation is finished, @callback will be invoked. You can
800  * then call g_dbus_address_get_stream_finish() to get the result of
801  * the operation.
802  *
803  * This is an asynchronous failable function. See
804  * g_dbus_address_get_stream_sync() for the synchronous version.
805  *
806  * Since: 2.26
807  */
808 void
809 g_dbus_address_get_stream (const gchar         *address,
810                            GCancellable        *cancellable,
811                            GAsyncReadyCallback  callback,
812                            gpointer             user_data)
813 {
814   GSimpleAsyncResult *res;
815   GetStreamData *data;
816
817   g_return_if_fail (address != NULL);
818
819   res = g_simple_async_result_new (NULL,
820                                    callback,
821                                    user_data,
822                                    g_dbus_address_get_stream);
823   data = g_new0 (GetStreamData, 1);
824   data->address = g_strdup (address);
825   g_simple_async_result_set_op_res_gpointer (res,
826                                              data,
827                                              (GDestroyNotify) get_stream_data_free);
828   g_simple_async_result_run_in_thread (res,
829                                        get_stream_thread_func,
830                                        G_PRIORITY_DEFAULT,
831                                        cancellable);
832   g_object_unref (res);
833 }
834
835 /**
836  * g_dbus_address_get_stream_finish:
837  * @res: A #GAsyncResult obtained from the GAsyncReadyCallback passed to g_dbus_address_get_stream().
838  * @out_guid: %NULL or return location to store the GUID extracted from @address, if any.
839  * @error: Return location for error or %NULL.
840  *
841  * Finishes an operation started with g_dbus_address_get_stream().
842  *
843  * Returns: A #GIOStream or %NULL if @error is set.
844  *
845  * Since: 2.26
846  */
847 GIOStream *
848 g_dbus_address_get_stream_finish (GAsyncResult        *res,
849                                   gchar              **out_guid,
850                                   GError             **error)
851 {
852   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
853   GetStreamData *data;
854   GIOStream *ret;
855
856   g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
857   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
858
859   g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_dbus_address_get_stream);
860
861   ret = NULL;
862
863   data = g_simple_async_result_get_op_res_gpointer (simple);
864   if (g_simple_async_result_propagate_error (simple, error))
865     goto out;
866
867   ret = g_object_ref (data->stream);
868   if (out_guid != NULL)
869     *out_guid = g_strdup (data->guid);
870
871  out:
872   return ret;
873 }
874
875 /**
876  * g_dbus_address_get_stream_sync:
877  * @address: A valid D-Bus address.
878  * @out_guid: %NULL or return location to store the GUID extracted from @address, if any.
879  * @cancellable: A #GCancellable or %NULL.
880  * @error: Return location for error or %NULL.
881  *
882  * Synchronously connects to an endpoint specified by @address and
883  * sets up the connection so it is in a state to run the client-side
884  * of the D-Bus authentication conversation.
885  *
886  * This is a synchronous failable function. See
887  * g_dbus_address_get_stream() for the asynchronous version.
888  *
889  * Returns: A #GIOStream or %NULL if @error is set.
890  *
891  * Since: 2.26
892  */
893 GIOStream *
894 g_dbus_address_get_stream_sync (const gchar   *address,
895                                 gchar        **out_guid,
896                                 GCancellable  *cancellable,
897                                 GError       **error)
898 {
899   GIOStream *ret;
900   gchar **addr_array;
901   guint n;
902   GError *last_error;
903
904   g_return_val_if_fail (address != NULL, NULL);
905   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
906
907   ret = NULL;
908   last_error = NULL;
909
910   addr_array = g_strsplit (address, ";", 0);
911   if (addr_array[0] == NULL)
912     {
913       last_error = g_error_new_literal (G_IO_ERROR,
914                                         G_IO_ERROR_INVALID_ARGUMENT,
915                                         _("The given address is empty"));
916       goto out;
917     }
918
919   for (n = 0; addr_array != NULL && addr_array[n] != NULL; n++)
920     {
921       const gchar *addr = addr_array[n];
922       GError *this_error;
923
924       this_error = NULL;
925       ret = g_dbus_address_try_connect_one (addr,
926                                             out_guid,
927                                             cancellable,
928                                             &this_error);
929       if (ret != NULL)
930         {
931           goto out;
932         }
933       else
934         {
935           g_assert (this_error != NULL);
936           if (last_error != NULL)
937             g_error_free (last_error);
938           last_error = this_error;
939         }
940     }
941
942  out:
943   if (ret != NULL)
944     {
945       if (last_error != NULL)
946         g_error_free (last_error);
947     }
948   else
949     {
950       g_assert (last_error != NULL);
951       g_propagate_error (error, last_error);
952     }
953
954   g_strfreev (addr_array);
955   return ret;
956 }
957
958 /* ---------------------------------------------------------------------------------------------------- */
959
960 #ifdef G_OS_UNIX
961 static gchar *
962 get_session_address_dbus_launch (GError **error)
963 {
964   gchar *ret;
965   gchar *machine_id;
966   gchar *command_line;
967   gchar *launch_stdout;
968   gchar *launch_stderr;
969   gint exit_status;
970   gchar *old_dbus_verbose;
971   gboolean restore_dbus_verbose;
972
973   ret = NULL;
974   machine_id = NULL;
975   command_line = NULL;
976   launch_stdout = NULL;
977   launch_stderr = NULL;
978   restore_dbus_verbose = FALSE;
979   old_dbus_verbose = NULL;
980
981   machine_id = _g_dbus_get_machine_id (error);
982   if (machine_id == NULL)
983     {
984       g_prefix_error (error, _("Cannot spawn a message bus without a machine-id: "));
985       goto out;
986     }
987
988   /* We're using private libdbus facilities here. When everything
989    * (X11, Mac OS X, Windows) is spec'ed out correctly (not even the
990    * X11 property is correctly documented right now) we should
991    * consider using the spec instead of dbus-launch.
992    *
993    *   --autolaunch=MACHINEID
994    *          This option implies that dbus-launch should scan  for  a  previ‐
995    *          ously-started  session  and  reuse the values found there. If no
996    *          session is found, it will start a new session. The  --exit-with-
997    *          session option is implied if --autolaunch is given.  This option
998    *          is for the exclusive use of libdbus, you do not want to  use  it
999    *          manually. It may change in the future.
1000    */
1001
1002   /* TODO: maybe provide a variable for where to look for the dbus-launch binary? */
1003   command_line = g_strdup_printf ("dbus-launch --autolaunch=%s --binary-syntax --close-stderr", machine_id);
1004
1005   if (G_UNLIKELY (_g_dbus_debug_address ()))
1006     {
1007       _g_dbus_debug_print_lock ();
1008       g_print ("GDBus-debug:Address: Running `%s' to get bus address (possibly autolaunching)\n", command_line);
1009       old_dbus_verbose = g_strdup (g_getenv ("DBUS_VERBOSE"));
1010       restore_dbus_verbose = TRUE;
1011       g_setenv ("DBUS_VERBOSE", "1", TRUE);
1012       _g_dbus_debug_print_unlock ();
1013     }
1014
1015   if (!g_spawn_command_line_sync (command_line,
1016                                   &launch_stdout,
1017                                   &launch_stderr,
1018                                   &exit_status,
1019                                   error))
1020     {
1021       g_prefix_error (error, _("Error spawning command line `%s': "), command_line);
1022       goto out;
1023     }
1024
1025   if (!WIFEXITED (exit_status))
1026     {
1027       gchar *escaped_stderr;
1028       escaped_stderr = g_strescape (launch_stderr, "");
1029       g_set_error (error,
1030                    G_IO_ERROR,
1031                    G_IO_ERROR_FAILED,
1032                    _("Abnormal program termination spawning command line `%s': %s"),
1033                    command_line,
1034                    escaped_stderr);
1035       g_free (escaped_stderr);
1036       goto out;
1037     }
1038
1039   if (WEXITSTATUS (exit_status) != 0)
1040     {
1041       gchar *escaped_stderr;
1042       escaped_stderr = g_strescape (launch_stderr, "");
1043       g_set_error (error,
1044                    G_IO_ERROR,
1045                    G_IO_ERROR_FAILED,
1046                    _("Command line `%s' exited with non-zero exit status %d: %s"),
1047                    command_line,
1048                    WEXITSTATUS (exit_status),
1049                    escaped_stderr);
1050       g_free (escaped_stderr);
1051       goto out;
1052     }
1053
1054   /* From the dbus-launch(1) man page:
1055    *
1056    *   --binary-syntax Write to stdout a nul-terminated bus address,
1057    *   then the bus PID as a binary integer of size sizeof(pid_t),
1058    *   then the bus X window ID as a binary integer of size
1059    *   sizeof(long).  Integers are in the machine's byte order, not
1060    *   network byte order or any other canonical byte order.
1061    */
1062   ret = g_strdup (launch_stdout);
1063
1064  out:
1065   if (G_UNLIKELY (_g_dbus_debug_address ()))
1066     {
1067       gchar *s;
1068       _g_dbus_debug_print_lock ();
1069       g_print ("GDBus-debug:Address: dbus-launch output:");
1070       if (launch_stdout != NULL)
1071         {
1072           s = _g_dbus_hexdump (launch_stdout, strlen (launch_stdout) + 1 + sizeof (pid_t) + sizeof (long), 2);
1073           g_print ("\n%s", s);
1074           g_free (s);
1075         }
1076       else
1077         {
1078           g_print (" (none)\n");
1079         }
1080       g_print ("GDBus-debug:Address: dbus-launch stderr output:");
1081       if (launch_stderr != NULL)
1082         g_print ("\n%s", launch_stderr);
1083       else
1084         g_print (" (none)\n");
1085       _g_dbus_debug_print_unlock ();
1086     }
1087
1088   g_free (machine_id);
1089   g_free (command_line);
1090   g_free (launch_stdout);
1091   g_free (launch_stderr);
1092   if (G_UNLIKELY (restore_dbus_verbose))
1093     {
1094       if (old_dbus_verbose != NULL)
1095         g_setenv ("DBUS_VERBOSE", old_dbus_verbose, TRUE);
1096       else
1097         g_unsetenv ("DBUS_VERBOSE");
1098     }
1099   g_free (old_dbus_verbose);
1100   return ret;
1101 }
1102 #endif
1103
1104 /* ---------------------------------------------------------------------------------------------------- */
1105
1106 /* TODO: implement for UNIX, Win32 and OS X */
1107 static gchar *
1108 get_session_address_platform_specific (GError **error)
1109 {
1110   gchar *ret;
1111 #ifdef G_OS_UNIX
1112   /* need to handle OS X in a different way since `dbus-launch --autolaunch' probably won't work there */
1113   ret = get_session_address_dbus_launch (error);
1114 #else
1115   ret = NULL;
1116   g_set_error (error,
1117                G_IO_ERROR,
1118                G_IO_ERROR_FAILED,
1119                _("Cannot determine session bus address (not implemented for this OS)"));
1120 #endif
1121   return ret;
1122 }
1123
1124 /* ---------------------------------------------------------------------------------------------------- */
1125
1126 /**
1127  * g_dbus_address_get_for_bus_sync:
1128  * @bus_type: A #GBusType.
1129  * @cancellable: A #GCancellable or %NULL.
1130  * @error: Return location for error or %NULL.
1131  *
1132  * Synchronously looks up the D-Bus address for the well-known message
1133  * bus instance specified by @bus_type. This may involve using various
1134  * platform specific mechanisms.
1135  *
1136  * Returns: A valid D-Bus address string for @bus_type or %NULL if @error is set.
1137  *
1138  * Since: 2.26
1139  */
1140 gchar *
1141 g_dbus_address_get_for_bus_sync (GBusType       bus_type,
1142                                  GCancellable  *cancellable,
1143                                  GError       **error)
1144 {
1145   gchar *ret;
1146   const gchar *starter_bus;
1147   GError *local_error;
1148
1149   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1150
1151   ret = NULL;
1152   local_error = NULL;
1153
1154   if (G_UNLIKELY (_g_dbus_debug_address ()))
1155     {
1156       guint n;
1157       _g_dbus_debug_print_lock ();
1158       g_print ("GDBus-debug:Address: In g_dbus_address_get_for_bus_sync() for bus type `%s'\n",
1159                _g_dbus_enum_to_string (G_TYPE_BUS_TYPE, bus_type));
1160       for (n = 0; n < 3; n++)
1161         {
1162           const gchar *k;
1163           const gchar *v;
1164           switch (n)
1165             {
1166             case 0: k = "DBUS_SESSION_BUS_ADDRESS"; break;
1167             case 1: k = "DBUS_SYSTEM_BUS_ADDRESS"; break;
1168             case 2: k = "DBUS_STARTER_BUS_TYPE"; break;
1169             default: g_assert_not_reached ();
1170             }
1171           v = g_getenv (k);
1172           g_print ("GDBus-debug:Address: env var %s", k);
1173           if (v != NULL)
1174             g_print ("=`%s'\n", v);
1175           else
1176             g_print (" is not set\n");
1177         }
1178       _g_dbus_debug_print_unlock ();
1179     }
1180
1181   switch (bus_type)
1182     {
1183     case G_BUS_TYPE_SYSTEM:
1184       ret = g_strdup (g_getenv ("DBUS_SYSTEM_BUS_ADDRESS"));
1185       if (ret == NULL)
1186         {
1187           ret = g_strdup ("unix:path=/var/run/dbus/system_bus_socket");
1188         }
1189       break;
1190
1191     case G_BUS_TYPE_SESSION:
1192       ret = g_strdup (g_getenv ("DBUS_SESSION_BUS_ADDRESS"));
1193       if (ret == NULL)
1194         {
1195           ret = get_session_address_platform_specific (&local_error);
1196         }
1197       break;
1198
1199     case G_BUS_TYPE_STARTER:
1200       starter_bus = g_getenv ("DBUS_STARTER_BUS_TYPE");
1201       if (g_strcmp0 (starter_bus, "session") == 0)
1202         {
1203           ret = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SESSION, cancellable, &local_error);
1204           goto out;
1205         }
1206       else if (g_strcmp0 (starter_bus, "system") == 0)
1207         {
1208           ret = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SYSTEM, cancellable, &local_error);
1209           goto out;
1210         }
1211       else
1212         {
1213           if (starter_bus != NULL)
1214             {
1215               g_set_error (&local_error,
1216                            G_IO_ERROR,
1217                            G_IO_ERROR_FAILED,
1218                            _("Cannot determine bus address from DBUS_STARTER_BUS_TYPE environment variable"
1219                              " - unknown value `%s'"),
1220                            starter_bus);
1221             }
1222           else
1223             {
1224               g_set_error_literal (&local_error,
1225                                    G_IO_ERROR,
1226                                    G_IO_ERROR_FAILED,
1227                                    _("Cannot determine bus address because the DBUS_STARTER_BUS_TYPE environment "
1228                                      "variable is not set"));
1229             }
1230         }
1231       break;
1232
1233     default:
1234       g_set_error (&local_error,
1235                    G_IO_ERROR,
1236                    G_IO_ERROR_FAILED,
1237                    _("Unknown bus type %d"),
1238                    bus_type);
1239       break;
1240     }
1241
1242  out:
1243   if (G_UNLIKELY (_g_dbus_debug_address ()))
1244     {
1245       _g_dbus_debug_print_lock ();
1246       if (ret != NULL)
1247         {
1248           g_print ("GDBus-debug:Address: Returning address `%s' for bus type `%s'\n",
1249                    ret,
1250                    _g_dbus_enum_to_string (G_TYPE_BUS_TYPE, bus_type));
1251         }
1252       else
1253         {
1254           g_print ("GDBus-debug:Address: Cannot look-up address bus type `%s': %s\n",
1255                    _g_dbus_enum_to_string (G_TYPE_BUS_TYPE, bus_type),
1256                    local_error->message);
1257         }
1258       _g_dbus_debug_print_unlock ();
1259     }
1260
1261   if (local_error != NULL)
1262     g_propagate_error (error, local_error);
1263
1264   return ret;
1265 }