Add missing allow-none annotations for function parameters.
[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: (allow-none): 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   g_simple_async_result_set_check_cancellable (res, cancellable);
860   data = g_new0 (GetStreamData, 1);
861   data->address = g_strdup (address);
862   g_simple_async_result_set_op_res_gpointer (res,
863                                              data,
864                                              (GDestroyNotify) get_stream_data_free);
865   g_simple_async_result_run_in_thread (res,
866                                        get_stream_thread_func,
867                                        G_PRIORITY_DEFAULT,
868                                        cancellable);
869   g_object_unref (res);
870 }
871
872 /**
873  * g_dbus_address_get_stream_finish:
874  * @res: A #GAsyncResult obtained from the GAsyncReadyCallback passed to g_dbus_address_get_stream().
875  * @out_guid: %NULL or return location to store the GUID extracted from @address, if any.
876  * @error: Return location for error or %NULL.
877  *
878  * Finishes an operation started with g_dbus_address_get_stream().
879  *
880  * Returns: (transfer full): A #GIOStream or %NULL if @error is set.
881  *
882  * Since: 2.26
883  */
884 GIOStream *
885 g_dbus_address_get_stream_finish (GAsyncResult        *res,
886                                   gchar              **out_guid,
887                                   GError             **error)
888 {
889   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
890   GetStreamData *data;
891   GIOStream *ret;
892
893   g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
894   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
895
896   g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_dbus_address_get_stream);
897
898   ret = NULL;
899
900   data = g_simple_async_result_get_op_res_gpointer (simple);
901   if (g_simple_async_result_propagate_error (simple, error))
902     goto out;
903
904   ret = g_object_ref (data->stream);
905   if (out_guid != NULL)
906     *out_guid = g_strdup (data->guid);
907
908  out:
909   return ret;
910 }
911
912 /**
913  * g_dbus_address_get_stream_sync:
914  * @address: A valid D-Bus address.
915  * @out_guid: %NULL or return location to store the GUID extracted from @address, if any.
916  * @cancellable: (allow-none): A #GCancellable or %NULL.
917  * @error: Return location for error or %NULL.
918  *
919  * Synchronously connects to an endpoint specified by @address and
920  * sets up the connection so it is in a state to run the client-side
921  * of the D-Bus authentication conversation.
922  *
923  * This is a synchronous failable function. See
924  * g_dbus_address_get_stream() for the asynchronous version.
925  *
926  * Returns: (transfer full): A #GIOStream or %NULL if @error is set.
927  *
928  * Since: 2.26
929  */
930 GIOStream *
931 g_dbus_address_get_stream_sync (const gchar   *address,
932                                 gchar        **out_guid,
933                                 GCancellable  *cancellable,
934                                 GError       **error)
935 {
936   GIOStream *ret;
937   gchar **addr_array;
938   guint n;
939   GError *last_error;
940
941   g_return_val_if_fail (address != NULL, NULL);
942   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
943
944   ret = NULL;
945   last_error = NULL;
946
947   addr_array = g_strsplit (address, ";", 0);
948   if (addr_array != NULL && addr_array[0] == NULL)
949     {
950       last_error = g_error_new_literal (G_IO_ERROR,
951                                         G_IO_ERROR_INVALID_ARGUMENT,
952                                         _("The given address is empty"));
953       goto out;
954     }
955
956   for (n = 0; addr_array != NULL && addr_array[n] != NULL; n++)
957     {
958       const gchar *addr = addr_array[n];
959       GError *this_error;
960
961       this_error = NULL;
962       ret = g_dbus_address_try_connect_one (addr,
963                                             out_guid,
964                                             cancellable,
965                                             &this_error);
966       if (ret != NULL)
967         {
968           goto out;
969         }
970       else
971         {
972           g_assert (this_error != NULL);
973           if (last_error != NULL)
974             g_error_free (last_error);
975           last_error = this_error;
976         }
977     }
978
979  out:
980   if (ret != NULL)
981     {
982       if (last_error != NULL)
983         g_error_free (last_error);
984     }
985   else
986     {
987       g_assert (last_error != NULL);
988       g_propagate_error (error, last_error);
989     }
990
991   g_strfreev (addr_array);
992   return ret;
993 }
994
995 /* ---------------------------------------------------------------------------------------------------- */
996
997 #ifdef G_OS_UNIX
998 static gchar *
999 get_session_address_dbus_launch (GError **error)
1000 {
1001   gchar *ret;
1002   gchar *machine_id;
1003   gchar *command_line;
1004   gchar *launch_stdout;
1005   gchar *launch_stderr;
1006   gint exit_status;
1007   gchar *old_dbus_verbose;
1008   gboolean restore_dbus_verbose;
1009
1010   ret = NULL;
1011   machine_id = NULL;
1012   command_line = NULL;
1013   launch_stdout = NULL;
1014   launch_stderr = NULL;
1015   restore_dbus_verbose = FALSE;
1016   old_dbus_verbose = NULL;
1017
1018   machine_id = _g_dbus_get_machine_id (error);
1019   if (machine_id == NULL)
1020     {
1021       g_prefix_error (error, _("Cannot spawn a message bus without a machine-id: "));
1022       goto out;
1023     }
1024
1025   /* We're using private libdbus facilities here. When everything
1026    * (X11, Mac OS X, Windows) is spec'ed out correctly (not even the
1027    * X11 property is correctly documented right now) we should
1028    * consider using the spec instead of dbus-launch.
1029    *
1030    *   --autolaunch=MACHINEID
1031    *          This option implies that dbus-launch should scan  for  a  previ‐
1032    *          ously-started  session  and  reuse the values found there. If no
1033    *          session is found, it will start a new session. The  --exit-with-
1034    *          session option is implied if --autolaunch is given.  This option
1035    *          is for the exclusive use of libdbus, you do not want to  use  it
1036    *          manually. It may change in the future.
1037    */
1038
1039   /* TODO: maybe provide a variable for where to look for the dbus-launch binary? */
1040   command_line = g_strdup_printf ("dbus-launch --autolaunch=%s --binary-syntax --close-stderr", machine_id);
1041
1042   if (G_UNLIKELY (_g_dbus_debug_address ()))
1043     {
1044       _g_dbus_debug_print_lock ();
1045       g_print ("GDBus-debug:Address: Running `%s' to get bus address (possibly autolaunching)\n", command_line);
1046       old_dbus_verbose = g_strdup (g_getenv ("DBUS_VERBOSE"));
1047       restore_dbus_verbose = TRUE;
1048       g_setenv ("DBUS_VERBOSE", "1", TRUE);
1049       _g_dbus_debug_print_unlock ();
1050     }
1051
1052   if (!g_spawn_command_line_sync (command_line,
1053                                   &launch_stdout,
1054                                   &launch_stderr,
1055                                   &exit_status,
1056                                   error))
1057     {
1058       g_prefix_error (error, _("Error spawning command line `%s': "), command_line);
1059       goto out;
1060     }
1061
1062   if (!WIFEXITED (exit_status))
1063     {
1064       gchar *escaped_stderr;
1065       escaped_stderr = g_strescape (launch_stderr, "");
1066       g_set_error (error,
1067                    G_IO_ERROR,
1068                    G_IO_ERROR_FAILED,
1069                    _("Abnormal program termination spawning command line `%s': %s"),
1070                    command_line,
1071                    escaped_stderr);
1072       g_free (escaped_stderr);
1073       goto out;
1074     }
1075
1076   if (WEXITSTATUS (exit_status) != 0)
1077     {
1078       gchar *escaped_stderr;
1079       escaped_stderr = g_strescape (launch_stderr, "");
1080       g_set_error (error,
1081                    G_IO_ERROR,
1082                    G_IO_ERROR_FAILED,
1083                    _("Command line `%s' exited with non-zero exit status %d: %s"),
1084                    command_line,
1085                    WEXITSTATUS (exit_status),
1086                    escaped_stderr);
1087       g_free (escaped_stderr);
1088       goto out;
1089     }
1090
1091   /* From the dbus-launch(1) man page:
1092    *
1093    *   --binary-syntax Write to stdout a nul-terminated bus address,
1094    *   then the bus PID as a binary integer of size sizeof(pid_t),
1095    *   then the bus X window ID as a binary integer of size
1096    *   sizeof(long).  Integers are in the machine's byte order, not
1097    *   network byte order or any other canonical byte order.
1098    */
1099   ret = g_strdup (launch_stdout);
1100
1101  out:
1102   if (G_UNLIKELY (_g_dbus_debug_address ()))
1103     {
1104       gchar *s;
1105       _g_dbus_debug_print_lock ();
1106       g_print ("GDBus-debug:Address: dbus-launch output:");
1107       if (launch_stdout != NULL)
1108         {
1109           s = _g_dbus_hexdump (launch_stdout, strlen (launch_stdout) + 1 + sizeof (pid_t) + sizeof (long), 2);
1110           g_print ("\n%s", s);
1111           g_free (s);
1112         }
1113       else
1114         {
1115           g_print (" (none)\n");
1116         }
1117       g_print ("GDBus-debug:Address: dbus-launch stderr output:");
1118       if (launch_stderr != NULL)
1119         g_print ("\n%s", launch_stderr);
1120       else
1121         g_print (" (none)\n");
1122       _g_dbus_debug_print_unlock ();
1123     }
1124
1125   g_free (machine_id);
1126   g_free (command_line);
1127   g_free (launch_stdout);
1128   g_free (launch_stderr);
1129   if (G_UNLIKELY (restore_dbus_verbose))
1130     {
1131       if (old_dbus_verbose != NULL)
1132         g_setenv ("DBUS_VERBOSE", old_dbus_verbose, TRUE);
1133       else
1134         g_unsetenv ("DBUS_VERBOSE");
1135     }
1136   g_free (old_dbus_verbose);
1137   return ret;
1138 }
1139 #endif
1140
1141 /* ---------------------------------------------------------------------------------------------------- */
1142
1143 static gchar *
1144 get_session_address_platform_specific (GError **error)
1145 {
1146   gchar *ret;
1147 #ifdef G_OS_UNIX
1148   /* need to handle OS X in a different way since `dbus-launch --autolaunch' probably won't work there */
1149   ret = get_session_address_dbus_launch (error);
1150 #else
1151   /* TODO: implement for UNIX, Win32 and OS X */
1152   ret = NULL;
1153   g_set_error (error,
1154                G_IO_ERROR,
1155                G_IO_ERROR_FAILED,
1156                _("Cannot determine session bus address (not implemented for this OS)"));
1157 #endif
1158   return ret;
1159 }
1160
1161 /* ---------------------------------------------------------------------------------------------------- */
1162
1163 /**
1164  * g_dbus_address_get_for_bus_sync:
1165  * @bus_type: A #GBusType.
1166  * @cancellable: (allow-none): A #GCancellable or %NULL.
1167  * @error: Return location for error or %NULL.
1168  *
1169  * Synchronously looks up the D-Bus address for the well-known message
1170  * bus instance specified by @bus_type. This may involve using various
1171  * platform specific mechanisms.
1172  *
1173  * Returns: A valid D-Bus address string for @bus_type or %NULL if @error is set.
1174  *
1175  * Since: 2.26
1176  */
1177 gchar *
1178 g_dbus_address_get_for_bus_sync (GBusType       bus_type,
1179                                  GCancellable  *cancellable,
1180                                  GError       **error)
1181 {
1182   gchar *ret;
1183   const gchar *starter_bus;
1184   GError *local_error;
1185
1186   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1187
1188   ret = NULL;
1189   local_error = NULL;
1190
1191   if (G_UNLIKELY (_g_dbus_debug_address ()))
1192     {
1193       guint n;
1194       _g_dbus_debug_print_lock ();
1195       g_print ("GDBus-debug:Address: In g_dbus_address_get_for_bus_sync() for bus type `%s'\n",
1196                _g_dbus_enum_to_string (G_TYPE_BUS_TYPE, bus_type));
1197       for (n = 0; n < 3; n++)
1198         {
1199           const gchar *k;
1200           const gchar *v;
1201           switch (n)
1202             {
1203             case 0: k = "DBUS_SESSION_BUS_ADDRESS"; break;
1204             case 1: k = "DBUS_SYSTEM_BUS_ADDRESS"; break;
1205             case 2: k = "DBUS_STARTER_BUS_TYPE"; break;
1206             default: g_assert_not_reached ();
1207             }
1208           v = g_getenv (k);
1209           g_print ("GDBus-debug:Address: env var %s", k);
1210           if (v != NULL)
1211             g_print ("=`%s'\n", v);
1212           else
1213             g_print (" is not set\n");
1214         }
1215       _g_dbus_debug_print_unlock ();
1216     }
1217
1218   switch (bus_type)
1219     {
1220     case G_BUS_TYPE_SYSTEM:
1221       ret = g_strdup (g_getenv ("DBUS_SYSTEM_BUS_ADDRESS"));
1222       if (ret == NULL)
1223         {
1224           ret = g_strdup ("unix:path=/var/run/dbus/system_bus_socket");
1225         }
1226       break;
1227
1228     case G_BUS_TYPE_SESSION:
1229       ret = g_strdup (g_getenv ("DBUS_SESSION_BUS_ADDRESS"));
1230       if (ret == NULL)
1231         {
1232           ret = get_session_address_platform_specific (&local_error);
1233         }
1234       break;
1235
1236     case G_BUS_TYPE_STARTER:
1237       starter_bus = g_getenv ("DBUS_STARTER_BUS_TYPE");
1238       if (g_strcmp0 (starter_bus, "session") == 0)
1239         {
1240           ret = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SESSION, cancellable, &local_error);
1241           goto out;
1242         }
1243       else if (g_strcmp0 (starter_bus, "system") == 0)
1244         {
1245           ret = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SYSTEM, cancellable, &local_error);
1246           goto out;
1247         }
1248       else
1249         {
1250           if (starter_bus != NULL)
1251             {
1252               g_set_error (&local_error,
1253                            G_IO_ERROR,
1254                            G_IO_ERROR_FAILED,
1255                            _("Cannot determine bus address from DBUS_STARTER_BUS_TYPE environment variable"
1256                              " - unknown value `%s'"),
1257                            starter_bus);
1258             }
1259           else
1260             {
1261               g_set_error_literal (&local_error,
1262                                    G_IO_ERROR,
1263                                    G_IO_ERROR_FAILED,
1264                                    _("Cannot determine bus address because the DBUS_STARTER_BUS_TYPE environment "
1265                                      "variable is not set"));
1266             }
1267         }
1268       break;
1269
1270     default:
1271       g_set_error (&local_error,
1272                    G_IO_ERROR,
1273                    G_IO_ERROR_FAILED,
1274                    _("Unknown bus type %d"),
1275                    bus_type);
1276       break;
1277     }
1278
1279  out:
1280   if (G_UNLIKELY (_g_dbus_debug_address ()))
1281     {
1282       _g_dbus_debug_print_lock ();
1283       if (ret != NULL)
1284         {
1285           g_print ("GDBus-debug:Address: Returning address `%s' for bus type `%s'\n",
1286                    ret,
1287                    _g_dbus_enum_to_string (G_TYPE_BUS_TYPE, bus_type));
1288         }
1289       else
1290         {
1291           g_print ("GDBus-debug:Address: Cannot look-up address bus type `%s': %s\n",
1292                    _g_dbus_enum_to_string (G_TYPE_BUS_TYPE, bus_type),
1293                    local_error ? local_error->message : "");
1294         }
1295       _g_dbus_debug_print_unlock ();
1296     }
1297
1298   if (local_error != NULL)
1299     g_propagate_error (error, local_error);
1300
1301   return ret;
1302 }