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