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