Change LGPL-2.1+ to LGPL-2.1-or-later
[platform/upstream/glib.git] / gio / gdbusaddress.c
1 /* GDBus - GLib D-Bus Library
2  *
3  * Copyright (C) 2008-2010 Red Hat, Inc.
4  *
5  * SPDX-License-Identifier: LGPL-2.1-or-later
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General
18  * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
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 "glib-private.h"
36 #include "gnetworkaddress.h"
37 #include "gsocketclient.h"
38 #include "giostream.h"
39 #include "gasyncresult.h"
40 #include "gtask.h"
41 #include "glib-private.h"
42 #include "gdbusprivate.h"
43 #include "gstdio.h"
44
45 #ifdef HAVE_UNISTD_H
46 #include <unistd.h>
47 #endif
48 #include <sys/stat.h>
49 #include <sys/types.h>
50 #include <gio/gunixsocketaddress.h>
51
52 #ifdef G_OS_WIN32
53 #include <windows.h>
54 #endif
55
56 #ifdef G_OS_WIN32
57 #define FO_CLOEXEC ""
58 #else
59 #define FO_CLOEXEC "e"
60 #endif
61
62 #include "glibintl.h"
63
64 /**
65  * SECTION:gdbusaddress
66  * @title: D-Bus Addresses
67  * @short_description: D-Bus connection endpoints
68  * @include: gio/gio.h
69  *
70  * Routines for working with D-Bus addresses. A D-Bus address is a string
71  * like `unix:tmpdir=/tmp/my-app-name`. The exact format of addresses
72  * is explained in detail in the
73  * [D-Bus specification](http://dbus.freedesktop.org/doc/dbus-specification.html#addresses).
74  *
75  * TCP D-Bus connections are supported, but accessing them via a proxy is
76  * currently not supported.
77  *
78  * Since GLib 2.72, `unix:` addresses are supported on Windows with `AF_UNIX`
79  * support (Windows 10).
80  */
81
82 static gchar *get_session_address_platform_specific (GError **error);
83 static gchar *get_session_address_dbus_launch       (GError **error);
84
85 /* ---------------------------------------------------------------------------------------------------- */
86
87 /**
88  * g_dbus_is_address:
89  * @string: A string.
90  *
91  * Checks if @string is a
92  * [D-Bus address](https://dbus.freedesktop.org/doc/dbus-specification.html#addresses).
93  *
94  * This doesn't check if @string is actually supported by #GDBusServer
95  * or #GDBusConnection - use g_dbus_is_supported_address() to do more
96  * checks.
97  *
98  * Returns: %TRUE if @string is a valid D-Bus address, %FALSE otherwise.
99  *
100  * Since: 2.26
101  */
102 gboolean
103 g_dbus_is_address (const gchar *string)
104 {
105   guint n;
106   gchar **a;
107   gboolean ret;
108
109   ret = FALSE;
110
111   g_return_val_if_fail (string != NULL, FALSE);
112
113   a = g_strsplit (string, ";", 0);
114   if (a[0] == NULL)
115     goto out;
116
117   for (n = 0; a[n] != NULL; n++)
118     {
119       if (!_g_dbus_address_parse_entry (a[n],
120                                         NULL,
121                                         NULL,
122                                         NULL))
123         goto out;
124     }
125
126   ret = TRUE;
127
128  out:
129   g_strfreev (a);
130   return ret;
131 }
132
133 static gboolean
134 is_valid_unix (const gchar  *address_entry,
135                GHashTable   *key_value_pairs,
136                GError      **error)
137 {
138   gboolean ret;
139   GPtrArray *keys;
140   const gchar *path;
141   const gchar *dir;
142   const gchar *tmpdir;
143   const gchar *abstract;
144
145   ret = FALSE;
146   path = NULL;
147   dir = NULL;
148   tmpdir = NULL;
149   abstract = NULL;
150
151   keys = g_hash_table_get_keys_as_ptr_array (key_value_pairs);
152   for (guint i = 0; i < keys->len; ++i)
153     {
154       const gchar *key = g_ptr_array_index (keys, i);
155       if (g_strcmp0 (key, "path") == 0)
156         path = g_hash_table_lookup (key_value_pairs, key);
157       else if (g_strcmp0 (key, "dir") == 0)
158         dir = g_hash_table_lookup (key_value_pairs, key);
159       else if (g_strcmp0 (key, "tmpdir") == 0)
160         tmpdir = g_hash_table_lookup (key_value_pairs, key);
161       else if (g_strcmp0 (key, "abstract") == 0)
162         abstract = g_hash_table_lookup (key_value_pairs, key);
163       else if (g_strcmp0 (key, "guid") != 0)
164         {
165           g_set_error (error,
166                        G_IO_ERROR,
167                        G_IO_ERROR_INVALID_ARGUMENT,
168                        _("Unsupported key “%s” in address entry “%s”"),
169                        key,
170                        address_entry);
171           goto out;
172         }
173     }
174
175   /* Exactly one key must be set */
176   if ((path != NULL) + (dir != NULL) + (tmpdir != NULL) + (abstract != NULL) > 1)
177     {
178       g_set_error (error,
179              G_IO_ERROR,
180              G_IO_ERROR_INVALID_ARGUMENT,
181              _("Meaningless key/value pair combination in address entry “%s”"),
182              address_entry);
183       goto out;
184     }
185   else if (path == NULL && dir == NULL && tmpdir == NULL && abstract == NULL)
186     {
187       g_set_error (error,
188                    G_IO_ERROR,
189                    G_IO_ERROR_INVALID_ARGUMENT,
190                    _("Address “%s” is invalid (need exactly one of path, dir, tmpdir, or abstract keys)"),
191                    address_entry);
192       goto out;
193     }
194
195   ret = TRUE;
196
197  out:
198   g_ptr_array_unref (keys);
199
200   return ret;
201 }
202
203 static gboolean
204 is_valid_nonce_tcp (const gchar  *address_entry,
205                     GHashTable   *key_value_pairs,
206                     GError      **error)
207 {
208   gboolean ret;
209   GPtrArray *keys;
210   const gchar *host;
211   const gchar *port;
212   const gchar *family;
213   const gchar *nonce_file;
214   gint port_num;
215   gchar *endp;
216
217   ret = FALSE;
218   host = NULL;
219   port = NULL;
220   family = NULL;
221   nonce_file = NULL;
222
223   keys = g_hash_table_get_keys_as_ptr_array (key_value_pairs);
224   for (guint i = 0; i < keys->len; ++i)
225     {
226       const gchar *key = g_ptr_array_index (keys, i);
227       if (g_strcmp0 (key, "host") == 0)
228         host = g_hash_table_lookup (key_value_pairs, key);
229       else if (g_strcmp0 (key, "port") == 0)
230         port = g_hash_table_lookup (key_value_pairs, key);
231       else if (g_strcmp0 (key, "family") == 0)
232         family = g_hash_table_lookup (key_value_pairs, key);
233       else if (g_strcmp0 (key, "noncefile") == 0)
234         nonce_file = g_hash_table_lookup (key_value_pairs, key);
235       else if (g_strcmp0 (key, "guid") != 0)
236         {
237           g_set_error (error,
238                        G_IO_ERROR,
239                        G_IO_ERROR_INVALID_ARGUMENT,
240                        _("Unsupported key “%s” in address entry “%s”"),
241                        key,
242                        address_entry);
243           goto out;
244         }
245     }
246
247   if (port != NULL)
248     {
249       port_num = strtol (port, &endp, 10);
250       if ((*port == '\0' || *endp != '\0') || port_num < 0 || port_num >= 65536)
251         {
252           g_set_error (error,
253                        G_IO_ERROR,
254                        G_IO_ERROR_INVALID_ARGUMENT,
255                        _("Error in address “%s” — the “%s” attribute is malformed"),
256                        address_entry, "port");
257           goto out;
258         }
259     }
260
261   if (family != NULL && !(g_strcmp0 (family, "ipv4") == 0 || g_strcmp0 (family, "ipv6") == 0))
262     {
263       g_set_error (error,
264                    G_IO_ERROR,
265                    G_IO_ERROR_INVALID_ARGUMENT,
266                    _("Error in address “%s” — the “%s” attribute is malformed"),
267                    address_entry, "family");
268       goto out;
269     }
270
271   if (host != NULL)
272     {
273       /* TODO: validate host */
274     }
275
276   if (nonce_file != NULL && *nonce_file == '\0')
277     {
278       g_set_error (error,
279                    G_IO_ERROR,
280                    G_IO_ERROR_INVALID_ARGUMENT,
281                    _("Error in address “%s” — the “%s” attribute is malformed"),
282                    address_entry, "noncefile");
283       goto out;
284     }
285
286   ret = TRUE;
287
288  out:
289   g_ptr_array_unref (keys);
290
291   return ret;
292 }
293
294 static gboolean
295 is_valid_tcp (const gchar  *address_entry,
296               GHashTable   *key_value_pairs,
297               GError      **error)
298 {
299   gboolean ret;
300   GPtrArray *keys;
301   const gchar *host;
302   const gchar *port;
303   const gchar *family;
304   gint port_num;
305   gchar *endp;
306
307   ret = FALSE;
308   host = NULL;
309   port = NULL;
310   family = NULL;
311
312   keys = g_hash_table_get_keys_as_ptr_array (key_value_pairs);
313   for (guint i = 0; i < keys->len; ++i)
314     {
315       const gchar *key = g_ptr_array_index (keys, i);
316       if (g_strcmp0 (key, "host") == 0)
317         host = g_hash_table_lookup (key_value_pairs, key);
318       else if (g_strcmp0 (key, "port") == 0)
319         port = g_hash_table_lookup (key_value_pairs, key);
320       else if (g_strcmp0 (key, "family") == 0)
321         family = g_hash_table_lookup (key_value_pairs, key);
322       else if (g_strcmp0 (key, "guid") != 0)
323         {
324           g_set_error (error,
325                        G_IO_ERROR,
326                        G_IO_ERROR_INVALID_ARGUMENT,
327                        _("Unsupported key “%s” in address entry “%s”"),
328                        key,
329                        address_entry);
330           goto out;
331         }
332     }
333
334   if (port != NULL)
335     {
336       port_num = strtol (port, &endp, 10);
337       if ((*port == '\0' || *endp != '\0') || port_num < 0 || port_num >= 65536)
338         {
339           g_set_error (error,
340                        G_IO_ERROR,
341                        G_IO_ERROR_INVALID_ARGUMENT,
342                        _("Error in address “%s” — the “%s” attribute is malformed"),
343                        address_entry, "port");
344           goto out;
345         }
346     }
347
348   if (family != NULL && !(g_strcmp0 (family, "ipv4") == 0 || g_strcmp0 (family, "ipv6") == 0))
349     {
350       g_set_error (error,
351                    G_IO_ERROR,
352                    G_IO_ERROR_INVALID_ARGUMENT,
353                    _("Error in address “%s” — the “%s” attribute is malformed"),
354                    address_entry, "family");
355       goto out;
356     }
357
358   if (host != NULL)
359     {
360       /* TODO: validate host */
361     }
362
363   ret= TRUE;
364
365  out:
366   g_ptr_array_unref (keys);
367
368   return ret;
369 }
370
371 /**
372  * g_dbus_is_supported_address:
373  * @string: A string.
374  * @error: Return location for error or %NULL.
375  *
376  * Like g_dbus_is_address() but also checks if the library supports the
377  * transports in @string and that key/value pairs for each transport
378  * are valid. See the specification of the
379  * [D-Bus address format](https://dbus.freedesktop.org/doc/dbus-specification.html#addresses).
380  *
381  * Returns: %TRUE if @string is a valid D-Bus address that is
382  * supported by this library, %FALSE if @error is set.
383  *
384  * Since: 2.26
385  */
386 gboolean
387 g_dbus_is_supported_address (const gchar  *string,
388                              GError      **error)
389 {
390   guint n;
391   gchar **a;
392   gboolean ret;
393
394   ret = FALSE;
395
396   g_return_val_if_fail (string != NULL, FALSE);
397   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
398
399   a = g_strsplit (string, ";", 0);
400   for (n = 0; a[n] != NULL; n++)
401     {
402       gchar *transport_name;
403       GHashTable *key_value_pairs;
404       gboolean supported;
405
406       if (!_g_dbus_address_parse_entry (a[n],
407                                         &transport_name,
408                                         &key_value_pairs,
409                                         error))
410         goto out;
411
412       supported = FALSE;
413       if (g_strcmp0 (transport_name, "unix") == 0)
414         supported = is_valid_unix (a[n], key_value_pairs, error);
415       else if (g_strcmp0 (transport_name, "tcp") == 0)
416         supported = is_valid_tcp (a[n], key_value_pairs, error);
417       else if (g_strcmp0 (transport_name, "nonce-tcp") == 0)
418         supported = is_valid_nonce_tcp (a[n], key_value_pairs, error);
419       else if (g_strcmp0 (a[n], "autolaunch:") == 0)
420         supported = TRUE;
421       else
422         g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
423                      _("Unknown or unsupported transport “%s” for address “%s”"),
424                      transport_name, a[n]);
425
426       g_free (transport_name);
427       g_hash_table_unref (key_value_pairs);
428
429       if (!supported)
430         goto out;
431     }
432
433   ret = TRUE;
434
435  out:
436   g_strfreev (a);
437
438   g_assert (ret || (!ret && (error == NULL || *error != NULL)));
439
440   return ret;
441 }
442
443 gboolean
444 _g_dbus_address_parse_entry (const gchar  *address_entry,
445                              gchar       **out_transport_name,
446                              GHashTable  **out_key_value_pairs,
447                              GError      **error)
448 {
449   gboolean ret;
450   GHashTable *key_value_pairs;
451   gchar *transport_name;
452   gchar **kv_pairs;
453   const gchar *s;
454   guint n;
455
456   ret = FALSE;
457   kv_pairs = NULL;
458   transport_name = NULL;
459   key_value_pairs = NULL;
460
461   s = strchr (address_entry, ':');
462   if (s == NULL)
463     {
464       g_set_error (error,
465                    G_IO_ERROR,
466                    G_IO_ERROR_INVALID_ARGUMENT,
467                    _("Address element “%s” does not contain a colon (:)"),
468                    address_entry);
469       goto out;
470     }
471   else if (s == address_entry)
472     {
473       g_set_error (error,
474                    G_IO_ERROR,
475                    G_IO_ERROR_INVALID_ARGUMENT,
476                    _("Transport name in address element “%s” must not be empty"),
477                    address_entry);
478       goto out;
479     }
480
481   transport_name = g_strndup (address_entry, s - address_entry);
482   key_value_pairs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
483
484   kv_pairs = g_strsplit (s + 1, ",", 0);
485   for (n = 0; kv_pairs[n] != NULL; n++)
486     {
487       const gchar *kv_pair = kv_pairs[n];
488       gchar *key;
489       gchar *value;
490
491       s = strchr (kv_pair, '=');
492       if (s == NULL)
493         {
494           g_set_error (error,
495                        G_IO_ERROR,
496                        G_IO_ERROR_INVALID_ARGUMENT,
497                        _("Key/Value pair %d, “%s”, in address element “%s” does not contain an equal sign"),
498                        n,
499                        kv_pair,
500                        address_entry);
501           goto out;
502         }
503       else if (s == kv_pair)
504         {
505           g_set_error (error,
506                        G_IO_ERROR,
507                        G_IO_ERROR_INVALID_ARGUMENT,
508                        _("Key/Value pair %d, “%s”, in address element “%s” must not have an empty key"),
509                        n,
510                        kv_pair,
511                        address_entry);
512           goto out;
513         }
514
515       key = g_uri_unescape_segment (kv_pair, s, NULL);
516       value = g_uri_unescape_segment (s + 1, kv_pair + strlen (kv_pair), NULL);
517       if (key == NULL || value == NULL)
518         {
519           g_set_error (error,
520                        G_IO_ERROR,
521                        G_IO_ERROR_INVALID_ARGUMENT,
522                        _("Error unescaping key or value in Key/Value pair %d, “%s”, in address element “%s”"),
523                        n,
524                        kv_pair,
525                        address_entry);
526           g_free (key);
527           g_free (value);
528           goto out;
529         }
530       g_hash_table_insert (key_value_pairs, key, value);
531     }
532
533   ret = TRUE;
534
535 out:
536   if (ret)
537     {
538       if (out_transport_name != NULL)
539         *out_transport_name = g_steal_pointer (&transport_name);
540       if (out_key_value_pairs != NULL)
541         *out_key_value_pairs = g_steal_pointer (&key_value_pairs);
542     }
543
544   g_clear_pointer (&key_value_pairs, g_hash_table_unref);
545   g_free (transport_name);
546   g_strfreev (kv_pairs);
547
548   return ret;
549 }
550
551 /* ---------------------------------------------------------------------------------------------------- */
552
553 static GIOStream *
554 g_dbus_address_try_connect_one (const gchar   *address_entry,
555                                 gchar        **out_guid,
556                                 GCancellable  *cancellable,
557                                 GError       **error);
558
559 /* TODO: Declare an extension point called GDBusTransport (or similar)
560  * and move code below to extensions implementing said extension
561  * point. That way we can implement a D-Bus transport over X11 without
562  * making libgio link to libX11...
563  */
564 static GIOStream *
565 g_dbus_address_connect (const gchar   *address_entry,
566                         const gchar   *transport_name,
567                         GHashTable    *key_value_pairs,
568                         GCancellable  *cancellable,
569                         GError       **error)
570 {
571   GIOStream *ret;
572   GSocketConnectable *connectable;
573   const gchar *nonce_file;
574
575   connectable = NULL;
576   ret = NULL;
577   nonce_file = NULL;
578
579   if (g_strcmp0 (transport_name, "unix") == 0)
580     {
581       const gchar *path;
582       const gchar *abstract;
583       path = g_hash_table_lookup (key_value_pairs, "path");
584       abstract = g_hash_table_lookup (key_value_pairs, "abstract");
585       if ((path == NULL && abstract == NULL) || (path != NULL && abstract != NULL))
586         {
587           g_set_error (error,
588                        G_IO_ERROR,
589                        G_IO_ERROR_INVALID_ARGUMENT,
590                        _("Error in address “%s” — the unix transport requires exactly one of the "
591                          "keys “path” or “abstract” to be set"),
592                        address_entry);
593         }
594       else if (path != NULL)
595         {
596           connectable = G_SOCKET_CONNECTABLE (g_unix_socket_address_new (path));
597         }
598       else if (abstract != NULL)
599         {
600           connectable = G_SOCKET_CONNECTABLE (g_unix_socket_address_new_with_type (abstract,
601                                                                                    -1,
602                                                                                    G_UNIX_SOCKET_ADDRESS_ABSTRACT));
603         }
604       else
605         {
606           g_assert_not_reached ();
607         }
608     }
609   else if (g_strcmp0 (transport_name, "tcp") == 0 || g_strcmp0 (transport_name, "nonce-tcp") == 0)
610     {
611       const gchar *s;
612       const gchar *host;
613       glong port;
614       gchar *endp;
615       gboolean is_nonce;
616
617       is_nonce = (g_strcmp0 (transport_name, "nonce-tcp") == 0);
618
619       host = g_hash_table_lookup (key_value_pairs, "host");
620       if (host == NULL)
621         {
622           g_set_error (error,
623                        G_IO_ERROR,
624                        G_IO_ERROR_INVALID_ARGUMENT,
625                        _("Error in address “%s” — the host attribute is missing or malformed"),
626                        address_entry);
627           goto out;
628         }
629
630       s = g_hash_table_lookup (key_value_pairs, "port");
631       if (s == NULL)
632         s = "0";
633       port = strtol (s, &endp, 10);
634       if ((*s == '\0' || *endp != '\0') || port < 0 || port >= 65536)
635         {
636           g_set_error (error,
637                        G_IO_ERROR,
638                        G_IO_ERROR_INVALID_ARGUMENT,
639                        _("Error in address “%s” — the port attribute is missing or malformed"),
640                        address_entry);
641           goto out;
642         }
643
644
645       if (is_nonce)
646         {
647           nonce_file = g_hash_table_lookup (key_value_pairs, "noncefile");
648           if (nonce_file == NULL)
649             {
650               g_set_error (error,
651                            G_IO_ERROR,
652                            G_IO_ERROR_INVALID_ARGUMENT,
653                            _("Error in address “%s” — the noncefile attribute is missing or malformed"),
654                            address_entry);
655               goto out;
656             }
657         }
658
659       /* TODO: deal with family key/value-pair */
660       connectable = g_network_address_new (host, port);
661     }
662   else if (g_strcmp0 (address_entry, "autolaunch:") == 0)
663     {
664       gchar *autolaunch_address;
665       autolaunch_address = get_session_address_dbus_launch (error);
666       if (autolaunch_address != NULL)
667         {
668           ret = g_dbus_address_try_connect_one (autolaunch_address, NULL, cancellable, error);
669           g_free (autolaunch_address);
670           goto out;
671         }
672       else
673         {
674           g_prefix_error (error, _("Error auto-launching: "));
675         }
676     }
677   else
678     {
679       g_set_error (error,
680                    G_IO_ERROR,
681                    G_IO_ERROR_INVALID_ARGUMENT,
682                    _("Unknown or unsupported transport “%s” for address “%s”"),
683                    transport_name,
684                    address_entry);
685     }
686
687   if (connectable != NULL)
688     {
689       GSocketClient *client;
690       GSocketConnection *connection;
691
692       g_assert (ret == NULL);
693       client = g_socket_client_new ();
694
695       /* Disable proxy support to prevent a deadlock on startup, since loading a
696        * proxy resolver causes the GIO modules to be loaded, and there will
697        * almost certainly be one of them which then tries to use GDBus.
698        * See: https://bugzilla.gnome.org/show_bug.cgi?id=792499 */
699       g_socket_client_set_enable_proxy (client, FALSE);
700
701       connection = g_socket_client_connect (client,
702                                             connectable,
703                                             cancellable,
704                                             error);
705       g_object_unref (connectable);
706       g_object_unref (client);
707       if (connection == NULL)
708         goto out;
709
710       ret = G_IO_STREAM (connection);
711
712       if (nonce_file != NULL)
713         {
714           gchar nonce_contents[16 + 1];
715           size_t num_bytes_read;
716           FILE *f;
717           int errsv;
718
719           /* be careful to read only 16 bytes - we also check that the file is only 16 bytes long */
720           f = fopen (nonce_file, "rb" FO_CLOEXEC);
721           errsv = errno;
722           if (f == NULL)
723             {
724               g_set_error (error,
725                            G_IO_ERROR,
726                            G_IO_ERROR_INVALID_ARGUMENT,
727                            _("Error opening nonce file “%s”: %s"),
728                            nonce_file,
729                            g_strerror (errsv));
730               g_object_unref (ret);
731               ret = NULL;
732               goto out;
733             }
734           num_bytes_read = fread (nonce_contents,
735                                   sizeof (gchar),
736                                   16 + 1,
737                                   f);
738           errsv = errno;
739           if (num_bytes_read != 16)
740             {
741               if (num_bytes_read == 0)
742                 {
743                   g_set_error (error,
744                                G_IO_ERROR,
745                                G_IO_ERROR_INVALID_ARGUMENT,
746                                _("Error reading from nonce file “%s”: %s"),
747                                nonce_file,
748                                g_strerror (errsv));
749                 }
750               else
751                 {
752                   g_set_error (error,
753                                G_IO_ERROR,
754                                G_IO_ERROR_INVALID_ARGUMENT,
755                                _("Error reading from nonce file “%s”, expected 16 bytes, got %d"),
756                                nonce_file,
757                                (gint) num_bytes_read);
758                 }
759               g_object_unref (ret);
760               ret = NULL;
761               fclose (f);
762               goto out;
763             }
764           fclose (f);
765
766           if (!g_output_stream_write_all (g_io_stream_get_output_stream (ret),
767                                           nonce_contents,
768                                           16,
769                                           NULL,
770                                           cancellable,
771                                           error))
772             {
773               g_prefix_error (error, _("Error writing contents of nonce file “%s” to stream:"), nonce_file);
774               g_object_unref (ret);
775               ret = NULL;
776               goto out;
777             }
778         }
779     }
780
781  out:
782
783   return ret;
784 }
785
786 static GIOStream *
787 g_dbus_address_try_connect_one (const gchar   *address_entry,
788                                 gchar        **out_guid,
789                                 GCancellable  *cancellable,
790                                 GError       **error)
791 {
792   GIOStream *ret;
793   GHashTable *key_value_pairs;
794   gchar *transport_name;
795   const gchar *guid;
796
797   ret = NULL;
798   transport_name = NULL;
799   key_value_pairs = NULL;
800
801   if (!_g_dbus_address_parse_entry (address_entry,
802                                     &transport_name,
803                                     &key_value_pairs,
804                                     error))
805     goto out;
806
807   ret = g_dbus_address_connect (address_entry,
808                                 transport_name,
809                                 key_value_pairs,
810                                 cancellable,
811                                 error);
812   if (ret == NULL)
813     goto out;
814
815   guid = g_hash_table_lookup (key_value_pairs, "guid");
816   if (guid != NULL && out_guid != NULL)
817     *out_guid = g_strdup (guid);
818
819 out:
820   g_free (transport_name);
821   if (key_value_pairs != NULL)
822     g_hash_table_unref (key_value_pairs);
823   return ret;
824 }
825
826
827 /* ---------------------------------------------------------------------------------------------------- */
828
829 typedef struct {
830   gchar *address;
831   gchar *guid;
832 } GetStreamData;
833
834 static void
835 get_stream_data_free (GetStreamData *data)
836 {
837   g_free (data->address);
838   g_free (data->guid);
839   g_free (data);
840 }
841
842 static void
843 get_stream_thread_func (GTask         *task,
844                         gpointer       source_object,
845                         gpointer       task_data,
846                         GCancellable  *cancellable)
847 {
848   GetStreamData *data = task_data;
849   GIOStream *stream;
850   GError *error = NULL;
851
852   stream = g_dbus_address_get_stream_sync (data->address,
853                                            &data->guid,
854                                            cancellable,
855                                            &error);
856   if (stream)
857     g_task_return_pointer (task, stream, g_object_unref);
858   else
859     g_task_return_error (task, error);
860 }
861
862 /**
863  * g_dbus_address_get_stream:
864  * @address: A valid D-Bus address.
865  * @cancellable: (nullable): A #GCancellable or %NULL.
866  * @callback: A #GAsyncReadyCallback to call when the request is satisfied.
867  * @user_data: Data to pass to @callback.
868  *
869  * Asynchronously connects to an endpoint specified by @address and
870  * sets up the connection so it is in a state to run the client-side
871  * of the D-Bus authentication conversation. @address must be in the
872  * [D-Bus address format](https://dbus.freedesktop.org/doc/dbus-specification.html#addresses).
873  *
874  * When the operation is finished, @callback will be invoked. You can
875  * then call g_dbus_address_get_stream_finish() to get the result of
876  * the operation.
877  *
878  * This is an asynchronous failable function. See
879  * g_dbus_address_get_stream_sync() for the synchronous version.
880  *
881  * Since: 2.26
882  */
883 void
884 g_dbus_address_get_stream (const gchar         *address,
885                            GCancellable        *cancellable,
886                            GAsyncReadyCallback  callback,
887                            gpointer             user_data)
888 {
889   GTask *task;
890   GetStreamData *data;
891
892   g_return_if_fail (address != NULL);
893
894   data = g_new0 (GetStreamData, 1);
895   data->address = g_strdup (address);
896
897   task = g_task_new (NULL, cancellable, callback, user_data);
898   g_task_set_source_tag (task, g_dbus_address_get_stream);
899   g_task_set_task_data (task, data, (GDestroyNotify) get_stream_data_free);
900   g_task_run_in_thread (task, get_stream_thread_func);
901   g_object_unref (task);
902 }
903
904 /**
905  * g_dbus_address_get_stream_finish:
906  * @res: A #GAsyncResult obtained from the GAsyncReadyCallback passed to g_dbus_address_get_stream().
907  * @out_guid: (optional) (out) (nullable): %NULL or return location to store the GUID extracted from @address, if any.
908  * @error: Return location for error or %NULL.
909  *
910  * Finishes an operation started with g_dbus_address_get_stream().
911  *
912  * A server is not required to set a GUID, so @out_guid may be set to %NULL
913  * even on success.
914  *
915  * Returns: (transfer full): A #GIOStream or %NULL if @error is set.
916  *
917  * Since: 2.26
918  */
919 GIOStream *
920 g_dbus_address_get_stream_finish (GAsyncResult        *res,
921                                   gchar              **out_guid,
922                                   GError             **error)
923 {
924   GTask *task;
925   GetStreamData *data;
926   GIOStream *ret;
927
928   g_return_val_if_fail (g_task_is_valid (res, NULL), NULL);
929   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
930
931   task = G_TASK (res);
932   ret = g_task_propagate_pointer (task, error);
933
934   if (ret != NULL && out_guid != NULL)
935     {
936       data = g_task_get_task_data (task);
937       *out_guid = data->guid;
938       data->guid = NULL;
939     }
940
941   return ret;
942 }
943
944 /**
945  * g_dbus_address_get_stream_sync:
946  * @address: A valid D-Bus address.
947  * @out_guid: (optional) (out) (nullable): %NULL or return location to store the GUID extracted from @address, if any.
948  * @cancellable: (nullable): A #GCancellable or %NULL.
949  * @error: Return location for error or %NULL.
950  *
951  * Synchronously connects to an endpoint specified by @address and
952  * sets up the connection so it is in a state to run the client-side
953  * of the D-Bus authentication conversation. @address must be in the
954  * [D-Bus address format](https://dbus.freedesktop.org/doc/dbus-specification.html#addresses).
955  *
956  * A server is not required to set a GUID, so @out_guid may be set to %NULL
957  * even on success.
958  *
959  * This is a synchronous failable function. See
960  * g_dbus_address_get_stream() for the asynchronous version.
961  *
962  * Returns: (transfer full): A #GIOStream or %NULL if @error is set.
963  *
964  * Since: 2.26
965  */
966 GIOStream *
967 g_dbus_address_get_stream_sync (const gchar   *address,
968                                 gchar        **out_guid,
969                                 GCancellable  *cancellable,
970                                 GError       **error)
971 {
972   GIOStream *ret;
973   gchar **addr_array;
974   guint n;
975   GError *last_error;
976
977   g_return_val_if_fail (address != NULL, NULL);
978   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
979
980   ret = NULL;
981   last_error = NULL;
982
983   addr_array = g_strsplit (address, ";", 0);
984   if (addr_array[0] == NULL)
985     {
986       last_error = g_error_new_literal (G_IO_ERROR,
987                                         G_IO_ERROR_INVALID_ARGUMENT,
988                                         _("The given address is empty"));
989       goto out;
990     }
991
992   for (n = 0; addr_array[n] != NULL; n++)
993     {
994       const gchar *addr = addr_array[n];
995       GError *this_error;
996
997       this_error = NULL;
998       ret = g_dbus_address_try_connect_one (addr,
999                                             out_guid,
1000                                             cancellable,
1001                                             &this_error);
1002       if (ret != NULL)
1003         {
1004           goto out;
1005         }
1006       else
1007         {
1008           g_assert (this_error != NULL);
1009           if (last_error != NULL)
1010             g_error_free (last_error);
1011           last_error = this_error;
1012         }
1013     }
1014
1015  out:
1016   if (ret != NULL)
1017     {
1018       if (last_error != NULL)
1019         g_error_free (last_error);
1020     }
1021   else
1022     {
1023       g_assert (last_error != NULL);
1024       g_propagate_error (error, last_error);
1025     }
1026
1027   g_strfreev (addr_array);
1028   return ret;
1029 }
1030
1031 /* ---------------------------------------------------------------------------------------------------- */
1032
1033 /*
1034  * Return the address of XDG_RUNTIME_DIR/bus if it exists, belongs to
1035  * us, and is a socket, and we are on Unix.
1036  */
1037 static gchar *
1038 get_session_address_xdg (void)
1039 {
1040 #ifdef G_OS_UNIX
1041   gchar *ret = NULL;
1042   gchar *bus;
1043   gchar *tmp;
1044   GStatBuf buf;
1045
1046   bus = g_build_filename (g_get_user_runtime_dir (), "bus", NULL);
1047
1048   /* if ENOENT, EPERM, etc., quietly don't use it */
1049   if (g_stat (bus, &buf) < 0)
1050     goto out;
1051
1052   /* if it isn't ours, we have incorrectly inherited someone else's
1053    * XDG_RUNTIME_DIR; silently don't use it
1054    */
1055   if (buf.st_uid != geteuid ())
1056     goto out;
1057
1058   /* if it isn't a socket, silently don't use it */
1059   if ((buf.st_mode & S_IFMT) != S_IFSOCK)
1060     goto out;
1061
1062   tmp = g_dbus_address_escape_value (bus);
1063   ret = g_strconcat ("unix:path=", tmp, NULL);
1064   g_free (tmp);
1065
1066 out:
1067   g_free (bus);
1068   return ret;
1069 #else
1070   return NULL;
1071 #endif
1072 }
1073
1074 /* ---------------------------------------------------------------------------------------------------- */
1075
1076 #ifdef G_OS_UNIX
1077 static gchar *
1078 get_session_address_dbus_launch (GError **error)
1079 {
1080   gchar *ret;
1081   gchar *machine_id;
1082   gchar *command_line;
1083   gchar *launch_stdout;
1084   gchar *launch_stderr;
1085   gint wait_status;
1086   gchar *old_dbus_verbose;
1087   gboolean restore_dbus_verbose;
1088
1089   ret = NULL;
1090   machine_id = NULL;
1091   command_line = NULL;
1092   launch_stdout = NULL;
1093   launch_stderr = NULL;
1094   restore_dbus_verbose = FALSE;
1095   old_dbus_verbose = NULL;
1096
1097   /* Don't run binaries as root if we're setuid. */
1098   if (GLIB_PRIVATE_CALL (g_check_setuid) ())
1099     {
1100       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1101                    _("Cannot spawn a message bus when AT_SECURE is set"));
1102       goto out;
1103     }
1104
1105   machine_id = _g_dbus_get_machine_id (error);
1106   if (machine_id == NULL)
1107     {
1108       g_prefix_error (error, _("Cannot spawn a message bus without a machine-id: "));
1109       goto out;
1110     }
1111
1112   if (g_getenv ("DISPLAY") == NULL)
1113     {
1114       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1115                    _("Cannot autolaunch D-Bus without X11 $DISPLAY"));
1116       goto out;
1117     }
1118
1119   /* We're using private libdbus facilities here. When everything
1120    * (X11, Mac OS X, Windows) is spec'ed out correctly (not even the
1121    * X11 property is correctly documented right now) we should
1122    * consider using the spec instead of dbus-launch.
1123    *
1124    *   --autolaunch=MACHINEID
1125    *          This option implies that dbus-launch should scan  for  a  previ‐
1126    *          ously-started  session  and  reuse the values found there. If no
1127    *          session is found, it will start a new session. The  --exit-with-
1128    *          session option is implied if --autolaunch is given.  This option
1129    *          is for the exclusive use of libdbus, you do not want to  use  it
1130    *          manually. It may change in the future.
1131    */
1132
1133   /* TODO: maybe provide a variable for where to look for the dbus-launch binary? */
1134   command_line = g_strdup_printf ("dbus-launch --autolaunch=%s --binary-syntax --close-stderr", machine_id);
1135
1136   if (G_UNLIKELY (_g_dbus_debug_address ()))
1137     {
1138       _g_dbus_debug_print_lock ();
1139       g_print ("GDBus-debug:Address: Running '%s' to get bus address (possibly autolaunching)\n", command_line);
1140       old_dbus_verbose = g_strdup (g_getenv ("DBUS_VERBOSE"));
1141       restore_dbus_verbose = TRUE;
1142       g_setenv ("DBUS_VERBOSE", "1", TRUE);
1143       _g_dbus_debug_print_unlock ();
1144     }
1145
1146   if (!g_spawn_command_line_sync (command_line,
1147                                   &launch_stdout,
1148                                   &launch_stderr,
1149                                   &wait_status,
1150                                   error))
1151     {
1152       goto out;
1153     }
1154
1155   if (!g_spawn_check_wait_status (wait_status, error))
1156     {
1157       g_prefix_error (error, _("Error spawning command line “%s”: "), command_line);
1158       goto out;
1159     }
1160
1161   /* From the dbus-launch(1) man page:
1162    *
1163    *   --binary-syntax Write to stdout a nul-terminated bus address,
1164    *   then the bus PID as a binary integer of size sizeof(pid_t),
1165    *   then the bus X window ID as a binary integer of size
1166    *   sizeof(long).  Integers are in the machine's byte order, not
1167    *   network byte order or any other canonical byte order.
1168    */
1169   ret = g_strdup (launch_stdout);
1170
1171  out:
1172   if (G_UNLIKELY (_g_dbus_debug_address ()))
1173     {
1174       gchar *s;
1175       _g_dbus_debug_print_lock ();
1176       g_print ("GDBus-debug:Address: dbus-launch output:");
1177       if (launch_stdout != NULL)
1178         {
1179           s = _g_dbus_hexdump (launch_stdout, strlen (launch_stdout) + 1 + sizeof (pid_t) + sizeof (long), 2);
1180           g_print ("\n%s", s);
1181           g_free (s);
1182         }
1183       else
1184         {
1185           g_print (" (none)\n");
1186         }
1187       g_print ("GDBus-debug:Address: dbus-launch stderr output:");
1188       if (launch_stderr != NULL)
1189         g_print ("\n%s", launch_stderr);
1190       else
1191         g_print (" (none)\n");
1192       _g_dbus_debug_print_unlock ();
1193     }
1194
1195   g_free (machine_id);
1196   g_free (command_line);
1197   g_free (launch_stdout);
1198   g_free (launch_stderr);
1199   if (G_UNLIKELY (restore_dbus_verbose))
1200     {
1201       if (old_dbus_verbose != NULL)
1202         g_setenv ("DBUS_VERBOSE", old_dbus_verbose, TRUE);
1203       else
1204         g_unsetenv ("DBUS_VERBOSE");
1205     }
1206   g_free (old_dbus_verbose);
1207   return ret;
1208 }
1209
1210 /* end of G_OS_UNIX case */
1211 #elif defined(G_OS_WIN32)
1212
1213 static gchar *
1214 get_session_address_dbus_launch (GError **error)
1215 {
1216   return _g_dbus_win32_get_session_address_dbus_launch (error);
1217 }
1218
1219 #else /* neither G_OS_UNIX nor G_OS_WIN32 */
1220 static gchar *
1221 get_session_address_dbus_launch (GError **error)
1222 {
1223   g_set_error (error,
1224                G_IO_ERROR,
1225                G_IO_ERROR_FAILED,
1226                _("Cannot determine session bus address (not implemented for this OS)"));
1227   return NULL;
1228 }
1229 #endif /* neither G_OS_UNIX nor G_OS_WIN32 */
1230
1231 /* ---------------------------------------------------------------------------------------------------- */
1232
1233 static gchar *
1234 get_session_address_platform_specific (GError **error)
1235 {
1236   gchar *ret;
1237
1238   /* Use XDG_RUNTIME_DIR/bus if it exists and is suitable. This is appropriate
1239    * for systems using the "a session is a user-session" model described in
1240    * <http://lists.freedesktop.org/archives/dbus/2015-January/016522.html>,
1241    * and implemented in dbus >= 1.9.14 and sd-bus.
1242    *
1243    * On systems following the more traditional "a session is a login-session"
1244    * model, this will fail and we'll fall through to X11 autolaunching
1245    * (dbus-launch) below.
1246    */
1247   ret = get_session_address_xdg ();
1248
1249   if (ret != NULL)
1250     return ret;
1251
1252   /* TODO (#694472): try launchd on OS X, like
1253    * _dbus_lookup_session_address_launchd() does, since
1254    * 'dbus-launch --autolaunch' probably won't work there
1255    */
1256
1257   /* As a last resort, try the "autolaunch:" transport. On Unix this means
1258    * X11 autolaunching; on Windows this means a different autolaunching
1259    * mechanism based on shared memory.
1260    */
1261   return get_session_address_dbus_launch (error);
1262 }
1263
1264 /* ---------------------------------------------------------------------------------------------------- */
1265
1266 /**
1267  * g_dbus_address_get_for_bus_sync:
1268  * @bus_type: a #GBusType
1269  * @cancellable: (nullable): a #GCancellable or %NULL
1270  * @error: return location for error or %NULL
1271  *
1272  * Synchronously looks up the D-Bus address for the well-known message
1273  * bus instance specified by @bus_type. This may involve using various
1274  * platform specific mechanisms.
1275  *
1276  * The returned address will be in the
1277  * [D-Bus address format](https://dbus.freedesktop.org/doc/dbus-specification.html#addresses).
1278  *
1279  * Returns: (transfer full): a valid D-Bus address string for @bus_type or
1280  *     %NULL if @error is set
1281  *
1282  * Since: 2.26
1283  */
1284 gchar *
1285 g_dbus_address_get_for_bus_sync (GBusType       bus_type,
1286                                  GCancellable  *cancellable,
1287                                  GError       **error)
1288 {
1289   gboolean has_elevated_privileges = GLIB_PRIVATE_CALL (g_check_setuid) ();
1290   gchar *ret, *s = NULL;
1291   const gchar *starter_bus;
1292   GError *local_error;
1293
1294   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1295
1296   ret = NULL;
1297   local_error = NULL;
1298
1299   if (G_UNLIKELY (_g_dbus_debug_address ()))
1300     {
1301       guint n;
1302       _g_dbus_debug_print_lock ();
1303       s = _g_dbus_enum_to_string (G_TYPE_BUS_TYPE, bus_type);
1304       g_print ("GDBus-debug:Address: In g_dbus_address_get_for_bus_sync() for bus type '%s'\n",
1305                s);
1306       g_free (s);
1307       for (n = 0; n < 3; n++)
1308         {
1309           const gchar *k;
1310           const gchar *v;
1311           switch (n)
1312             {
1313             case 0: k = "DBUS_SESSION_BUS_ADDRESS"; break;
1314             case 1: k = "DBUS_SYSTEM_BUS_ADDRESS"; break;
1315             case 2: k = "DBUS_STARTER_BUS_TYPE"; break;
1316             default: g_assert_not_reached ();
1317             }
1318           v = g_getenv (k);
1319           g_print ("GDBus-debug:Address: env var %s", k);
1320           if (v != NULL)
1321             g_print ("='%s'\n", v);
1322           else
1323             g_print (" is not set\n");
1324         }
1325       _g_dbus_debug_print_unlock ();
1326     }
1327
1328   /* Don’t load the addresses from the environment if running as setuid, as they
1329    * come from an unprivileged caller. */
1330   switch (bus_type)
1331     {
1332     case G_BUS_TYPE_SYSTEM:
1333       if (has_elevated_privileges)
1334         ret = NULL;
1335       else
1336         ret = g_strdup (g_getenv ("DBUS_SYSTEM_BUS_ADDRESS"));
1337
1338       if (ret == NULL)
1339         {
1340           /* While the D-Bus specification says this must be `/var/run/dbus/system_bus_socket`,
1341            * a footnote allows it to use localstatedir:
1342            * https://dbus.freedesktop.org/doc/dbus-specification.html#ftn.id-1.13.6.4.3.3
1343            * or, on systems where /run is the same as /var/run, runstatedir:
1344            * https://gitlab.freedesktop.org/dbus/dbus/-/merge_requests/209 */
1345           ret = g_strdup ("unix:path=" GLIB_RUNSTATEDIR "/dbus/system_bus_socket");
1346         }
1347       break;
1348
1349     case G_BUS_TYPE_SESSION:
1350       if (has_elevated_privileges)
1351         ret = NULL;
1352       else
1353         ret = g_strdup (g_getenv ("DBUS_SESSION_BUS_ADDRESS"));
1354
1355       if (ret == NULL)
1356         {
1357           ret = get_session_address_platform_specific (&local_error);
1358         }
1359       break;
1360
1361     case G_BUS_TYPE_STARTER:
1362       starter_bus = g_getenv ("DBUS_STARTER_BUS_TYPE");
1363       if (g_strcmp0 (starter_bus, "session") == 0)
1364         {
1365           ret = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SESSION, cancellable, &local_error);
1366           goto out;
1367         }
1368       else if (g_strcmp0 (starter_bus, "system") == 0)
1369         {
1370           ret = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SYSTEM, cancellable, &local_error);
1371           goto out;
1372         }
1373       else
1374         {
1375           if (starter_bus != NULL)
1376             {
1377               g_set_error (&local_error,
1378                            G_IO_ERROR,
1379                            G_IO_ERROR_FAILED,
1380                            _("Cannot determine bus address from DBUS_STARTER_BUS_TYPE environment variable"
1381                              " — unknown value “%s”"),
1382                            starter_bus);
1383             }
1384           else
1385             {
1386               g_set_error_literal (&local_error,
1387                                    G_IO_ERROR,
1388                                    G_IO_ERROR_FAILED,
1389                                    _("Cannot determine bus address because the DBUS_STARTER_BUS_TYPE environment "
1390                                      "variable is not set"));
1391             }
1392         }
1393       break;
1394
1395     default:
1396       g_set_error (&local_error,
1397                    G_IO_ERROR,
1398                    G_IO_ERROR_FAILED,
1399                    _("Unknown bus type %d"),
1400                    bus_type);
1401       break;
1402     }
1403
1404  out:
1405   if (G_UNLIKELY (_g_dbus_debug_address ()))
1406     {
1407       _g_dbus_debug_print_lock ();
1408       s = _g_dbus_enum_to_string (G_TYPE_BUS_TYPE, bus_type);
1409       if (ret != NULL)
1410         {
1411           g_print ("GDBus-debug:Address: Returning address '%s' for bus type '%s'\n",
1412                    ret, s);
1413         }
1414       else
1415         {
1416           g_print ("GDBus-debug:Address: Cannot look-up address bus type '%s': %s\n",
1417                    s, local_error ? local_error->message : "");
1418         }
1419       g_free (s);
1420       _g_dbus_debug_print_unlock ();
1421     }
1422
1423   if (local_error != NULL)
1424     g_propagate_error (error, local_error);
1425
1426   return ret;
1427 }
1428
1429 /**
1430  * g_dbus_address_escape_value:
1431  * @string: an unescaped string to be included in a D-Bus address
1432  *     as the value in a key-value pair
1433  *
1434  * Escape @string so it can appear in a D-Bus address as the value
1435  * part of a key-value pair.
1436  *
1437  * For instance, if @string is `/run/bus-for-:0`,
1438  * this function would return `/run/bus-for-%3A0`,
1439  * which could be used in a D-Bus address like
1440  * `unix:nonce-tcp:host=127.0.0.1,port=42,noncefile=/run/bus-for-%3A0`.
1441  *
1442  * Returns: (transfer full): a copy of @string with all
1443  *     non-optionally-escaped bytes escaped
1444  *
1445  * Since: 2.36
1446  */
1447 gchar *
1448 g_dbus_address_escape_value (const gchar *string)
1449 {
1450   GString *s;
1451   gsize i;
1452
1453   g_return_val_if_fail (string != NULL, NULL);
1454
1455   /* There will often not be anything needing escaping at all. */
1456   s = g_string_sized_new (strlen (string));
1457
1458   /* D-Bus address escaping is mostly the same as URI escaping... */
1459   g_string_append_uri_escaped (s, string, "\\/", FALSE);
1460
1461   /* ... but '~' is an unreserved character in URIs, but a
1462    * non-optionally-escaped character in D-Bus addresses. */
1463   for (i = 0; i < s->len; i++)
1464     {
1465       if (G_UNLIKELY (s->str[i] == '~'))
1466         {
1467           s->str[i] = '%';
1468           g_string_insert (s, i + 1, "7E");
1469           i += 2;
1470         }
1471     }
1472
1473   return g_string_free (s, FALSE);
1474 }