[kdbus] Integrate kdbus core with new API.
[platform/upstream/glib.git] / gio / gdbusaddress.c
index 2c1041e..0f59a85 100644 (file)
@@ -1,6 +1,6 @@
 /* GDBus - GLib D-Bus Library
  *
- * Copyright (C) 2008-2009 Red Hat, Inc.
+ * Copyright (C) 2008-2010 Red Hat, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -13,9 +13,7 @@
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General
- * Public License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
  *
  * Author: David Zeuthen <davidz@redhat.com>
  */
 #include "config.h"
 
 #include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
 
-#include <glib/gi18n.h>
-
+#include "gioerror.h"
 #include "gdbusutils.h"
 #include "gdbusaddress.h"
 #include "gdbuserror.h"
 #include "gioenumtypes.h"
+#include "gnetworkaddress.h"
+#include "gsocketclient.h"
+#include "giostream.h"
+#include "gasyncresult.h"
+#include "gsimpleasyncresult.h"
+#include "glib-private.h"
 #include "gdbusprivate.h"
+#include "giomodule-priv.h"
+#include "gdbusdaemon.h"
 
 #ifdef G_OS_UNIX
 #include <gio/gunixsocketaddress.h>
+#include <gio/gkdbusconnection.h>
+#endif
+
+#ifdef G_OS_WIN32
+#include <windows.h>
+#include <io.h>
+#include <conio.h>
 #endif
 
+#include "glibintl.h"
+
 /**
  * SECTION:gdbusaddress
  * @title: D-Bus Addresses
  * @short_description: D-Bus connection endpoints
  * @include: gio/gio.h
  *
- * Routines for working with D-Bus addresses.
+ * Routines for working with D-Bus addresses. A D-Bus address is a string
+ * like "unix:tmpdir=/tmp/my-app-name". The exact format of addresses
+ * is explained in detail in the [D-Bus specification](http://dbus.freedesktop.org/doc/dbus-specification.html\#addresses).
  */
 
+static gchar *get_session_address_platform_specific (GError **error);
+
 /* ---------------------------------------------------------------------------------------------------- */
 
 /**
@@ -73,6 +94,9 @@ g_dbus_is_address (const gchar *string)
   g_return_val_if_fail (string != NULL, FALSE);
 
   a = g_strsplit (string, ";", 0);
+  if (a[0] == NULL)
+    goto out;
+
   for (n = 0; a[n] != NULL; n++)
     {
       if (!_g_dbus_address_parse_entry (a[n],
@@ -122,7 +146,7 @@ is_valid_unix (const gchar  *address_entry,
           g_set_error (error,
                        G_IO_ERROR,
                        G_IO_ERROR_INVALID_ARGUMENT,
-                       _("Unsupported key `%s' in address entry `%s'"),
+                       _("Unsupported key '%s' in address entry '%s'"),
                        key,
                        address_entry);
           goto out;
@@ -133,26 +157,23 @@ is_valid_unix (const gchar  *address_entry,
     {
       if (tmpdir != NULL || abstract != NULL)
         goto meaningless;
-      /* TODO: validate path */
     }
   else if (tmpdir != NULL)
     {
       if (path != NULL || abstract != NULL)
         goto meaningless;
-      /* TODO: validate tmpdir */
     }
   else if (abstract != NULL)
     {
       if (path != NULL || tmpdir != NULL)
         goto meaningless;
-      /* TODO: validate abstract */
     }
   else
     {
       g_set_error (error,
                    G_IO_ERROR,
                    G_IO_ERROR_INVALID_ARGUMENT,
-                   _("Address `%s' is invalid (need exactly one of path, tmpdir or abstract keys"),
+                   _("Address '%s' is invalid (need exactly one of path, tmpdir or abstract keys)"),
                    address_entry);
       goto out;
     }
