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