Include sys/wait.h for WEXITSTATUS()
[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 #include "gioalias.h"
47
48 /**
49  * SECTION:gdbusaddress
50  * @title: D-Bus Addresses
51  * @short_description: D-Bus connection endpoints
52  * @include: gio/gio.h
53  *
54  * Routines for working with D-Bus addresses. A D-Bus address is a string
55  * like "unix:tmpdir=/tmp/my-app-name". The exact format of addresses
56  * is explained in detail in the <link linkend="http://dbus.freedesktop.org/doc/dbus-specification.html&num;addresses">D-Bus specification</link>.
57  */
58
59 static gchar *get_session_address_platform_specific (GError **error);
60
61 /* ---------------------------------------------------------------------------------------------------- */
62
63 /**
64  * g_dbus_is_address:
65  * @string: A string.
66  *
67  * Checks if @string is a D-Bus address.
68  *
69  * This doesn't check if @string is actually supported by #GDBusServer
70  * or #GDBusConnection - use g_dbus_is_supported_address() to do more
71  * checks.
72  *
73  * Returns: %TRUE if @string is a valid D-Bus address, %FALSE otherwise.
74  *
75  * Since: 2.26
76  */
77 gboolean
78 g_dbus_is_address (const gchar *string)
79 {
80   guint n;
81   gchar **a;
82   gboolean ret;
83
84   ret = FALSE;
85
86   g_return_val_if_fail (string != NULL, FALSE);
87
88   a = g_strsplit (string, ";", 0);
89   if (a[0] == NULL)
90     goto out;
91
92   for (n = 0; a[n] != NULL; n++)
93     {
94       if (!_g_dbus_address_parse_entry (a[n],
95                                         NULL,
96                                         NULL,
97                                         NULL))
98         goto out;
99     }
100
101   ret = TRUE;
102
103  out:
104   g_strfreev (a);
105   return ret;
106 }
107
108 static gboolean
109 is_valid_unix (const gchar  *address_entry,
110                GHashTable   *key_value_pairs,
111                GError      **error)
112 {
113   gboolean ret;
114   GList *keys;
115   GList *l;
116   const gchar *path;
117   const gchar *tmpdir;
118   const gchar *abstract;
119
120   ret = FALSE;
121   keys = NULL;
122   path = NULL;
123   tmpdir = NULL;
124   abstract = NULL;
125
126   keys = g_hash_table_get_keys (key_value_pairs);
127   for (l = keys; l != NULL; l = l->next)
128     {
129       const gchar *key = l->data;
130       if (g_strcmp0 (key, "path") == 0)
131         path = g_hash_table_lookup (key_value_pairs, key);
132       else if (g_strcmp0 (key, "tmpdir") == 0)
133         tmpdir = g_hash_table_lookup (key_value_pairs, key);
134       else if (g_strcmp0 (key, "abstract") == 0)
135         abstract = g_hash_table_lookup (key_value_pairs, key);
136       else
137         {
138           g_set_error (error,
139                        G_IO_ERROR,
140                        G_IO_ERROR_INVALID_ARGUMENT,
141                        _("Unsupported key `%s' in address entry `%s'"),
142                        key,
143                        address_entry);
144           goto out;
145         }
146     }
147
148   if (path != NULL)
149     {
150       if (tmpdir != NULL || abstract != NULL)
151         goto meaningless;
152       /* TODO: validate path */
153     }
154   else if (tmpdir != NULL)
155     {
156       if (path != NULL || abstract != NULL)
157         goto meaningless;
158       /* TODO: validate tmpdir */
159     }
160   else if (abstract != NULL)
161     {
162       if (path != NULL || tmpdir != NULL)
163         goto meaningless;
164       /* TODO: validate abstract */
165     }
166   else
167     {
168       g_set_error (error,
169                    G_IO_ERROR,
170                    G_IO_ERROR_INVALID_ARGUMENT,
171                    _("Address `%s' is invalid (need exactly one of path, tmpdir or abstract keys)"),
172                    address_entry);
173       goto out;
174     }
175
176
177   ret= TRUE;
178   goto out;
179
180  meaningless:
181   g_set_error (error,
182                G_IO_ERROR,
183                G_IO_ERROR_INVALID_ARGUMENT,
184                _("Meaningless key/value pair combination in address entry `%s'"),
185                address_entry);
186
187  out:
188   g_list_free (keys);
189
190   return ret;
191 }
192
193 static gboolean
194 is_valid_nonce_tcp (const gchar  *address_entry,
195                     GHashTable   *key_value_pairs,
196                     GError      **error)
197 {
198   gboolean ret;
199   GList *keys;
200   GList *l;
201   const gchar *host;
202   const gchar *port;
203   const gchar *family;
204   const gchar *nonce_file;
205   gint port_num;
206   gchar *endp;
207
208   ret = FALSE;
209   keys = NULL;
210   host = NULL;
211   port = NULL;
212   family = NULL;
213   nonce_file = NULL;
214
215   keys = g_hash_table_get_keys (key_value_pairs);
216   for (l = keys; l != NULL; l = l->next)
217     {
218       const gchar *key = l->data;
219       if (g_strcmp0 (key, "host") == 0)
220         host = g_hash_table_lookup (key_value_pairs, key);
221       else if (g_strcmp0 (key, "port") == 0)
222         port = g_hash_table_lookup (key_value_pairs, key);
223       else if (g_strcmp0 (key, "family") == 0)
224         family = g_hash_table_lookup (key_value_pairs, key);
225       else if (g_strcmp0 (key, "noncefile") == 0)
226         nonce_file = g_hash_table_lookup (key_value_pairs, key);
227       else
228         {
229           g_set_error (error,
230                        G_IO_ERROR,
231                        G_IO_ERROR_INVALID_ARGUMENT,
232                        _("Unsupported key `%s' in address entry `%s'"),
233                        key,
234                        address_entry);
235           goto out;
236         }
237     }
238
239   if (port != NULL)
240     {
241       port_num = strtol (port, &endp, 10);
242       if ((*port == '\0' || *endp != '\0') || port_num < 0 || port_num >= 65536)
243         {
244           g_set_error (error,
245                        G_IO_ERROR,
246                        G_IO_ERROR_INVALID_ARGUMENT,
247                        _("Error in address `%s' - the port attribute is malformed"),
248                        address_entry);
249           goto out;
250         }
251     }
252
253   if (family != NULL && !(g_strcmp0 (family, "ipv4") == 0 || g_strcmp0 (family, "ipv6") == 0))
254     {
255       g_set_error (error,
256                    G_IO_ERROR,
257                    G_IO_ERROR_INVALID_ARGUMENT,
258                    _("Error in address `%s' - the family attribute is malformed"),
259                    address_entry);
260       goto out;
261     }
262
263   ret= TRUE;
264
265  out:
266   g_list_free (keys);
267
268   return ret;
269 }
270
271 static gboolean
272 is_valid_tcp (const gchar  *address_entry,
273               GHashTable   *key_value_pairs,
274               GError      **error)
275 {
276   gboolean ret;
277   GList *keys;
278   GList *l;
279   const gchar *host;
280   const gchar *port;
281   const gchar *family;
282   gint port_num;
283   gchar *endp;
284
285   ret = FALSE;
286   keys = NULL;
287   host = NULL;
288   port = NULL;
289   family = NULL;
290
291   keys = g_hash_table_get_keys (key_value_pairs);
292   for (l = keys; l != NULL; l = l->next)
293     {
294       const gchar *key = l->data;
295       if (g_strcmp0 (key, "host") == 0)
296         host = g_hash_table_lookup (key_value_pairs, key);
297       else if (g_strcmp0 (key, "port") == 0)
298         port = g_hash_table_lookup (key_value_pairs, key);
299       else if (g_strcmp0 (key, "family") == 0)
300         family = g_hash_table_lookup (key_value_pairs, key);
301       else
302         {
303           g_set_error (error,
304                        G_IO_ERROR,
305                        G_IO_ERROR_INVALID_ARGUMENT,
306                        _("Unsupported key `%s' in address entry `%s'"),
307                        key,
308                        address_entry);
309           goto out;
310         }
311     }
312
313   if (port != NULL)
314     {
315       port_num = strtol (port, &endp, 10);
316       if ((*port == '\0' || *endp != '\0') || port_num < 0 || port_num >= 65536)
317         {
318           g_set_error (error,
319                        G_IO_ERROR,
320                        G_IO_ERROR_INVALID_ARGUMENT,
321                        _("Error in address `%s' - the port attribute is malformed"),
322                        address_entry);
323           goto out;
324         }
325     }
326
327   if (family != NULL && !(g_strcmp0 (family, "ipv4") == 0 || g_strcmp0 (family, "ipv6") == 0))
328     {
329       g_set_error (error,
330                    G_IO_ERROR,
331                    G_IO_ERROR_INVALID_ARGUMENT,
332                    _("Error in address `%s' - the family attribute is malformed"),
333                    address_entry);
334       goto out;
335     }
336
337   ret= TRUE;
338
339  out:
340   g_list_free (keys);
341
342   return ret;
343 }
344
345 /**
346  * g_dbus_is_supported_address:
347  * @string: A string.
348  * @error: Return location for error or %NULL.
349  *
350  * Like g_dbus_is_address() but also checks if the library suppors the
351  * transports in @string and that key/value pairs for each transport
352  * are valid.
353  *
354  * Returns: %TRUE if @string is a valid D-Bus address that is
355  * supported by this library, %FALSE if @error is set.
356  *
357  * Since: 2.26
358  */
359 gboolean
360 g_dbus_is_supported_address (const gchar  *string,
361                              GError      **error)
362 {
363   guint n;
364   gchar **a;
365   gboolean ret;
366
367   ret = FALSE;
368
369   g_return_val_if_fail (string != NULL, FALSE);
370   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
371
372   a = g_strsplit (string, ";", 0);
373   for (n = 0; a[n] != NULL; n++)
374     {
375       gchar *transport_name;
376       GHashTable *key_value_pairs;
377       gboolean supported;
378
379       if (!_g_dbus_address_parse_entry (a[n],
380                                         &transport_name,
381                                         &key_value_pairs,
382                                         error))
383         goto out;
384
385       supported = FALSE;
386       if (g_strcmp0 (transport_name, "unix") == 0)
387         supported = is_valid_unix (a[n], key_value_pairs, error);
388       else if (g_strcmp0 (transport_name, "tcp") == 0)
389         supported = is_valid_tcp (a[n], key_value_pairs, error);
390       else if (g_strcmp0 (transport_name, "nonce-tcp") == 0)
391         supported = is_valid_nonce_tcp (a[n], key_value_pairs, error);
392       else if (g_strcmp0 (a[n], "autolaunch:") == 0)
393         supported = TRUE;
394
395       g_free (transport_name);
396       g_hash_table_unref (key_value_pairs);
397
398       if (!supported)
399         goto out;
400     }
401
402   ret = TRUE;
403
404  out:
405   g_strfreev (a);
406
407   g_assert (ret || (!ret && (error == NULL || *error != NULL)));
408
409   return ret;
410 }
411
412 gboolean
413 _g_dbus_address_parse_entry (const gchar  *address_entry,
414                              gchar       **out_transport_name,
415                              GHashTable  **out_key_value_pairs,
416                              GError      **error)
417 {
418   gboolean ret;
419   GHashTable *key_value_pairs;
420   gchar *transport_name;
421   gchar **kv_pairs;
422   const gchar *s;
423   guint n;
424
425   ret = FALSE;
426   kv_pairs = NULL;
427   transport_name = NULL;
428   key_value_pairs = NULL;
429
430   s = strchr (address_entry, ':');
431   if (s == NULL)
432     {
433       g_set_error (error,
434                    G_IO_ERROR,
435                    G_IO_ERROR_INVALID_ARGUMENT,
436                    _("Address element `%s', does not contain a colon (:)"),
437                    address_entry);
438       goto out;
439     }
440
441   transport_name = g_strndup (address_entry, s - address_entry);
442   key_value_pairs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
443
444   kv_pairs = g_strsplit (s + 1, ",", 0);
445   for (n = 0; kv_pairs != NULL && kv_pairs[n] != NULL; n++)
446     {
447       const gchar *kv_pair = kv_pairs[n];
448       gchar *key;
449       gchar *value;
450
451       s = strchr (kv_pair, '=');
452       if (s == NULL)
453         {
454           g_set_error (error,
455                        G_IO_ERROR,
456                        G_IO_ERROR_INVALID_ARGUMENT,
457                        _("Key/Value pair %d, `%s', in address element `%s', does not contain an equal sign"),
458                        n,
459                        kv_pair,
460                        address_entry);
461           goto out;
462         }
463
464       /* TODO: actually validate that no illegal characters are present before and after then '=' sign */
465       key = g_uri_unescape_segment (kv_pair, s, NULL);
466       value = g_uri_unescape_segment (s + 1, kv_pair + strlen (kv_pair), NULL);
467       g_hash_table_insert (key_value_pairs, key, value);
468     }
469
470   ret = TRUE;
471
472 out:
473   g_strfreev (kv_pairs);
474   if (ret)
475     {
476       if (out_transport_name != NULL)
477         *out_transport_name = transport_name;
478       else
479         g_free (transport_name);
480       if (out_key_value_pairs != NULL)
481         *out_key_value_pairs = key_value_pairs;
482       else if (key_value_pairs != NULL)
483         g_hash_table_unref (key_value_pairs);
484     }
485   else
486     {
487       g_free (transport_name);
488       if (key_value_pairs != NULL)
489         g_hash_table_unref (key_value_pairs);
490     }
491   return ret;
492 }
493
494 /* ---------------------------------------------------------------------------------------------------- */
495
496 static GIOStream *
497 g_dbus_address_try_connect_one (const gchar   *address_entry,
498                                 gchar        **out_guid,
499                                 GCancellable  *cancellable,
500                                 GError       **error);
501
502 /* TODO: Declare an extension point called GDBusTransport (or similar)
503  * and move code below to extensions implementing said extension
504  * point. That way we can implement a D-Bus transport over X11 without
505  * making libgio link to libX11...
506  */
507 static GIOStream *
508 g_dbus_address_connect (const gchar   *address_entry,
509                         const gchar   *transport_name,
510                         GHashTable    *key_value_pairs,
511                         GCancellable  *cancellable,
512                         GError       **error)
513 {
514   GIOStream *ret;
515   GSocketConnectable *connectable;
516   const gchar *nonce_file;
517
518   connectable = NULL;
519   ret = NULL;
520   nonce_file = NULL;
521
522   if (FALSE)
523     {
524     }
525 #ifdef G_OS_UNIX
526   else if (g_strcmp0 (transport_name, "unix") == 0)
527     {
528       const gchar *path;
529       const gchar *abstract;
530       path = g_hash_table_lookup (key_value_pairs, "path");
531       abstract = g_hash_table_lookup (key_value_pairs, "abstract");
532       if ((path == NULL && abstract == NULL) || (path != NULL && abstract != NULL))
533         {
534           g_set_error (error,
535                        G_IO_ERROR,
536                        G_IO_ERROR_INVALID_ARGUMENT,
537                        _("Error in address `%s' - the unix transport requires exactly one of the "
538                          "keys `path' or `abstract' to be set"),
539                        address_entry);
540         }
541       else if (path != NULL)
542         {
543           connectable = G_SOCKET_CONNECTABLE (g_unix_socket_address_new (path));
544         }
545       else if (abstract != NULL)
546         {
547           connectable = G_SOCKET_CONNECTABLE (g_unix_socket_address_new_with_type (abstract,
548                                                                                    -1,
549                                                                                    G_UNIX_SOCKET_ADDRESS_ABSTRACT));
550         }
551       else
552         {
553           g_assert_not_reached ();
554         }
555     }
556 #endif
557   else if (g_strcmp0 (transport_name, "tcp") == 0 || g_strcmp0 (transport_name, "nonce-tcp") == 0)
558     {
559       const gchar *s;
560       const gchar *host;
561       guint port;
562       gchar *endp;
563       gboolean is_nonce;
564
565       is_nonce = (g_strcmp0 (transport_name, "nonce-tcp") == 0);
566
567       host = g_hash_table_lookup (key_value_pairs, "host");
568       if (host == NULL)
569         {
570           g_set_error (error,
571                        G_IO_ERROR,
572                        G_IO_ERROR_INVALID_ARGUMENT,
573                        _("Error in address `%s' - the host attribute is missing or malformed"),
574                        address_entry);
575           goto out;
576         }
577
578       s = g_hash_table_lookup (key_value_pairs, "port");
579       if (s == NULL)
580         s = "0";
581       port = strtol (s, &endp, 10);
582       if ((*s == '\0' || *endp != '\0') || port < 0 || port >= 65536)
583         {
584           g_set_error (error,
585                        G_IO_ERROR,
586                        G_IO_ERROR_INVALID_ARGUMENT,
587                        _("Error in address `%s' - the port attribute is missing or malformed"),
588                        address_entry);
589           goto out;
590         }
591
592
593       if (is_nonce)
594         {
595           nonce_file = g_hash_table_lookup (key_value_pairs, "noncefile");
596           if (nonce_file == NULL)
597             {
598               g_set_error (error,
599                            G_IO_ERROR,
600                            G_IO_ERROR_INVALID_ARGUMENT,
601                            _("Error in address `%s' - the noncefile attribute is missing or malformed"),
602                            address_entry);
603               goto out;
604             }
605         }
606
607       /* TODO: deal with family */
608       connectable = g_network_address_new (host, port);
609     }
610   else if (g_strcmp0 (address_entry, "autolaunch:") == 0)
611     {
612       gchar *autolaunch_address;
613       autolaunch_address = get_session_address_platform_specific (error);
614       if (autolaunch_address != NULL)
615         {
616           ret = g_dbus_address_try_connect_one (autolaunch_address, NULL, cancellable, error);
617           g_free (autolaunch_address);
618           goto out;
619         }
620       else
621         {
622           g_prefix_error (error, _("Error auto-launching: "));
623         }
624     }
625   else
626     {
627       g_set_error (error,
628                    G_IO_ERROR,
629                    G_IO_ERROR_INVALID_ARGUMENT,
630                    _("Unknown or unsupported transport `%s' for address `%s'"),
631                    transport_name,
632                    address_entry);
633     }
634
635   if (connectable != NULL)
636     {
637       GSocketClient *client;
638       GSocketConnection *connection;
639
640       g_assert (ret == NULL);
641       client = g_socket_client_new ();
642       connection = g_socket_client_connect (client,
643                                             connectable,
644                                             cancellable,
645                                             error);
646       g_object_unref (connectable);
647       g_object_unref (client);
648       if (connection == NULL)
649         goto out;
650
651       ret = G_IO_STREAM (connection);
652
653       if (nonce_file != NULL)
654         {
655           gchar *nonce_contents;
656           gsize nonce_length;
657
658           /* TODO: too dangerous to read the entire file? (think denial-of-service etc.) */
659           if (!g_file_get_contents (nonce_file,
660                                     &nonce_contents,
661                                     &nonce_length,
662                                     error))
663             {
664               g_prefix_error (error, _("Error reading nonce file `%s':"), nonce_file);
665               g_object_unref (ret);
666               ret = NULL;
667               goto out;
668             }
669
670           if (nonce_length != 16)
671             {
672               g_set_error (error,
673                            G_IO_ERROR,
674                            G_IO_ERROR_INVALID_ARGUMENT,
675                            _("The nonce-file `%s' was %" G_GSIZE_FORMAT " 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 }
1266
1267 #define __G_DBUS_ADDRESS_C__
1268 #include "gioaliasdef.c"