@@ -165,7 +186,7 @@ is_valid_unix (const gchar  *address_entry,
   g_set_error (error,
                G_IO_ERROR,
                G_IO_ERROR_INVALID_ARGUMENT,
-               _("Meaningless key/value pair combination in address entry `%s'"),
+               _("Meaningless key/value pair combination in address entry '%s'"),
                address_entry);
 
  out:
@@ -213,7 +234,7 @@ is_valid_nonce_tcp (const gchar  *address_entry,
           g_set_error (error,
                        G_IO_ERROR,
                        G_IO_ERROR_INVALID_ARGUMENT,
-                       _("Unsupported key `%s' in address entry `%s'"),
+                       _("Unsupported key '%s' in address entry '%s'"),
                        key,
                        address_entry);
           goto out;
@@ -228,7 +249,7 @@ is_valid_nonce_tcp (const gchar  *address_entry,
           g_set_error (error,
                        G_IO_ERROR,
                        G_IO_ERROR_INVALID_ARGUMENT,
-                       _("Error in address `%s' - the port attribute is malformed"),
+                       _("Error in address '%s' - the port attribute is malformed"),
                        address_entry);
           goto out;
         }
@@ -239,11 +260,18 @@ is_valid_nonce_tcp (const gchar  *address_entry,
       g_set_error (error,
                    G_IO_ERROR,
                    G_IO_ERROR_INVALID_ARGUMENT,
-                   _("Error in address `%s' - the family attribute is malformed"),
+                   _("Error in address '%s' - the family attribute is malformed"),
                    address_entry);
       goto out;
     }
 
+  if (host != NULL)
+    {
+      /* TODO: validate host */
+    }
+
+  nonce_file = nonce_file; /* To avoid -Wunused-but-set-variable */
+
   ret= TRUE;
 
  out:
@@ -287,7 +315,7 @@ is_valid_tcp (const gchar  *address_entry,
           g_set_error (error,
                        G_IO_ERROR,
                        G_IO_ERROR_INVALID_ARGUMENT,
-                       _("Unsupported key `%s' in address entry `%s'"),
+                       _("Unsupported key '%s' in address entry '%s'"),
                        key,
                        address_entry);
           goto out;
@@ -302,7 +330,7 @@ is_valid_tcp (const gchar  *address_entry,
           g_set_error (error,
                        G_IO_ERROR,
                        G_IO_ERROR_INVALID_ARGUMENT,
-                       _("Error in address `%s' - the port attribute is malformed"),
+                       _("Error in address '%s' - the port attribute is malformed"),
                        address_entry);
           goto out;
         }
@@ -313,11 +341,16 @@ is_valid_tcp (const gchar  *address_entry,
       g_set_error (error,
                    G_IO_ERROR,
                    G_IO_ERROR_INVALID_ARGUMENT,
-                   _("Error in address `%s' - the family attribute is malformed"),
+                   _("Error in address '%s' - the family attribute is malformed"),
                    address_entry);
       goto out;
     }
 
+  if (host != NULL)
+    {
+      /* TODO: validate host */
+    }
+
   ret= TRUE;
 
  out:
@@ -326,6 +359,16 @@ is_valid_tcp (const gchar  *address_entry,
   return ret;
 }
 
+static int
+g_dbus_is_supported_address_kdbus (const gchar  *transport_name)
+{
+  int supported = 0;
+
+  supported = g_strcmp0 (transport_name, "kernel") == 0;
+
+  return supported;
+}
+
 /**
  * g_dbus_is_supported_address:
  * @string: A string.
@@ -367,12 +410,15 @@ g_dbus_is_supported_address (const gchar  *string,
         goto out;
 
       supported = FALSE;
-      if (g_strcmp0 (transport_name, "unix") == 0)
+      if ((g_strcmp0 (transport_name, "unix") == 0)
+          || g_dbus_is_supported_address_kdbus (transport_name))
         supported = is_valid_unix (a[n], key_value_pairs, error);
       else if (g_strcmp0 (transport_name, "tcp") == 0)
         supported = is_valid_tcp (a[n], key_value_pairs, error);
       else if (g_strcmp0 (transport_name, "nonce-tcp") == 0)
         supported = is_valid_nonce_tcp (a[n], key_value_pairs, error);
+      else if (g_strcmp0 (a[n], "autolaunch:") == 0)
+        supported = TRUE;
 
       g_free (transport_name);
       g_hash_table_unref (key_value_pairs);
@@ -392,10 +438,10 @@ g_dbus_is_supported_address (const gchar  *string,
 }
 
 gboolean
-_g_dbus_address_parse_entry (const gchar   *address_entry,
-                             gchar        **out_transport_name,
-                             GHashTable   **out_key_value_pairs,
-                             GError       **error)
+_g_dbus_address_parse_entry (const gchar  *address_entry,
+                             gchar       **out_transport_name,
+                             GHashTable  **out_key_value_pairs,
+                             GError      **error)
 {
   gboolean ret;
   GHashTable *key_value_pairs;
@@ -415,7 +461,7 @@ _g_dbus_address_parse_entry (const gchar   *address_entry,
       g_set_error (error,
                    G_IO_ERROR,
                    G_IO_ERROR_INVALID_ARGUMENT,
-                   _("Address element `%s', does not contain a colon (:)"),
+                   _("Address element '%s' does not contain a colon (:)"),
                    address_entry);
       goto out;
     }
@@ -436,16 +482,28 @@ _g_dbus_address_parse_entry (const gchar   *address_entry,
           g_set_error (error,
                        G_IO_ERROR,
                        G_IO_ERROR_INVALID_ARGUMENT,
-                       _("Key/Value pair %d, `%s', in address element `%s', does not contain an equal sign"),
+                       _("Key/Value pair %d, '%s', in address element '%s' does not contain an equal sign"),
                        n,
                        kv_pair,
                        address_entry);
           goto out;
         }
 
-      /* TODO: actually validate that no illegal characters are present before and after then '=' sign */
       key = g_uri_unescape_segment (kv_pair, s, NULL);
       value = g_uri_unescape_segment (s + 1, kv_pair + strlen (kv_pair), NULL);
+      if (key == NULL || value == NULL)
+        {
+          g_set_error (error,
+                       G_IO_ERROR,
+                       G_IO_ERROR_INVALID_ARGUMENT,
+                       _("Error unescaping key or value in Key/Value pair %d, '%s', in address element '%s'"),
+                       n,
+                       kv_pair,
+                       address_entry);
+          g_free (key);
+          g_free (value);
+          goto out;
+        }
       g_hash_table_insert (key_value_pairs, key, value);
     }
 
@@ -475,17 +533,23 @@ out:
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+static GIOStream *
+g_dbus_address_try_connect_one (const gchar   *address_entry,
+                                gchar        **out_guid,
+                                GCancellable  *cancellable,
+                                GError       **error);
+
 /* TODO: Declare an extension point called GDBusTransport (or similar)
  * and move code below to extensions implementing said extension
  * point. That way we can implement a D-Bus transport over X11 without
  * making libgio link to libX11...
  */
 static GIOStream *
-g_dbus_address_connect (const gchar    *address_entry,
-                        const gchar    *transport_name,
-                        GHashTable     *key_value_pairs,
-                        GCancellable   *cancellable,
-                        GError        **error)
+g_dbus_address_connect (const gchar   *address_entry,
+                        const gchar   *transport_name,
+                        GHashTable    *key_value_pairs,
+                        GCancellable  *cancellable,
+                        GError       **error)
 {
   GIOStream *ret;
   GSocketConnectable *connectable;
@@ -499,7 +563,8 @@ g_dbus_address_connect (const gchar    *address_entry,
     {
     }
 #ifdef G_OS_UNIX
-  else if (g_strcmp0 (transport_name, "unix") == 0)
+  if ((g_strcmp0 (transport_name, "unix") == 0)
+      || g_dbus_is_supported_address_kdbus (transport_name))
     {
       const gchar *path;
       const gchar *abstract;
@@ -510,8 +575,8 @@ g_dbus_address_connect (const gchar    *address_entry,
           g_set_error (error,
                        G_IO_ERROR,
                        G_IO_ERROR_INVALID_ARGUMENT,
-                       _("Error in address `%s' - the unix transport requires exactly one of the "
-                         "keys `path' or `abstract' to be set"),
+                       _("Error in address '%s' - the unix transport requires exactly one of the "
+                         "keys 'path' or 'abstract' to be set"),
                        address_entry);
         }
       else if (path != NULL)
@@ -534,7 +599,7 @@ g_dbus_address_connect (const gchar    *address_entry,
     {
       const gchar *s;
       const gchar *host;
-      guint port;
+      glong port;
       gchar *endp;
       gboolean is_nonce;
 
@@ -546,7 +611,7 @@ g_dbus_address_connect (const gchar    *address_entry,
           g_set_error (error,
                        G_IO_ERROR,
                        G_IO_ERROR_INVALID_ARGUMENT,
-                       _("Error in address `%s' - the host attribute is missing or malformed"),
+                       _("Error in address '%s' - the host attribute is missing or malformed"),
                        address_entry);
           goto out;
         }
@@ -560,7 +625,7 @@ g_dbus_address_connect (const gchar    *address_entry,
           g_set_error (error,
                        G_IO_ERROR,
                        G_IO_ERROR_INVALID_ARGUMENT,
-                       _("Error in address `%s' - the port attribute is missing or malformed"),
+                       _("Error in address '%s' - the port attribute is missing or malformed"),
                        address_entry);
           goto out;
         }
@@ -574,88 +639,146 @@ g_dbus_address_connect (const gchar    *address_entry,
               g_set_error (error,
                            G_IO_ERROR,
                            G_IO_ERROR_INVALID_ARGUMENT,
-                           _("Error in address `%s' - the noncefile attribute is missing or malformed"),
+                           _("Error in address '%s' - the noncefile attribute is missing or malformed"),
                            address_entry);
               goto out;
             }
         }
 
-      /* TODO: deal with family */
+      /* TODO: deal with family key/value-pair */
       connectable = g_network_address_new (host, port);
     }
+  else if (g_strcmp0 (address_entry, "autolaunch:") == 0)
+    {
+      gchar *autolaunch_address;
+      autolaunch_address = get_session_address_platform_specific (error);
+      if (autolaunch_address != NULL)
+        {
+          ret = g_dbus_address_try_connect_one (autolaunch_address, NULL, cancellable, error);
+          g_free (autolaunch_address);
+          goto out;
+        }
+      else
+        {
+          g_prefix_error (error, _("Error auto-launching: "));
+        }
+    }
   else
     {
       g_set_error (error,
                    G_IO_ERROR,
                    G_IO_ERROR_INVALID_ARGUMENT,
-                   _("Unknown or unsupported transport `%s' for address `%s'"),
+                   _("Unknown or unsupported transport '%s' for address '%s'"),
                    transport_name,
                    address_entry);
     }
 
   if (connectable != NULL)
     {
-      GSocketClient *client;
-      GSocketConnection *connection;
 
-      g_assert (ret == NULL);
-      client = g_socket_client_new ();
-      connection = g_socket_client_connect (client,
-                                            connectable,
-                                            cancellable,
-                                            error);
-      g_object_unref (connectable);
-      g_object_unref (client);
-      if (connection == NULL)
-        goto out;
+      if (g_dbus_is_supported_address_kdbus (transport_name))
+        {
+          GKdbusConnection *connection;
+          gboolean status;
+
+          const gchar *path;
+          path = g_hash_table_lookup (key_value_pairs, "path");
+
+          g_assert (ret == NULL);
+          connection = _g_kdbus_connection_new ();
+          status = _g_kdbus_connection_connect (connection,
+                                                path,
+                                                cancellable,
+                                                error);
+          g_object_unref (connectable);
 
-      ret = G_IO_STREAM (connection);
+          if (!status)
+            goto out;
+
+          ret = G_IO_STREAM (connection);
+        }
+      else
+        {
+          GSocketClient *client;
+          GSocketConnection *connection;
+
+          g_assert (ret == NULL);
+          client = g_socket_client_new ();
+          connection = g_socket_client_connect (client,
+                                                connectable,
+                                                cancellable,
+                                                error);
+          g_object_unref (connectable);
+          g_object_unref (client);
+          if (connection == NULL)
+            goto out;
+
+          ret = G_IO_STREAM (connection);
+        }
 
       if (nonce_file != NULL)
         {
-          gchar *nonce_contents;
-          gsize nonce_length;
+          gchar nonce_contents[16 + 1];
+          size_t num_bytes_read;
+          FILE *f;
 
-          /* TODO: too dangerous to read the entire file? (think denial-of-service etc.) */
-          if (!g_file_get_contents (nonce_file,
-                                    &nonce_contents,
-                                    &nonce_length,
-                                    error))
+          /* be careful to read only 16 bytes - we also check that the file is only 16 bytes long */
+          f = fopen (nonce_file, "rb");
+          if (f == NULL)
             {
-              g_prefix_error (error, _("Error reading nonce file `%s':"), nonce_file);
+              g_set_error (error,
+                           G_IO_ERROR,
+                           G_IO_ERROR_INVALID_ARGUMENT,
+                           _("Error opening nonce file '%s': %s"),
+                           nonce_file,
+                           g_strerror (errno));
               g_object_unref (ret);
               ret = NULL;
               goto out;
             }
-
-          if (nonce_length != 16)
+          num_bytes_read = fread (nonce_contents,
+                                  sizeof (gchar),
+                                  16 + 1,
+                                  f);
+          if (num_bytes_read != 16)
             {
-              g_set_error (error,
-                           G_IO_ERROR,
-                           G_IO_ERROR_INVALID_ARGUMENT,
-                           _("The nonce-file `%s' was %" G_GSIZE_FORMAT " bytes. Expected 16 bytes."),
-                           nonce_file,
-                           nonce_length);
-              g_free (nonce_contents);
+              if (num_bytes_read == 0)
+                {
+                  g_set_error (error,
+                               G_IO_ERROR,
+                               G_IO_ERROR_INVALID_ARGUMENT,
+                               _("Error reading from nonce file '%s': %s"),
+                               nonce_file,
+                               g_strerror (errno));
+                }
+              else
+                {
+                  g_set_error (error,
+                               G_IO_ERROR,
+                               G_IO_ERROR_INVALID_ARGUMENT,
+                               _("Error reading from nonce file '%s', expected 16 bytes, got %d"),
+                               nonce_file,
+                               (gint) num_bytes_read);
+                }
               g_object_unref (ret);
               ret = NULL;
+              fclose (f);
               goto out;
             }
+          fclose (f);
 
           if (!g_output_stream_write_all (g_io_stream_get_output_stream (ret),
                                           nonce_contents,
-                                          nonce_length,
+                                          16,
                                           NULL,
                                           cancellable,
                                           error))
             {
-              g_prefix_error (error, _("Error write contents of nonce file `%s' to stream:"), nonce_file);
+              g_prefix_error (error, _("Error writing contents of nonce file '%s' to stream:"), nonce_file);
               g_object_unref (ret);
               ret = NULL;
-              g_free (nonce_contents);
               goto out;
             }
-          g_free (nonce_contents);
         }
     }
 
@@ -665,10 +788,10 @@ g_dbus_address_connect (const gchar    *address_entry,
 }
 
 static GIOStream *
-g_dbus_address_try_connect_one (const gchar         *address_entry,
-                                gchar              **out_guid,
-                                GCancellable        *cancellable,
-                                GError             **error)
+g_dbus_address_try_connect_one (const gchar   *address_entry,
+                                gchar        **out_guid,
+                                GCancellable  *cancellable,
+                                GError       **error)
 {
   GIOStream *ret;
   GHashTable *key_value_pairs;
@@ -693,7 +816,6 @@ g_dbus_address_try_connect_one (const gchar         *address_entry,
   if (ret == NULL)
     goto out;
 
-  /* TODO: validate that guid is of correct format */
   guid = g_hash_table_lookup (key_value_pairs, "guid");
   if (guid != NULL && out_guid != NULL)
     *out_guid = g_strdup (guid);
@@ -740,16 +862,13 @@ get_stream_thread_func (GSimpleAsyncResult *res,
                                                  cancellable,
                                                  &error);
   if (data->stream == NULL)
-    {
-      g_simple_async_result_set_from_error (res, error);
-      g_error_free (error);
-    }
+    g_simple_async_result_take_error (res, error);
 }
 
 /**
  * g_dbus_address_get_stream:
  * @address: A valid D-Bus address.
- * @cancellable: A #GCancellable or %NULL.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
  * @callback: A #GAsyncReadyCallback to call when the request is satisfied.
  * @user_data: Data to pass to @callback.
  *
@@ -781,6 +900,7 @@ g_dbus_address_get_stream (const gchar         *address,
                                    callback,
                                    user_data,
                                    g_dbus_address_get_stream);
+  g_simple_async_result_set_check_cancellable (res, cancellable);
   data = g_new0 (GetStreamData, 1);
   data->address = g_strdup (address);
   g_simple_async_result_set_op_res_gpointer (res,
@@ -801,7 +921,7 @@ g_dbus_address_get_stream (const gchar         *address,
  *
  * Finishes an operation started with g_dbus_address_get_stream().
  *
- * Returns: A #GIOStream or %NULL if @error is set.
+ * Returns: (transfer full): A #GIOStream or %NULL if @error is set.
  *
  * Since: 2.26
  */
@@ -837,7 +957,7 @@ g_dbus_address_get_stream_finish (GAsyncResult        *res,
  * g_dbus_address_get_stream_sync:
  * @address: A valid D-Bus address.
  * @out_guid: %NULL or return location to store the GUID extracted from @address, if any.
- * @cancellable: A #GCancellable or %NULL.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
  * @error: Return location for error or %NULL.
  *
  * Synchronously connects to an endpoint specified by @address and
@@ -847,15 +967,15 @@ g_dbus_address_get_stream_finish (GAsyncResult        *res,
  * This is a synchronous failable function. See
  * g_dbus_address_get_stream() for the asynchronous version.
  *
- * Returns: A #GIOStream or %NULL if @error is set.
+ * Returns: (transfer full): A #GIOStream or %NULL if @error is set.
  *
  * Since: 2.26
  */
 GIOStream *
-g_dbus_address_get_stream_sync (const gchar         *address,
-                                gchar              **out_guid,
-                                GCancellable        *cancellable,
-                                GError             **error)
+g_dbus_address_get_stream_sync (const gchar   *address,
+                                gchar        **out_guid,
+                                GCancellable  *cancellable,
+                                GError       **error)
 {
   GIOStream *ret;
   gchar **addr_array;
@@ -869,11 +989,19 @@ g_dbus_address_get_stream_sync (const gchar         *address,
   last_error = NULL;
 
   addr_array = g_strsplit (address, ";", 0);
-  last_error = NULL;
+  if (addr_array != NULL && addr_array[0] == NULL)
+    {
+      last_error = g_error_new_literal (G_IO_ERROR,
+                                        G_IO_ERROR_INVALID_ARGUMENT,
+                                        _("The given address is empty"));
+      goto out;
+    }
+
   for (n = 0; addr_array != NULL && addr_array[n] != NULL; n++)
     {
       const gchar *addr = addr_array[n];
       GError *this_error;
+
       this_error = NULL;
       ret = g_dbus_address_try_connect_one (addr,
                                             out_guid,
@@ -903,31 +1031,475 @@ g_dbus_address_get_stream_sync (const gchar         *address,
       g_assert (last_error != NULL);
       g_propagate_error (error, last_error);
     }
+
+  g_strfreev (addr_array);
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+#ifdef G_OS_UNIX
+static gchar *
+get_session_address_dbus_launch (GError **error)
+{
+  gchar *ret;
+  gchar *machine_id;
+  gchar *command_line;
+  gchar *launch_stdout;
+  gchar *launch_stderr;
+  gint exit_status;
+  gchar *old_dbus_verbose;
+  gboolean restore_dbus_verbose;
+
+  ret = NULL;
+  machine_id = NULL;
+  command_line = NULL;
+  launch_stdout = NULL;
+  launch_stderr = NULL;
+  restore_dbus_verbose = FALSE;
+  old_dbus_verbose = NULL;
+
+  /* Don't run binaries as root if we're setuid. */
+  if (GLIB_PRIVATE_CALL (g_check_setuid) ())
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                  _("Cannot spawn a message bus when setuid"));
+      goto out;
+    }
+
+  machine_id = _g_dbus_get_machine_id (error);
+  if (machine_id == NULL)
+    {
+      g_prefix_error (error, _("Cannot spawn a message bus without a machine-id: "));
+      goto out;
+    }
+
+  /* We're using private libdbus facilities here. When everything
+   * (X11, Mac OS X, Windows) is spec'ed out correctly (not even the
+   * X11 property is correctly documented right now) we should
+   * consider using the spec instead of dbus-launch.
+   *
+   *   --autolaunch=MACHINEID
+   *          This option implies that dbus-launch should scan  for  a  previā€
+   *          ously-started  session  and  reuse the values found there. If no
+   *          session is found, it will start a new session. The  --exit-with-
+   *          session option is implied if --autolaunch is given.  This option
+   *          is for the exclusive use of libdbus, you do not want to  use  it
+   *          manually. It may change in the future.
+   */
+
+  /* TODO: maybe provide a variable for where to look for the dbus-launch binary? */
+  command_line = g_strdup_printf ("dbus-launch --autolaunch=%s --binary-syntax --close-stderr", machine_id);
+
+  if (G_UNLIKELY (_g_dbus_debug_address ()))
+    {
+      _g_dbus_debug_print_lock ();
+      g_print ("GDBus-debug:Address: Running '%s' to get bus address (possibly autolaunching)\n", command_line);
+      old_dbus_verbose = g_strdup (g_getenv ("DBUS_VERBOSE"));
+      restore_dbus_verbose = TRUE;
+      g_setenv ("DBUS_VERBOSE", "1", TRUE);
+      _g_dbus_debug_print_unlock ();
+    }
+
+  if (!g_spawn_command_line_sync (command_line,
+                                  &launch_stdout,
+                                  &launch_stderr,
+                                  &exit_status,
+                                  error))
+    {
+      goto out;
+    }
+
+  if (!g_spawn_check_exit_status (exit_status, error))
+    {
+      g_prefix_error (error, _("Error spawning command line '%s': "), command_line);
+      goto out;
+    }
+
+  /* From the dbus-launch(1) man page:
+   *
+   *   --binary-syntax Write to stdout a nul-terminated bus address,
+   *   then the bus PID as a binary integer of size sizeof(pid_t),
+   *   then the bus X window ID as a binary integer of size
+   *   sizeof(long).  Integers are in the machine's byte order, not
+   *   network byte order or any other canonical byte order.
+   */
+  ret = g_strdup (launch_stdout);
+
+ out:
+  if (G_UNLIKELY (_g_dbus_debug_address ()))
+    {
+      gchar *s;
+      _g_dbus_debug_print_lock ();
+      g_print ("GDBus-debug:Address: dbus-launch output:");
+      if (launch_stdout != NULL)
+        {
+          s = _g_dbus_hexdump (launch_stdout, strlen (launch_stdout) + 1 + sizeof (pid_t) + sizeof (long), 2);
+          g_print ("\n%s", s);
+          g_free (s);
+        }
+      else
+        {
+          g_print (" (none)\n");
+        }
+      g_print ("GDBus-debug:Address: dbus-launch stderr output:");
+      if (launch_stderr != NULL)
+        g_print ("\n%s", launch_stderr);
+      else
+        g_print (" (none)\n");
+      _g_dbus_debug_print_unlock ();
+    }
+
+  g_free (machine_id);
+  g_free (command_line);
+  g_free (launch_stdout);
+  g_free (launch_stderr);
+  if (G_UNLIKELY (restore_dbus_verbose))
+    {
+      if (old_dbus_verbose != NULL)
+        g_setenv ("DBUS_VERBOSE", old_dbus_verbose, TRUE);
+      else
+        g_unsetenv ("DBUS_VERBOSE");
+    }
+  g_free (old_dbus_verbose);
   return ret;
 }
+#endif
+
+#ifdef G_OS_WIN32
+
+#define DBUS_DAEMON_ADDRESS_INFO "DBusDaemonAddressInfo"
+#define DBUS_DAEMON_MUTEX "DBusDaemonMutex"
+#define UNIQUE_DBUS_INIT_MUTEX "UniqueDBusInitMutex"
+#define DBUS_AUTOLAUNCH_MUTEX "DBusAutolaunchMutex"
+
+static void
+release_mutex (HANDLE mutex)
+{
+  ReleaseMutex (mutex);
+  CloseHandle (mutex);
+}
+
+static HANDLE
+acquire_mutex (const char *mutexname)
+{
+  HANDLE mutex;
+  DWORD res;
+
+  mutex = CreateMutexA (NULL, FALSE, mutexname);
+  if (!mutex)
+    return 0;
+
+  res = WaitForSingleObject (mutex, INFINITE);
+  switch (res)
+    {
+    case WAIT_ABANDONED:
+      release_mutex (mutex);
+      return 0;
+    case WAIT_FAILED:
+    case WAIT_TIMEOUT:
+      return 0;
+    }
+
+  return mutex;
+}
+
+static gboolean
+is_mutex_owned (const char *mutexname)
+{
+  HANDLE mutex;
+  gboolean res = FALSE;
+
+  mutex = CreateMutexA (NULL, FALSE, mutexname);
+  if (WaitForSingleObject (mutex, 10) == WAIT_TIMEOUT)
+    res = TRUE;
+  else
+    ReleaseMutex (mutex);
+  CloseHandle (mutex);
+
+  return res;
+}
+
+static char *
+read_shm (const char *shm_name)
+{
+  HANDLE shared_mem;
+  char *shared_data;
+  char *res;
+  int i;
+
+  res = NULL;
+
+  for (i = 0; i < 20; i++)
+    {
+      shared_mem = OpenFileMappingA (FILE_MAP_READ, FALSE, shm_name);
+      if (shared_mem != 0)
+       break;
+      Sleep (100);
+    }
+
+  if (shared_mem != 0)
+    {
+      shared_data = MapViewOfFile (shared_mem, FILE_MAP_READ, 0, 0, 0);
+      if (shared_data != NULL)
+       {
+         res = g_strdup (shared_data);
+         UnmapViewOfFile (shared_data);
+       }
+      CloseHandle (shared_mem);
+    }
+
+  return res;
+}
+
+static HANDLE
+set_shm (const char *shm_name, const char *value)
+{
+  HANDLE shared_mem;
+  char *shared_data;
+
+  shared_mem = CreateFileMappingA (INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
+                                  0, strlen (value) + 1, shm_name);
+  if (shared_mem == 0)
+    return 0;
+
+  shared_data = MapViewOfFile (shared_mem, FILE_MAP_WRITE, 0, 0, 0 );
+  if (shared_data == NULL)
+    return 0;
+
+  strcpy (shared_data, value);
+
+  UnmapViewOfFile (shared_data);
+
+  return shared_mem;
+}
+
+/* These keep state between publish_session_bus and unpublish_session_bus */
+static HANDLE published_daemon_mutex;
+static HANDLE published_shared_mem;
+
+static gboolean
+publish_session_bus (const char *address)
+{
+  HANDLE init_mutex;
+
+  init_mutex = acquire_mutex (UNIQUE_DBUS_INIT_MUTEX);
+
+  published_daemon_mutex = CreateMutexA (NULL, FALSE, DBUS_DAEMON_MUTEX);
+  if (WaitForSingleObject (published_daemon_mutex, 10 ) != WAIT_OBJECT_0)
+    {
+      release_mutex (init_mutex);
+      CloseHandle (published_daemon_mutex);
+      published_daemon_mutex = NULL;
+      return FALSE;
+    }
+
+  published_shared_mem = set_shm (DBUS_DAEMON_ADDRESS_INFO, address);
+  if (!published_shared_mem)
+    {
+      release_mutex (init_mutex);
+      CloseHandle (published_daemon_mutex);
+      published_daemon_mutex = NULL;
+      return FALSE;
+    }
+
+  release_mutex (init_mutex);
+  return TRUE;
+}
+
+static void
+unpublish_session_bus (void)
+{
+  HANDLE init_mutex;
+
+  init_mutex = acquire_mutex (UNIQUE_DBUS_INIT_MUTEX);
+
+  CloseHandle (published_shared_mem);
+  published_shared_mem = NULL;
+
+  release_mutex (published_daemon_mutex);
+  published_daemon_mutex = NULL;
+
+  release_mutex (init_mutex);
+}
+
+static void
+wait_console_window (void)
+{
+  FILE *console = fopen ("CONOUT$", "w");
+
+  SetConsoleTitleW (L"gdbus-daemon output. Type any character to close this window.");
+  fprintf (console, _("(Type any character to close this window)\n"));
+  fflush (console);
+  _getch ();
+}
+
+static void
+open_console_window (void)
+{
+  if (((HANDLE) _get_osfhandle (fileno (stdout)) == INVALID_HANDLE_VALUE ||
+       (HANDLE) _get_osfhandle (fileno (stderr)) == INVALID_HANDLE_VALUE) && AllocConsole ())
+    {
+      if ((HANDLE) _get_osfhandle (fileno (stdout)) == INVALID_HANDLE_VALUE)
+        freopen ("CONOUT$", "w", stdout);
+
+      if ((HANDLE) _get_osfhandle (fileno (stderr)) == INVALID_HANDLE_VALUE)
+        freopen ("CONOUT$", "w", stderr);
+
+      SetConsoleTitleW (L"gdbus-daemon debug output.");
+
+      atexit (wait_console_window);
+    }
+}
+static void
+idle_timeout_cb (GDBusDaemon *daemon, gpointer user_data)
+{
+  GMainLoop *loop = user_data;
+  g_main_loop_quit (loop);
+}
+
+__declspec(dllexport) void CALLBACK g_win32_run_session_bus (HWND hwnd, HINSTANCE hinst, char *cmdline, int nCmdShow);
+
+__declspec(dllexport) void CALLBACK
+g_win32_run_session_bus (HWND hwnd, HINSTANCE hinst, char *cmdline, int nCmdShow)
+{
+  GDBusDaemon *daemon;
+  GMainLoop *loop;
+  const char *address;
+  GError *error = NULL;
+
+  if (g_getenv ("GDBUS_DAEMON_DEBUG") != NULL)
+    open_console_window ();
+
+  loop = g_main_loop_new (NULL, FALSE);
+
+  address = "nonce-tcp:";
+  daemon = _g_dbus_daemon_new (address, NULL, &error);
+  if (daemon == NULL)
+    {
+      g_printerr ("Can't init bus: %s\n", error->message);
+      return;
+    }
+
+  g_signal_connect (daemon, "idle-timeout", G_CALLBACK (idle_timeout_cb), loop);
+
+  if ( publish_session_bus (_g_dbus_daemon_get_address (daemon)))
+    {
+      g_main_loop_run (loop);
+
+      unpublish_session_bus ();
+    }
+
+  g_main_loop_unref (loop);
+  g_object_unref (daemon);
+}
+
+static gchar *
+get_session_address_dbus_launch (GError **error)
+{
+  HANDLE autolaunch_mutex, init_mutex;
+  char *address = NULL;
+  wchar_t gio_path[MAX_PATH+1+200];
+
+  autolaunch_mutex = acquire_mutex (DBUS_AUTOLAUNCH_MUTEX);
+
+  init_mutex = acquire_mutex (UNIQUE_DBUS_INIT_MUTEX);
+
+  if (is_mutex_owned (DBUS_DAEMON_MUTEX))
+    address = read_shm (DBUS_DAEMON_ADDRESS_INFO);
+
+  release_mutex (init_mutex);
+
+  if (address == NULL)
+    {
+      gio_path[MAX_PATH] = 0;
+      if (GetModuleFileNameW (_g_io_win32_get_module (), gio_path, MAX_PATH))
+       {
+         PROCESS_INFORMATION pi = { 0 };
+         STARTUPINFOW si = { 0 };
+         BOOL res;
+         wchar_t gio_path_short[MAX_PATH];
+         wchar_t rundll_path[MAX_PATH*2];
+         wchar_t args[MAX_PATH*4];
+
+         GetShortPathNameW (gio_path, gio_path_short, MAX_PATH);
+
+         GetWindowsDirectoryW (rundll_path, MAX_PATH);
+         wcscat (rundll_path, L"\\rundll32.exe");
+         if (GetFileAttributesW (rundll_path) == INVALID_FILE_ATTRIBUTES)
+           {
+             GetSystemDirectoryW (rundll_path, MAX_PATH);
+             wcscat (rundll_path, L"\\rundll32.exe");
+           }
+
+         wcscpy (args, L"\"");
+         wcscat (args, rundll_path);
+         wcscat (args, L"\" ");
+         wcscat (args, gio_path_short);
+#if defined(_WIN64) || defined(_M_X64) || defined(_M_AMD64)
+         wcscat (args, L",g_win32_run_session_bus");
+#elif defined (_MSC_VER)
+         wcscat (args, L",_g_win32_run_session_bus@16");
+#else
+         wcscat (args, L",g_win32_run_session_bus@16");
+#endif
+
+         res = CreateProcessW (rundll_path, args,
+                               0, 0, FALSE,
+                               NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW | DETACHED_PROCESS,
+                               0, NULL /* TODO: Should be root */,
+                               &si, &pi);
+         if (res)
+           address = read_shm (DBUS_DAEMON_ADDRESS_INFO);
+       }
+    }
+
+  release_mutex (autolaunch_mutex);
+
+  if (address == NULL)
+    g_set_error (error,
+                G_IO_ERROR,
+                G_IO_ERROR_FAILED,
+                _("Session dbus not running, and autolaunch failed"));
+
+  return address;
+}
+#endif
 
 /* ---------------------------------------------------------------------------------------------------- */
 
-/* TODO: implement for UNIX, Win32 and OS X */
 static gchar *
-get_session_address_platform_specific (void)
+get_session_address_platform_specific (GError **error)
 {
-  return NULL;
+  gchar *ret;
+#if defined (G_OS_UNIX) || defined(G_OS_WIN32)
+  /* need to handle OS X in a different way since 'dbus-launch --autolaunch' probably won't work there */
+  ret = get_session_address_dbus_launch (error);
+#else
+  /* TODO: implement for OS X */
+  ret = NULL;
+  g_set_error (error,
+               G_IO_ERROR,
+               G_IO_ERROR_FAILED,
+               _("Cannot determine session bus address (not implemented for this OS)"));
+#endif
+  return ret;
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
 
 /**
  * g_dbus_address_get_for_bus_sync:
- * @bus_type: A #GBusType.
- * @cancellable: A #GCancellable or %NULL.
- * @error: Return location for error or %NULL.
+ * @bus_type: a #GBusType
+ * @cancellable: (allow-none): a #GCancellable or %NULL
+ * @error: return location for error or %NULL
  *
  * Synchronously looks up the D-Bus address for the well-known message
  * bus instance specified by @bus_type. This may involve using various
  * platform specific mechanisms.
  *
- * Returns: A valid D-Bus address string for @bus_type or %NULL if @error is set.
+ * Returns: a valid D-Bus address string for @bus_type or %NULL if
+ *     @error is set
  *
  * Since: 2.26
  */
@@ -937,11 +1509,42 @@ g_dbus_address_get_for_bus_sync (GBusType       bus_type,
                                  GError       **error)
 {
   gchar *ret;
+  const gchar *system_bus;
+  const gchar *session_bus;
   const gchar *starter_bus;
+  GError *local_error;
 
   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
   ret = NULL;
+  local_error = NULL;
+
+  if (G_UNLIKELY (_g_dbus_debug_address ()))
+    {
+      guint n;
+      _g_dbus_debug_print_lock ();
+      g_print ("GDBus-debug:Address: In g_dbus_address_get_for_bus_sync() for bus type '%s'\n",
+               _g_dbus_enum_to_string (G_TYPE_BUS_TYPE, bus_type));
+      for (n = 0; n < 3; n++)
+        {
+          const gchar *k;
+          const gchar *v;
+          switch (n)
+            {
+            case 0: k = "DBUS_SESSION_BUS_ADDRESS"; break;
+            case 1: k = "DBUS_SYSTEM_BUS_ADDRESS"; break;
+            case 2: k = "DBUS_STARTER_BUS_TYPE"; break;
+            default: g_assert_not_reached ();
+            }
+          v = g_getenv (k);
+          g_print ("GDBus-debug:Address: env var %s", k);
+          if (v != NULL)
+            g_print ("='%s'\n", v);
+          else
+            g_print (" is not set\n");
+        }
+      _g_dbus_debug_print_unlock ();
+    }
 
   switch (bus_type)
     {
@@ -957,43 +1560,53 @@ g_dbus_address_get_for_bus_sync (GBusType       bus_type,
       ret = g_strdup (g_getenv ("DBUS_SESSION_BUS_ADDRESS"));
       if (ret == NULL)
         {
-          ret = get_session_address_platform_specific ();
-          if (ret == NULL)
-            {
-              g_set_error (error,
-                           G_IO_ERROR,
-                           G_IO_ERROR_FAILED,
-                           _("Cannot determine session bus address (TODO: run dbus-launch to find out)"));
-            }
+          ret = get_session_address_platform_specific (&local_error);
         }
       break;
 
+    case G_BUS_TYPE_MACHINE:
+      system_bus = g_getenv ("DBUS_SYSTEM_BUS_ADDRESS");
+      if (system_bus == NULL)
+        ret = g_strdup ("kernel:path=/dev/kdbus/0-system/bus;unix:path=/var/run/dbus/system_bus_socket");
+      else
+        ret = g_strdup_printf ("kernel:path=/dev/kdbus/0-system/bus;%s", system_bus);
+      break;
+
+    case G_BUS_TYPE_USER:
+      session_bus = g_getenv ("DBUS_SESSION_BUS_ADDRESS");
+      if (session_bus == NULL)
+        ret = g_strdup_printf ("kernel:path=/dev/kdbus/%d-user/bus;%s", getuid(),
+                                   get_session_address_platform_specific (&local_error));
+      else
+        ret = g_strdup_printf ("kernel:path=/dev/kdbus/%d-user/bus;%s", getuid(), session_bus);
+      break;
+
     case G_BUS_TYPE_STARTER:
       starter_bus = g_getenv ("DBUS_STARTER_BUS_TYPE");
       if (g_strcmp0 (starter_bus, "session") == 0)
         {
-          ret = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SESSION, cancellable, error);
+          ret = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SESSION, cancellable, &local_error);
           goto out;
         }
       else if (g_strcmp0 (starter_bus, "system") == 0)
         {
-          ret = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SYSTEM, cancellable, error);
+          ret = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SYSTEM, cancellable, &local_error);
           goto out;
         }
       else
         {
           if (starter_bus != NULL)
             {
-              g_set_error (error,
+              g_set_error (&local_error,
                            G_IO_ERROR,
                            G_IO_ERROR_FAILED,
                            _("Cannot determine bus address from DBUS_STARTER_BUS_TYPE environment variable"
-                             " - unknown value `%s'"),
+                             " - unknown value '%s'"),
                            starter_bus);
             }
           else
             {
-              g_set_error_literal (error,
+              g_set_error_literal (&local_error,
                                    G_IO_ERROR,
                                    G_IO_ERROR_FAILED,
                                    _("Cannot determine bus address because the DBUS_STARTER_BUS_TYPE environment "
@@ -1003,7 +1616,7 @@ g_dbus_address_get_for_bus_sync (GBusType       bus_type,
       break;
 
     default:
-      g_set_error (error,
+      g_set_error (&local_error,
                    G_IO_ERROR,
                    G_IO_ERROR_FAILED,
                    _("Unknown bus type %d"),
@@ -1012,5 +1625,73 @@ g_dbus_address_get_for_bus_sync (GBusType       bus_type,
     }
 
  out:
+  if (G_UNLIKELY (_g_dbus_debug_address ()))
+    {
+      _g_dbus_debug_print_lock ();
+      if (ret != NULL)
+        {
+          g_print ("GDBus-debug:Address: Returning address '%s' for bus type '%s'\n",
+                   ret,
+                   _g_dbus_enum_to_string (G_TYPE_BUS_TYPE, bus_type));
+        }
+      else
+        {
+          g_print ("GDBus-debug:Address: Cannot look-up address bus type '%s': %s\n",
+                   _g_dbus_enum_to_string (G_TYPE_BUS_TYPE, bus_type),
+                   local_error ? local_error->message : "");
+        }
+      _g_dbus_debug_print_unlock ();
+    }
+
+  if (local_error != NULL)
+    g_propagate_error (error, local_error);
+
   return ret;
 }
+
+/**
+ * g_dbus_address_escape_value:
+ * @string: an unescaped string to be included in a D-Bus address
+ *     as the value in a key-value pair
+ *
+ * Escape @string so it can appear in a D-Bus address as the value
+ * part of a key-value pair.
+ *
+ * For instance, if @string is "/run/bus-for-:0",
+ * this function would return "/run/bus-for-%3A0",
+ * which could be used in a D-Bus address like
+ * "unix:nonce-tcp:host=127.0.0.1,port=42,noncefile=/run/bus-for-%3A0".
+ *
+ * Returns: (transfer full): a copy of @string with all
+ *     non-optionally-escaped bytes escaped
+ *
+ * Since: 2.36
+ */
+gchar *
+g_dbus_address_escape_value (const gchar *string)
+{
+  GString *s;
+  gsize i;
+
+  g_return_val_if_fail (string != NULL, NULL);
+
+  /* There will often not be anything needing escaping at all. */
+  s = g_string_sized_new (strlen (string));
+
+  /* D-Bus address escaping is mostly the same as URI escaping... */
+  g_string_append_uri_escaped (s, string, "\\/", FALSE);
+
+  /* ... but '~' is an unreserved character in URIs, but a
+   * non-optionally-escaped character in D-Bus addresses. */
+  for (i = 0; i < s->len; i++)
+    {
+      if (G_UNLIKELY (s->str[i] == '~'))
+        {
+          s->str[i] = '%';
+          g_string_insert (s, i + 1, "7E");
+          i += 2;
+        }
+    }
+
+  return g_string_free (s, FALSE);
+}