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