[glib/gdbus-codegen] Start merging gdbus-codegen code



commit 94b907134426e26393a86630dae5ce53baee6ae6
Author: David Zeuthen <davidz redhat com>
Date:   Fri Apr 8 15:14:47 2011 -0400

    Start merging gdbus-codegen code
    
    Signed-off-by: David Zeuthen <davidz redhat com>

 gio/Makefile.am                |   16 +
 gio/gdbusauthobserver.c        |   17 +-
 gio/gdbusinterface.c           |  410 +++++++++++
 gio/gdbusinterface.h           |   79 ++
 gio/gdbusinterfacestub.c       |  693 ++++++++++++++++++
 gio/gdbusinterfacestub.h       |  105 +++
 gio/gdbusobject.c              |  191 +++++
 gio/gdbusobject.h              |   95 +++
 gio/gdbusobjectmanager.c       |  216 ++++++
 gio/gdbusobjectmanager.h       |   89 +++
 gio/gdbusobjectmanagerclient.c | 1575 ++++++++++++++++++++++++++++++++++++++++
 gio/gdbusobjectmanagerclient.h |  129 ++++
 gio/gdbusobjectmanagerserver.c |  898 +++++++++++++++++++++++
 gio/gdbusobjectmanagerserver.h |   80 ++
 gio/gdbusobjectproxy.c         |  315 ++++++++
 gio/gdbusobjectproxy.h         |   72 ++
 gio/gdbusobjectstub.c          |  475 ++++++++++++
 gio/gdbusobjectstub.h          |   87 +++
 gio/gdbusprivate.c             |   18 +
 gio/gdbusprivate.h             |   20 +-
 gio/gdbusproxy.c               |   36 +
 gio/gio-marshal.list           |    3 +
 gio/gio.h                      |    8 +
 gio/gio.symbols                |   96 +++
 gio/gioenums.h                 |   36 +
 gio/giotypes.h                 |   33 +
 26 files changed, 5774 insertions(+), 18 deletions(-)
---
diff --git a/gio/Makefile.am b/gio/Makefile.am
index e2fcd7e..318c22c 100644
--- a/gio/Makefile.am
+++ b/gio/Makefile.am
@@ -82,6 +82,14 @@ gdbus_headers = 			\
 	gdbusintrospection.h		\
 	gdbusmethodinvocation.h		\
 	gdbusserver.h			\
+	gdbusinterface.h		\
+	gdbusinterfacestub.h		\
+	gdbusobject.h			\
+	gdbusobjectstub.h		\
+	gdbusobjectproxy.h		\
+	gdbusobjectmanager.h		\
+	gdbusobjectmanagerclient.h	\
+	gdbusobjectmanagerserver.h	\
 	$(NULL)
 
 gdbus_sources = 							\
@@ -103,6 +111,14 @@ gdbus_sources = 							\
 	gdbusintrospection.h		gdbusintrospection.c		\
 	gdbusmethodinvocation.h		gdbusmethodinvocation.c		\
 	gdbusserver.h			gdbusserver.c			\
+	gdbusinterface.h		gdbusinterface.c		\
+	gdbusinterfacestub.h		gdbusinterfacestub.c		\
+	gdbusobject.h			gdbusobject.c			\
+	gdbusobjectstub.h		gdbusobjectstub.c		\
+	gdbusobjectproxy.h		gdbusobjectproxy.c		\
+	gdbusobjectmanager.h		gdbusobjectmanager.c		\
+	gdbusobjectmanagerclient.h	gdbusobjectmanagerclient.c	\
+	gdbusobjectmanagerserver.h	gdbusobjectmanagerserver.c	\
 	$(NULL)
 
 settings_headers = \
diff --git a/gio/gdbusauthobserver.c b/gio/gdbusauthobserver.c
index 46af595..ff94821 100644
--- a/gio/gdbusauthobserver.c
+++ b/gio/gdbusauthobserver.c
@@ -27,6 +27,7 @@
 #include "gcredentials.h"
 #include "gioenumtypes.h"
 #include "giostream.h"
+#include "gdbusprivate.h"
 
 #include "glibintl.h"
 
@@ -130,22 +131,6 @@ g_dbus_auth_observer_authorize_authenticated_peer_real (GDBusAuthObserver  *obse
   return TRUE;
 }
 
-gboolean
-_g_signal_accumulator_false_handled (GSignalInvocationHint *ihint,
-                                     GValue                *return_accu,
-                                     const GValue          *handler_return,
-                                     gpointer               dummy)
-{
-  gboolean continue_emission;
-  gboolean signal_handled;
-
-  signal_handled = g_value_get_boolean (handler_return);
-  g_value_set_boolean (return_accu, signal_handled);
-  continue_emission = signal_handled;
-
-  return continue_emission;
-}
-
 static void
 g_dbus_auth_observer_class_init (GDBusAuthObserverClass *klass)
 {
diff --git a/gio/gdbusinterface.c b/gio/gdbusinterface.c
new file mode 100644
index 0000000..d8f8594
--- /dev/null
+++ b/gio/gdbusinterface.c
@@ -0,0 +1,410 @@
+/* GDBus - GLib D-Bus Library
+ *
+ * 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
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#include "config.h"
+
+#include "gdbusobject.h"
+#include "gdbusinterface.h"
+#include "gio-marshal.h"
+
+#include "glibintl.h"
+
+/**
+ * SECTION:gdbusinterface
+ * @short_description: Base type for D-Bus interfaces
+ * @include: gio/gio.h
+ *
+ * The #GDBusInterface type is the base type for D-Bus interfaces both
+ * on the service side (see #GDBusInterfaceStub) and client side (see
+ * #GDBusProxy).
+ */
+
+typedef GDBusInterfaceIface GDBusInterfaceInterface;
+G_DEFINE_INTERFACE (GDBusInterface, g_dbus_interface, G_TYPE_OBJECT)
+
+static void
+g_dbus_interface_default_init (GDBusInterfaceIface *iface)
+{
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/**
+ * g_dbus_interface_get_info:
+ * @interface: An exported D-Bus interface.
+ *
+ * Gets D-Bus introspection information for the D-Bus interface
+ * implemented by @interface.
+ *
+ * Returns: (transfer none): A #GDBusInterfaceInfo. Do not free.
+ */
+GDBusInterfaceInfo *
+g_dbus_interface_get_info (GDBusInterface *interface)
+{
+  g_return_val_if_fail (G_IS_DBUS_INTERFACE (interface), NULL);
+  return G_DBUS_INTERFACE_GET_IFACE (interface)->get_info (interface);
+}
+
+/**
+ * g_dbus_interface_get_object:
+ * @interface: An exported D-Bus interface.
+ *
+ * Gets the #GDBusObject that @interface belongs to, if any.
+ *
+ * Returns: (transfer none): A #GDBusObject or %NULL. The returned
+ * reference belongs to @interface and should not be freed.
+ */
+GDBusObject *
+g_dbus_interface_get_object (GDBusInterface *interface)
+{
+  g_return_val_if_fail (G_IS_DBUS_INTERFACE (interface), NULL);
+  return G_DBUS_INTERFACE_GET_IFACE (interface)->get_object (interface);
+}
+
+/**
+ * g_dbus_interface_set_object:
+ * @interface: An exported D-Bus interface.
+ * @object: A #GDBusObject or %NULL.
+ *
+ * Sets the #GDBusObject for @interface to @object.
+ *
+ * Note that @interface will hold a weak reference to @object.
+ */
+void
+g_dbus_interface_set_object (GDBusInterface    *interface,
+                             GDBusObject       *object)
+{
+  g_return_if_fail (G_IS_DBUS_INTERFACE (interface));
+  g_return_if_fail (object == NULL || G_IS_DBUS_OBJECT (object));
+  G_DBUS_INTERFACE_GET_IFACE (interface)->set_object (interface, object);
+}
+
+/* Keep it here for now. TODO: move */
+
+#include <string.h>
+
+/**
+ * g_dbus_gvariant_to_gvalue:
+ * @value: A #GVariant.
+ * @out_gvalue: Return location for the #GValue.
+ *
+ * Convert a #GVariant to a #GValue. If @value is floating, it is consumed.
+ *
+ * Note that the passed @out_gvalue does not have to have a #GType set.
+ *
+ * Returns: %TRUE if the conversion succeeded, %FALSE otherwise.
+ */
+gboolean
+g_dbus_gvariant_to_gvalue (GVariant             *value,
+                           GValue               *out_gvalue)
+{
+  gboolean ret;
+  const GVariantType *type;
+  gchar **array;
+
+  g_return_val_if_fail (value != NULL, FALSE);
+  g_return_val_if_fail (out_gvalue != NULL, FALSE);
+
+  ret = FALSE;
+
+  memset (out_gvalue, '\0', sizeof (GValue));
+
+  switch (g_variant_classify (value))
+    {
+    case G_VARIANT_CLASS_BOOLEAN:
+      g_value_init (out_gvalue, G_TYPE_BOOLEAN);
+      g_value_set_boolean (out_gvalue, g_variant_get_boolean (value));
+      break;
+
+    case G_VARIANT_CLASS_BYTE:
+      g_value_init (out_gvalue, G_TYPE_UCHAR);
+      g_value_set_uchar (out_gvalue, g_variant_get_byte (value));
+      break;
+
+    case G_VARIANT_CLASS_INT16:
+      g_value_init (out_gvalue, G_TYPE_INT);
+      g_value_set_int (out_gvalue, g_variant_get_int16 (value));
+      break;
+
+    case G_VARIANT_CLASS_UINT16:
+      g_value_init (out_gvalue, G_TYPE_UINT);
+      g_value_set_uint (out_gvalue, g_variant_get_uint16 (value));
+      break;
+
+    case G_VARIANT_CLASS_INT32:
+      g_value_init (out_gvalue, G_TYPE_INT);
+      g_value_set_int (out_gvalue, g_variant_get_int32 (value));
+      break;
+
+    case G_VARIANT_CLASS_UINT32:
+      g_value_init (out_gvalue, G_TYPE_UINT);
+      g_value_set_uint (out_gvalue, g_variant_get_uint32 (value));
+      break;
+
+    case G_VARIANT_CLASS_INT64:
+      g_value_init (out_gvalue, G_TYPE_INT64);
+      g_value_set_int64 (out_gvalue, g_variant_get_int64 (value));
+      break;
+
+    case G_VARIANT_CLASS_UINT64:
+      g_value_init (out_gvalue, G_TYPE_UINT64);
+      g_value_set_uint64 (out_gvalue, g_variant_get_uint64 (value));
+      break;
+
+    case G_VARIANT_CLASS_HANDLE:
+      g_value_init (out_gvalue, G_TYPE_INT);
+      g_value_set_int (out_gvalue, g_variant_get_int32 (value));
+      break;
+
+    case G_VARIANT_CLASS_DOUBLE:
+      g_value_init (out_gvalue, G_TYPE_DOUBLE);
+      g_value_set_double (out_gvalue, g_variant_get_double (value));
+      break;
+
+    case G_VARIANT_CLASS_STRING:
+      g_value_init (out_gvalue, G_TYPE_STRING);
+      g_value_set_string (out_gvalue, g_variant_get_string (value, NULL));
+      break;
+
+    case G_VARIANT_CLASS_OBJECT_PATH:
+      g_value_init (out_gvalue, G_TYPE_STRING);
+      g_value_set_string (out_gvalue, g_variant_get_string (value, NULL));
+      break;
+
+    case G_VARIANT_CLASS_SIGNATURE:
+      g_value_init (out_gvalue, G_TYPE_STRING);
+      g_value_set_string (out_gvalue, g_variant_get_string (value, NULL));
+      break;
+
+    case G_VARIANT_CLASS_ARRAY:
+      type = g_variant_get_type (value);
+      switch (g_variant_type_peek_string (type)[1])
+        {
+        case G_VARIANT_CLASS_BYTE:
+          g_value_init (out_gvalue, G_TYPE_STRING);
+          g_value_set_string (out_gvalue, g_variant_get_bytestring (value));
+          break;
+
+        case G_VARIANT_CLASS_STRING:
+          g_value_init (out_gvalue, G_TYPE_STRV);
+          array = g_variant_dup_strv (value, NULL);
+          g_value_take_boxed (out_gvalue, array);
+          break;
+
+        case G_VARIANT_CLASS_ARRAY:
+          switch (g_variant_type_peek_string (type)[2])
+            {
+            case G_VARIANT_CLASS_BYTE:
+              g_value_init (out_gvalue, G_TYPE_STRV);
+              array = g_variant_dup_bytestring_array (value, NULL);
+              g_value_take_boxed (out_gvalue, array);
+              break;
+
+            default:
+              g_value_init (out_gvalue, G_TYPE_VARIANT);
+              g_value_set_variant (out_gvalue, value);
+              break;
+            }
+          break;
+
+        default:
+          g_value_init (out_gvalue, G_TYPE_VARIANT);
+          g_value_set_variant (out_gvalue, value);
+          break;
+        }
+      break;
+
+    case G_VARIANT_CLASS_VARIANT:
+    case G_VARIANT_CLASS_MAYBE:
+    case G_VARIANT_CLASS_TUPLE:
+    case G_VARIANT_CLASS_DICT_ENTRY:
+      g_value_init (out_gvalue, G_TYPE_VARIANT);
+      g_value_set_variant (out_gvalue, value);
+      break;
+    }
+
+  ret = TRUE;
+
+  return ret;
+}
+
+
+/**
+ * g_dbus_gvalue_to_gvariant:
+ * @gvalue: A #GValue to convert to a #GVariant.
+ * @expected_type: The #GVariantType to create.
+ *
+ * Convert a #GValue to #GVariant.
+ *
+ * Returns: A #GVariant (never floating) holding the data from @gvalue
+ * or %NULL in case of error. Free with g_variant_unref().
+ */
+GVariant *
+g_dbus_gvalue_to_gvariant (const GValue         *gvalue,
+                           const GVariantType   *expected_type)
+{
+  GVariant *ret;
+  const gchar *s;
+  const gchar * const *as;
+  const gchar *empty_strv[1] = {NULL};
+
+  g_return_val_if_fail (gvalue != NULL, NULL);
+  g_return_val_if_fail (expected_type != NULL, NULL);
+
+  ret = NULL;
+
+  /* The expected type could easily be e.g. "s" with the GValue holding a string.
+   * because of the UseGVariant annotation
+   */
+  if (G_VALUE_TYPE (gvalue) == G_TYPE_VARIANT)
+    {
+      ret = g_value_dup_variant (gvalue);
+    }
+  else
+    {
+      switch (g_variant_type_peek_string (expected_type)[0])
+        {
+        case G_VARIANT_CLASS_BOOLEAN:
+          ret = g_variant_ref_sink (g_variant_new_boolean (g_value_get_boolean (gvalue)));
+          break;
+
+        case G_VARIANT_CLASS_BYTE:
+          ret = g_variant_ref_sink (g_variant_new_byte (g_value_get_uchar (gvalue)));
+          break;
+
+        case G_VARIANT_CLASS_INT16:
+          ret = g_variant_ref_sink (g_variant_new_int16 (g_value_get_int (gvalue)));
+          break;
+
+        case G_VARIANT_CLASS_UINT16:
+          ret = g_variant_ref_sink (g_variant_new_uint16 (g_value_get_uint (gvalue)));
+          break;
+
+        case G_VARIANT_CLASS_INT32:
+          ret = g_variant_ref_sink (g_variant_new_int32 (g_value_get_int (gvalue)));
+          break;
+
+        case G_VARIANT_CLASS_UINT32:
+          ret = g_variant_ref_sink (g_variant_new_uint32 (g_value_get_uint (gvalue)));
+          break;
+
+        case G_VARIANT_CLASS_INT64:
+          ret = g_variant_ref_sink (g_variant_new_int64 (g_value_get_int64 (gvalue)));
+          break;
+
+        case G_VARIANT_CLASS_UINT64:
+          ret = g_variant_ref_sink (g_variant_new_uint64 (g_value_get_uint64 (gvalue)));
+          break;
+
+        case G_VARIANT_CLASS_HANDLE:
+          ret = g_variant_ref_sink (g_variant_new_handle (g_value_get_int (gvalue)));
+          break;
+
+        case G_VARIANT_CLASS_DOUBLE:
+          ret = g_variant_ref_sink (g_variant_new_double (g_value_get_double (gvalue)));
+          break;
+
+        case G_VARIANT_CLASS_STRING:
+          s = g_value_get_string (gvalue);
+          if (s == NULL)
+            s = "";
+          ret = g_variant_ref_sink (g_variant_new_string (s));
+          break;
+
+        case G_VARIANT_CLASS_OBJECT_PATH:
+          s = g_value_get_string (gvalue);
+          if (s == NULL)
+            s = "/";
+          ret = g_variant_ref_sink (g_variant_new_object_path (s));
+          break;
+
+        case G_VARIANT_CLASS_SIGNATURE:
+          s = g_value_get_string (gvalue);
+          if (s == NULL)
+            s = "";
+          ret = g_variant_ref_sink (g_variant_new_signature (s));
+          break;
+
+        case G_VARIANT_CLASS_ARRAY:
+          switch (g_variant_type_peek_string (expected_type)[1])
+            {
+            case G_VARIANT_CLASS_BYTE:
+              s = g_value_get_string (gvalue);
+              if (s == NULL)
+                s = "";
+              ret = g_variant_ref_sink (g_variant_new_bytestring (s));
+              break;
+
+            case G_VARIANT_CLASS_STRING:
+              as = g_value_get_boxed (gvalue);
+              if (as == NULL)
+                as = empty_strv;
+              ret = g_variant_ref_sink (g_variant_new_strv (as, -1));
+              break;
+
+            case G_VARIANT_CLASS_ARRAY:
+              switch (g_variant_type_peek_string (expected_type)[2])
+                {
+                case G_VARIANT_CLASS_BYTE:
+                  as = g_value_get_boxed (gvalue);
+                  if (as == NULL)
+                    as = empty_strv;
+                  ret = g_variant_ref_sink (g_variant_new_bytestring_array (as, -1));
+                  break;
+
+                default:
+                  ret = g_value_dup_variant (gvalue);
+                  break;
+                }
+              break;
+
+            default:
+              ret = g_value_dup_variant (gvalue);
+              break;
+            }
+          break;
+
+        default:
+        case G_VARIANT_CLASS_VARIANT:
+        case G_VARIANT_CLASS_MAYBE:
+        case G_VARIANT_CLASS_TUPLE:
+        case G_VARIANT_CLASS_DICT_ENTRY:
+          ret = g_value_dup_variant (gvalue);
+          break;
+        }
+    }
+
+  /* Could be that the GValue is holding a NULL GVariant - in that case,
+   * we return an "empty" GVariant instead of a NULL GVariant
+   */
+  if (ret == NULL)
+    {
+      GVariant *untrusted_empty;
+      untrusted_empty = g_variant_new_from_data (expected_type, NULL, 0, FALSE, NULL, NULL);
+      ret = g_variant_ref_sink (g_variant_get_normal_form (untrusted_empty));
+      g_variant_unref (untrusted_empty);
+    }
+
+  g_assert (!g_variant_is_floating (ret));
+
+  return ret;
+}
diff --git a/gio/gdbusinterface.h b/gio/gdbusinterface.h
new file mode 100644
index 0000000..275f43e
--- /dev/null
+++ b/gio/gdbusinterface.h
@@ -0,0 +1,79 @@
+/* GDBus - GLib D-Bus Library
+ *
+ * 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
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#ifndef __G_DBUS_INTERFACE_H__
+#define __G_DBUS_INTERFACE_H__
+
+#include <gio/giotypes.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_DBUS_INTERFACE         (g_dbus_interface_get_type())
+#define G_DBUS_INTERFACE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_INTERFACE, GDBusInterface))
+#define G_IS_DBUS_INTERFACE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_INTERFACE))
+#define G_DBUS_INTERFACE_GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE((o), G_TYPE_DBUS_INTERFACE, GDBusInterfaceIface))
+
+/**
+ * GDBusInterface:
+ *
+ * Base type for D-Bus interfaces.
+ */
+typedef struct _GDBusInterface GDBusInterface; /* Dummy typedef */
+
+typedef struct _GDBusInterfaceIface GDBusInterfaceIface;
+
+/**
+ * GDBusInterfaceIface:
+ * @parent_iface: The parent interface.
+ * @get_info: Returns a #GDBusInterfaceInfo. See g_dbus_interface_get_info().
+ * @get_object: Gets the enclosing #GDBusObject. See g_dbus_interface_get_object().
+ * @set_object: Sets the enclosing #GDBusObject. See g_dbus_interface_set_object().
+ *
+ * Base type for D-Bus interfaces.
+ */
+struct _GDBusInterfaceIface
+{
+  GTypeInterface parent_iface;
+
+  /* Virtual Functions */
+  GDBusInterfaceInfo   *(*get_info)   (GDBusInterface      *interface);
+  GDBusObject          *(*get_object) (GDBusInterface      *interface);
+  void                  (*set_object) (GDBusInterface      *interface,
+                                       GDBusObject         *object);
+};
+
+GType                 g_dbus_interface_get_type         (void) G_GNUC_CONST;
+GDBusInterfaceInfo   *g_dbus_interface_get_info         (GDBusInterface      *interface);
+GDBusObject          *g_dbus_interface_get_object       (GDBusInterface      *interface);
+void                  g_dbus_interface_set_object       (GDBusInterface      *interface,
+                                                         GDBusObject         *object);
+
+/* Keep it here for now. TODO: move */
+
+gboolean g_dbus_gvariant_to_gvalue (GVariant             *value,
+                                    GValue               *out_gvalue);
+GVariant *g_dbus_gvalue_to_gvariant (const GValue         *gvalue,
+                                     const GVariantType   *expected_type);
+
+G_END_DECLS
+
+#endif /* __G_DBUS_INTERFACE_H__ */
diff --git a/gio/gdbusinterfacestub.c b/gio/gdbusinterfacestub.c
new file mode 100644
index 0000000..4eee99b
--- /dev/null
+++ b/gio/gdbusinterfacestub.c
@@ -0,0 +1,693 @@
+/* GDBus - GLib D-Bus Library
+ *
+ * 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
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#include "config.h"
+
+#include "gdbusinterface.h"
+#include "gdbusinterfacestub.h"
+#include "gdbusobjectstub.h"
+#include "gio-marshal.h"
+#include "gioenumtypes.h"
+#include "gdbusprivate.h"
+#include "gdbusmethodinvocation.h"
+#include "gdbusconnection.h"
+#include "gioscheduler.h"
+#include "gioerror.h"
+
+#include "glibintl.h"
+
+/**
+ * SECTION:gdbusinterfacestub
+ * @short_description: Service-side D-Bus interface
+ * @include: gio/gio.h
+ *
+ * Abstract base class for D-Bus interfaces on the service side.
+ */
+
+struct _GDBusInterfaceStubPrivate
+{
+  GDBusObject *object;
+  GDBusInterfaceStubFlags flags;
+  guint registration_id;
+
+  GDBusConnection *connection;
+  gchar *object_path;
+  GDBusInterfaceVTable *hooked_vtable;
+};
+
+enum
+{
+  G_AUTHORIZE_METHOD_SIGNAL,
+  LAST_SIGNAL
+};
+
+enum
+{
+  PROP_0,
+  PROP_G_FLAGS
+};
+
+static guint signals[LAST_SIGNAL] = {0};
+
+static void dbus_interface_interface_init (GDBusInterfaceIface *iface);
+
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GDBusInterfaceStub, g_dbus_interface_stub, G_TYPE_OBJECT,
+                                  G_IMPLEMENT_INTERFACE (G_TYPE_DBUS_INTERFACE, dbus_interface_interface_init));
+
+static void
+g_dbus_interface_stub_finalize (GObject *object)
+{
+  GDBusInterfaceStub *stub = G_DBUS_INTERFACE_STUB (object);
+  /* unexport if already exported */
+  if (stub->priv->registration_id > 0)
+    g_dbus_interface_stub_unexport (stub);
+
+  g_assert (stub->priv->connection == NULL);
+  g_assert (stub->priv->object_path == NULL);
+  g_assert (stub->priv->hooked_vtable == NULL);
+
+  if (stub->priv->object != NULL)
+    g_object_remove_weak_pointer (G_OBJECT (stub->priv->object), (gpointer *) &stub->priv->object);
+  G_OBJECT_CLASS (g_dbus_interface_stub_parent_class)->finalize (object);
+}
+
+static void
+g_dbus_interface_stub_get_property (GObject      *object,
+                                    guint         prop_id,
+                                    GValue       *value,
+                                    GParamSpec   *pspec)
+{
+  GDBusInterfaceStub *stub = G_DBUS_INTERFACE_STUB (object);
+
+  switch (prop_id)
+    {
+    case PROP_G_FLAGS:
+      g_value_set_flags (value, g_dbus_interface_stub_get_flags (stub));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+g_dbus_interface_stub_set_property (GObject      *object,
+                                    guint         prop_id,
+                                    const GValue *value,
+                                    GParamSpec   *pspec)
+{
+  GDBusInterfaceStub *stub = G_DBUS_INTERFACE_STUB (object);
+
+  switch (prop_id)
+    {
+    case PROP_G_FLAGS:
+      g_dbus_interface_stub_set_flags (stub, g_value_get_flags (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static gboolean
+g_dbus_interface_stub_g_authorize_method_default (GDBusInterfaceStub    *stub,
+                                                  GDBusMethodInvocation *invocation)
+{
+  return TRUE;
+}
+
+static void
+g_dbus_interface_stub_class_init (GDBusInterfaceStubClass *klass)
+{
+  GObjectClass *gobject_class;
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gobject_class->finalize     = g_dbus_interface_stub_finalize;
+  gobject_class->set_property = g_dbus_interface_stub_set_property;
+  gobject_class->get_property = g_dbus_interface_stub_get_property;
+
+  klass->g_authorize_method = g_dbus_interface_stub_g_authorize_method_default;
+
+  /**
+   * GDBusInterfaceStub:g-flags:
+   *
+   * Flags from the #GDBusInterfaceStubFlags enumeration.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_G_FLAGS,
+                                   g_param_spec_flags ("g-flags",
+                                                       "g-flags",
+                                                       "Flags for the interface stub",
+                                                       G_TYPE_DBUS_INTERFACE_STUB_FLAGS,
+                                                       G_DBUS_INTERFACE_STUB_FLAGS_NONE,
+                                                       G_PARAM_READABLE |
+                                                       G_PARAM_WRITABLE |
+                                                       G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GDBusInterfaceStub::g-authorize-method:
+   * @interface: The #GDBusInterfaceStub emitting the signal.
+   * @invocation: A #GDBusMethodInvocation.
+   *
+   * Emitted when a method is invoked by a remote caller and used to
+   * determine if the method call is authorized.
+   *
+   * Note that this signal is emitted in a thread dedicated to
+   * handling the method call so handlers are allowed to perform
+   * blocking IO. This means that it is appropriate to call
+   * e.g. <ulink
+   * url="http://hal.freedesktop.org/docs/polkit/PolkitAuthority.html#polkit-authority-check-authorization-sync";>polkit_authority_check_authorization_sync()</ulink>
+   * with the <ulink
+   * url="http://hal.freedesktop.org/docs/polkit/PolkitAuthority.html#POLKIT-CHECK-AUTHORIZATION-FLAGS-ALLOW-USER-INTERACTION:CAPS";>POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION</ulink> flag set.
+   *
+   * If %FALSE is returned then no further handlers are run and the
+   * signal handler must take ownership of @invocation and finish
+   * handling the call (e.g. return an error via
+   * g_dbus_method_invocation_return_error()).
+   *
+   * Otherwise, if %TRUE is returned, signal emission continues. If no
+   * handlers return %FALSE, then the method is dispatched. If
+   * @interface has an enclosing #GDBusObjectStub, then the
+   * #GDBusObjectStub::authorize-method signal handlers run before the
+   * handlers for this signal.
+   *
+   * The default class handler just returns %TRUE.
+   *
+   * Please note that the common case is optimized: if no signals
+   * handlers are connected and the default class handler isn't
+   * overridden (for both @interface and the enclosing
+   * #GDBusObjectStub, if any) and #GDBusInterfaceStub:g-flags does
+   * not have the
+   * %G_DBUS_INTERFACE_STUB_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD
+   * flags set, no dedicated thread is ever used and the call will be
+   * handled in the same thread as the object that @interface belongs
+   * to was exported in.
+   *
+   * Returns: %TRUE if the call is authorized, %FALSE otherwise.
+   */
+  signals[G_AUTHORIZE_METHOD_SIGNAL] =
+    g_signal_new ("g-authorize-method",
+                  G_TYPE_DBUS_INTERFACE_STUB,
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (GDBusInterfaceStubClass, g_authorize_method),
+                  _g_signal_accumulator_false_handled,
+                  NULL,
+                  _gio_marshal_BOOLEAN__OBJECT,
+                  G_TYPE_BOOLEAN,
+                  1,
+                  G_TYPE_DBUS_METHOD_INVOCATION);
+
+  g_type_class_add_private (klass, sizeof (GDBusInterfaceStubPrivate));
+}
+
+static void
+g_dbus_interface_stub_init (GDBusInterfaceStub *stub)
+{
+  stub->priv = G_TYPE_INSTANCE_GET_PRIVATE (stub, G_TYPE_DBUS_INTERFACE_STUB, GDBusInterfaceStubPrivate);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/**
+ * g_dbus_interface_stub_get_flags:
+ * @stub: A #GDBusInterfaceStub.
+ *
+ * Gets the #GDBusInterfaceStubFlags that describes what the behavior
+ * of @stub
+ *
+ * Returns: One or more flags from the #GDBusInterfaceStubFlags enumeration.
+ */
+GDBusInterfaceStubFlags
+g_dbus_interface_stub_get_flags (GDBusInterfaceStub  *stub)
+{
+  g_return_val_if_fail (G_IS_DBUS_INTERFACE_STUB (stub), G_DBUS_INTERFACE_STUB_FLAGS_NONE);
+  return stub->priv->flags;
+}
+
+/**
+ * g_dbus_interface_stub_set_flags:
+ * @stub: A #GDBusInterfaceStub.
+ * @flags: Flags from the #GDBusInterfaceStubFlags enumeration.
+ *
+ * Sets flags describing what the behavior of @stub should be.
+ */
+void
+g_dbus_interface_stub_set_flags (GDBusInterfaceStub      *stub,
+                                 GDBusInterfaceStubFlags  flags)
+{
+  g_return_if_fail (G_IS_DBUS_INTERFACE_STUB (stub));
+  if (stub->priv->flags != flags)
+    {
+      stub->priv->flags = flags;
+      g_object_notify (G_OBJECT (stub), "g-flags");
+    }
+}
+
+/**
+ * g_dbus_interface_stub_get_info:
+ * @stub: A #GDBusInterfaceStub.
+ *
+ * Gets D-Bus introspection information for the D-Bus interface
+ * implemented by @interface.
+ *
+ * Returns: (transfer none): A #GDBusInterfaceInfo (never %NULL). Do not free.
+ */
+GDBusInterfaceInfo *
+g_dbus_interface_stub_get_info (GDBusInterfaceStub *stub)
+{
+  GDBusInterfaceInfo *ret;
+  g_return_val_if_fail (G_IS_DBUS_INTERFACE_STUB (stub), NULL);
+  ret = G_DBUS_INTERFACE_STUB_GET_CLASS (stub)->get_info (stub);
+  g_warn_if_fail (ret != NULL);
+  return ret;
+}
+
+/**
+ * g_dbus_interface_stub_get_vtable:
+ * @stub: A #GDBusInterfaceStub.
+ *
+ * Gets the interface vtable for the D-Bus interface implemented by
+ * @interface. The returned function pointers should expect @stub
+ * itself to be passed as @user_data.
+ *
+ * Returns: A #GDBusInterfaceVTable (never %NULL).
+ */
+GDBusInterfaceVTable *
+g_dbus_interface_stub_get_vtable (GDBusInterfaceStub *stub)
+{
+  GDBusInterfaceVTable *ret;
+  g_return_val_if_fail (G_IS_DBUS_INTERFACE_STUB (stub), NULL);
+  ret = G_DBUS_INTERFACE_STUB_GET_CLASS (stub)->get_vtable (stub);
+  g_warn_if_fail (ret != NULL);
+  return ret;
+}
+
+/**
+ * g_dbus_interface_stub_get_properties:
+ * @stub: A #GDBusInterfaceStub.
+ *
+ * Gets all D-Bus properties for @stub.
+ *
+ * Returns: A new, floating, #GVariant. Free with g_variant_unref().
+ */
+GVariant *
+g_dbus_interface_stub_get_properties (GDBusInterfaceStub *stub)
+{
+  GVariant *ret;
+  g_return_val_if_fail (G_IS_DBUS_INTERFACE_STUB (stub), NULL);
+  ret = G_DBUS_INTERFACE_STUB_GET_CLASS (stub)->get_properties (stub);
+  g_warn_if_fail (g_variant_is_floating (ret));
+  return ret;
+}
+
+/**
+ * g_dbus_interface_stub_flush:
+ * @stub: A #GDBusInterfaceStub.
+ *
+ * If @stub has outstanding changes, request for these changes to be
+ * emitted immediately.
+ *
+ * For example, an exported D-Bus interface may queue up property
+ * changes and emit the
+ * <literal>org.freedesktop.DBus.Properties::PropertiesChanged</literal>
+ * signal later (e.g. in an idle handler). This technique is useful
+ * for collapsing multiple property changes into one.
+ */
+void
+g_dbus_interface_stub_flush (GDBusInterfaceStub *stub)
+{
+  g_return_if_fail (G_IS_DBUS_INTERFACE_STUB (stub));
+  G_DBUS_INTERFACE_STUB_GET_CLASS (stub)->flush (stub);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static GDBusInterfaceInfo *
+_g_dbus_interface_stub_get_info (GDBusInterface *interface)
+{
+  GDBusInterfaceStub *stub = G_DBUS_INTERFACE_STUB (interface);
+  return g_dbus_interface_stub_get_info (stub);
+}
+
+static GDBusObject *
+g_dbus_interface_stub_get_object (GDBusInterface *interface)
+{
+  GDBusInterfaceStub *stub = G_DBUS_INTERFACE_STUB (interface);
+  return stub->priv->object;
+}
+
+static void
+g_dbus_interface_stub_set_object (GDBusInterface *interface,
+                                  GDBusObject    *object)
+{
+  GDBusInterfaceStub *stub = G_DBUS_INTERFACE_STUB (interface);
+  if (stub->priv->object != NULL)
+    g_object_remove_weak_pointer (G_OBJECT (stub->priv->object), (gpointer *) &stub->priv->object);
+  stub->priv->object = object;
+  if (object != NULL)
+    g_object_add_weak_pointer (G_OBJECT (stub->priv->object), (gpointer *) &stub->priv->object);
+}
+
+static void
+dbus_interface_interface_init (GDBusInterfaceIface *iface)
+{
+  iface->get_info = _g_dbus_interface_stub_get_info;
+  iface->get_object  = g_dbus_interface_stub_get_object;
+  iface->set_object  = g_dbus_interface_stub_set_object;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef struct
+{
+  volatile gint ref_count;
+  GDBusInterfaceStub           *stub;
+  GDBusInterfaceMethodCallFunc  method_call_func;
+  GDBusMethodInvocation        *invocation;
+  GMainContext                 *context;
+} DispatchData;
+
+static void
+dispatch_data_unref (DispatchData *data)
+{
+  if (g_atomic_int_dec_and_test (&data->ref_count))
+    {
+      if (data->context != NULL)
+        g_main_context_unref (data->context);
+      g_free (data);
+    }
+}
+
+static DispatchData *
+dispatch_data_ref (DispatchData *data)
+{
+  g_atomic_int_inc (&data->ref_count);
+  return data;
+}
+
+static gboolean
+dispatch_invoke_in_context_func (gpointer user_data)
+{
+  DispatchData *data = user_data;
+  data->method_call_func (g_dbus_method_invocation_get_connection (data->invocation),
+                          g_dbus_method_invocation_get_sender (data->invocation),
+                          g_dbus_method_invocation_get_object_path (data->invocation),
+                          g_dbus_method_invocation_get_interface_name (data->invocation),
+                          g_dbus_method_invocation_get_method_name (data->invocation),
+                          g_dbus_method_invocation_get_parameters (data->invocation),
+                          data->invocation,
+                          g_dbus_method_invocation_get_user_data (data->invocation));
+  return FALSE;
+}
+
+static gboolean
+dispatch_in_thread_func (GIOSchedulerJob *job,
+                         GCancellable    *cancellable,
+                         gpointer         user_data)
+{
+  DispatchData *data = user_data;
+  gboolean authorized;
+
+  /* first check on the enclosing object (if any), then the interface */
+  authorized = TRUE;
+  if (data->stub->priv->object != NULL)
+    {
+      g_signal_emit_by_name (data->stub->priv->object,
+                             "authorize-method",
+                             data->stub,
+                             data->invocation,
+                             &authorized);
+    }
+  if (authorized)
+    {
+      g_signal_emit (data->stub,
+                     signals[G_AUTHORIZE_METHOD_SIGNAL],
+                     0,
+                     data->invocation,
+                     &authorized);
+    }
+
+  if (authorized)
+    {
+      gboolean run_in_thread;
+      run_in_thread = (data->stub->priv->flags & G_DBUS_INTERFACE_STUB_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD);
+      if (run_in_thread)
+        {
+          /* might as well just re-use the existing thread */
+          data->method_call_func (g_dbus_method_invocation_get_connection (data->invocation),
+                                  g_dbus_method_invocation_get_sender (data->invocation),
+                                  g_dbus_method_invocation_get_object_path (data->invocation),
+                                  g_dbus_method_invocation_get_interface_name (data->invocation),
+                                  g_dbus_method_invocation_get_method_name (data->invocation),
+                                  g_dbus_method_invocation_get_parameters (data->invocation),
+                                  data->invocation,
+                                  g_dbus_method_invocation_get_user_data (data->invocation));
+        }
+      else
+        {
+          /* bah, back to original context */
+          g_main_context_invoke_full (data->context,
+                                      G_PRIORITY_DEFAULT,
+                                      dispatch_invoke_in_context_func,
+                                      dispatch_data_ref (data),
+                                      (GDestroyNotify) dispatch_data_unref);
+        }
+    }
+  else
+    {
+      /* do nothing */
+    }
+
+  return FALSE;
+}
+
+static void
+g_dbus_interface_method_dispatch_helper (GDBusInterfaceStub           *stub,
+                                         GDBusInterfaceMethodCallFunc  method_call_func,
+                                         GDBusMethodInvocation        *invocation)
+{
+  gboolean has_handlers;
+  gboolean has_default_class_handler;
+  gboolean emit_authorized_signal;
+  gboolean run_in_thread;
+
+  g_return_if_fail (G_IS_DBUS_INTERFACE_STUB (stub));
+  g_return_if_fail (method_call_func != NULL);
+  g_return_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation));
+
+  /* optimization for the common case where
+   *
+   *  a) no handler is connected and class handler is not overridden (both interface and object); and
+   *  b) method calls are not dispatched in a thread
+   */
+  has_handlers = g_signal_has_handler_pending (stub,
+                                               signals[G_AUTHORIZE_METHOD_SIGNAL],
+                                               0,
+                                               TRUE);
+  has_default_class_handler = (G_DBUS_INTERFACE_STUB_GET_CLASS (stub)->g_authorize_method ==
+                               g_dbus_interface_stub_g_authorize_method_default);
+
+  emit_authorized_signal = (has_handlers || !has_default_class_handler);
+  if (!emit_authorized_signal)
+    {
+      if (stub->priv->object != NULL)
+        emit_authorized_signal = _g_dbus_object_stub_has_authorize_method_handlers (G_DBUS_OBJECT_STUB (stub->priv->object));
+    }
+
+  run_in_thread = (stub->priv->flags & G_DBUS_INTERFACE_STUB_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD);
+  if (!emit_authorized_signal && !run_in_thread)
+    {
+      method_call_func (g_dbus_method_invocation_get_connection (invocation),
+                        g_dbus_method_invocation_get_sender (invocation),
+                        g_dbus_method_invocation_get_object_path (invocation),
+                        g_dbus_method_invocation_get_interface_name (invocation),
+                        g_dbus_method_invocation_get_method_name (invocation),
+                        g_dbus_method_invocation_get_parameters (invocation),
+                        invocation,
+                        g_dbus_method_invocation_get_user_data (invocation));
+    }
+  else
+    {
+      DispatchData *data;
+      data = g_new0 (DispatchData, 1);
+      data->stub = stub;
+      data->method_call_func = method_call_func;
+      data->invocation = invocation;
+      data->context = g_main_context_get_thread_default ();
+      data->ref_count = 1;
+      if (data->context != NULL)
+        g_main_context_ref (data->context);
+      g_io_scheduler_push_job (dispatch_in_thread_func,
+                               data,
+                               (GDestroyNotify) dispatch_data_unref,
+                               G_PRIORITY_DEFAULT,
+                               NULL); /* GCancellable* */
+    }
+}
+
+static void
+stub_intercept_handle_method_call(GDBusConnection *connection,
+                                  const gchar *sender,
+                                  const gchar *object_path,
+                                  const gchar *interface_name,
+                                  const gchar *method_name,
+                                  GVariant *parameters,
+                                  GDBusMethodInvocation *invocation,
+                                  gpointer user_data)
+{
+  GDBusInterfaceStub *stub = G_DBUS_INTERFACE_STUB (user_data);
+  g_dbus_interface_method_dispatch_helper (stub,
+                                           g_dbus_interface_stub_get_vtable (stub)->method_call,
+                                           invocation);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/**
+ * g_dbus_interface_stub_get_connection:
+ * @stub: A #GDBusInterfaceStub.
+ *
+ * Gets the connection that @stub is exported on, if any.
+ *
+ * Returns: (transfer none): A #GDBusConnection or %NULL if @stub is
+ * not exported anywhere. Do not free, the object belongs to @stub.
+ */
+GDBusConnection *
+g_dbus_interface_stub_get_connection (GDBusInterfaceStub *stub)
+{
+  g_return_val_if_fail (G_IS_DBUS_INTERFACE_STUB (stub), NULL);
+  return stub->priv->connection;
+}
+
+/**
+ * g_dbus_interface_stub_get_object_path:
+ * @stub: A #GDBusInterfaceStub.
+ *
+ * Gets the object that that @stub is exported on, if any.
+ *
+ * Returns: A string owned by @stub or %NULL if stub is not exported
+ * anywhere. Do not free, the string belongs to @stub.
+ */
+const gchar *
+g_dbus_interface_stub_get_object_path (GDBusInterfaceStub *stub)
+{
+  g_return_val_if_fail (G_IS_DBUS_INTERFACE_STUB (stub), NULL);
+  return stub->priv->object_path;
+}
+
+/**
+ * g_dbus_interface_stub_export:
+ * @stub: The D-Bus interface to export.
+ * @connection: A #GDBusConnection to export @stub on.
+ * @object_path: The path to export the interface at.
+ * @error: Return location for error or %NULL.
+ *
+ * Exports @stubs at @object_path on @connection.
+ *
+ * Use g_dbus_interface_stub_unexport() to unexport the object.
+ *
+ * Returns: %TRUE if the interface was exported, other %FALSE with
+ * @error set.
+ */
+gboolean
+g_dbus_interface_stub_export (GDBusInterfaceStub  *stub,
+                              GDBusConnection     *connection,
+                              const gchar         *object_path,
+                              GError             **error)
+{
+  gboolean ret;
+
+  g_return_val_if_fail (G_IS_DBUS_INTERFACE_STUB (stub), 0);
+  g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), 0);
+  g_return_val_if_fail (g_variant_is_object_path (object_path), 0);
+  g_return_val_if_fail (error == NULL || *error == NULL, 0);
+
+  ret = FALSE;
+  if (stub->priv->registration_id > 0)
+    {
+      g_set_error_literal (error,
+                           G_IO_ERROR,
+                           G_IO_ERROR_FAILED, /* TODO: new error code */
+                           "The object is already exported");
+      goto out;
+    }
+
+  g_assert (stub->priv->connection == NULL);
+  g_assert (stub->priv->object_path == NULL);
+  g_assert (stub->priv->hooked_vtable == NULL);
+
+  /* Hook the vtable since we need to intercept method calls for
+   * ::g-authorize-method and for dispatching in thread vs
+   * context
+   */
+  stub->priv->hooked_vtable = g_memdup (g_dbus_interface_stub_get_vtable (stub), sizeof (GDBusInterfaceVTable));
+  stub->priv->hooked_vtable->method_call = stub_intercept_handle_method_call;
+
+  stub->priv->connection = g_object_ref (connection);
+  stub->priv->object_path = g_strdup (object_path);
+  stub->priv->registration_id = g_dbus_connection_register_object (connection,
+                                                                   object_path,
+                                                                   g_dbus_interface_stub_get_info (stub),
+                                                                   stub->priv->hooked_vtable,
+                                                                   stub,
+                                                                   NULL, /* user_data_free_func */
+                                                                   error);
+  if (stub->priv->registration_id == 0)
+    goto out;
+
+  ret = TRUE;
+
+ out:
+  return ret;
+}
+
+/**
+ * g_dbus_interface_stub_unexport:
+ * @stub: A #GDBusInterfaceStub.
+ *
+ * Stops exporting an interface previously exported with
+ * g_dbus_interface_stub_export().
+ */
+void
+g_dbus_interface_stub_unexport (GDBusInterfaceStub *stub)
+{
+  g_return_if_fail (G_IS_DBUS_INTERFACE_STUB (stub));
+  g_return_if_fail (stub->priv->registration_id > 0);
+
+  g_assert (stub->priv->connection != NULL);
+  g_assert (stub->priv->object_path != NULL);
+  g_assert (stub->priv->hooked_vtable != NULL);
+
+  g_warn_if_fail (g_dbus_connection_unregister_object (stub->priv->connection,
+                                                       stub->priv->registration_id));
+
+  g_object_unref (stub->priv->connection);
+  g_free (stub->priv->object_path);
+  stub->priv->connection = NULL;
+  stub->priv->object_path = NULL;
+  stub->priv->hooked_vtable = NULL;
+  stub->priv->registration_id = 0;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
diff --git a/gio/gdbusinterfacestub.h b/gio/gdbusinterfacestub.h
new file mode 100644
index 0000000..8e03d72
--- /dev/null
+++ b/gio/gdbusinterfacestub.h
@@ -0,0 +1,105 @@
+/* GDBus - GLib D-Bus Library
+ *
+ * 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
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#ifndef __G_DBUS_INTERFACE_STUB_H__
+#define __G_DBUS_INTERFACE_STUB_H__
+
+#include <gio/giotypes.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_DBUS_INTERFACE_STUB         (g_dbus_interface_stub_get_type ())
+#define G_DBUS_INTERFACE_STUB(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_INTERFACE_STUB, GDBusInterfaceStub))
+#define G_DBUS_INTERFACE_STUB_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_INTERFACE_STUB, GDBusInterfaceStubClass))
+#define G_DBUS_INTERFACE_STUB_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_INTERFACE_STUB, GDBusInterfaceStubClass))
+#define G_IS_DBUS_INTERFACE_STUB(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_INTERFACE_STUB))
+#define G_IS_DBUS_INTERFACE_STUB_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_INTERFACE_STUB))
+
+typedef struct _GDBusInterfaceStubClass   GDBusInterfaceStubClass;
+typedef struct _GDBusInterfaceStubPrivate GDBusInterfaceStubPrivate;
+
+/**
+ * GDBusInterfaceStub:
+ *
+ * The #GDBusInterfaceStub structure contains private data and should
+ * only be accessed using the provided API.
+ */
+struct _GDBusInterfaceStub
+{
+  /*< private >*/
+  GObject parent_instance;
+  GDBusInterfaceStubPrivate *priv;
+};
+
+/**
+ * GDBusInterfaceStubClass:
+ * @parent_class: The parent class.
+ * @get_info: Returns a #GDBusInterfaceInfo. See g_dbus_interface_stub_get_info() for details.
+ * @get_vtable: Returns a #GDBusInterfaceVTable. See g_dbus_interface_stub_get_vtable() for details.
+ * @get_properties: Returns a new, floating, #GVariant with all properties. See g_dbus_interface_stub_get_properties().
+ * @flush: Emits outstanding changes, if any. See g_dbus_interface_stub_flush().
+ * @g_authorize_method: Signal class handler for the #GDBusInterfaceStub::g-authorize-method signal.
+ *
+ * Class structure for #GDBusInterfaceStub.
+ */
+struct _GDBusInterfaceStubClass
+{
+  GObjectClass parent_class;
+
+  /* Virtual Functions */
+  GDBusInterfaceInfo   *(*get_info)       (GDBusInterfaceStub  *stub);
+  GDBusInterfaceVTable *(*get_vtable)     (GDBusInterfaceStub  *stub);
+  GVariant             *(*get_properties) (GDBusInterfaceStub  *stub);
+  void                  (*flush)          (GDBusInterfaceStub  *stub);
+
+  /*< private >*/
+  gpointer vfunc_padding[8];
+  /*< public >*/
+
+  /* Signals */
+  gboolean (*g_authorize_method) (GDBusInterfaceStub    *stub,
+                                  GDBusMethodInvocation *invocation);
+
+  /*< private >*/
+  gpointer signal_padding[8];
+};
+
+GType                    g_dbus_interface_stub_get_type       (void) G_GNUC_CONST;
+GDBusInterfaceStubFlags  g_dbus_interface_stub_get_flags      (GDBusInterfaceStub      *stub);
+void                     g_dbus_interface_stub_set_flags      (GDBusInterfaceStub      *stub,
+                                                               GDBusInterfaceStubFlags  flags);
+GDBusInterfaceInfo      *g_dbus_interface_stub_get_info       (GDBusInterfaceStub      *stub);
+GDBusInterfaceVTable    *g_dbus_interface_stub_get_vtable     (GDBusInterfaceStub      *stub);
+GVariant                *g_dbus_interface_stub_get_properties (GDBusInterfaceStub      *stub);
+void                     g_dbus_interface_stub_flush          (GDBusInterfaceStub      *stub);
+
+gboolean                 g_dbus_interface_stub_export          (GDBusInterfaceStub      *stub,
+                                                                GDBusConnection         *connection,
+                                                                const gchar             *object_path,
+                                                                GError                 **error);
+void                     g_dbus_interface_stub_unexport        (GDBusInterfaceStub      *stub);
+GDBusConnection         *g_dbus_interface_stub_get_connection  (GDBusInterfaceStub      *stub);
+const gchar             *g_dbus_interface_stub_get_object_path (GDBusInterfaceStub      *stub);
+
+G_END_DECLS
+
+#endif /* __G_DBUS_INTERFACE_STUB_H */
diff --git a/gio/gdbusobject.c b/gio/gdbusobject.c
new file mode 100644
index 0000000..2211d4c
--- /dev/null
+++ b/gio/gdbusobject.c
@@ -0,0 +1,191 @@
+/* GDBus - GLib D-Bus Library
+ *
+ * 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
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#include "config.h"
+
+#include "gdbusobject.h"
+#include "gdbusinterface.h"
+#include "gdbusutils.h"
+#include "gio-marshal.h"
+
+#include "glibintl.h"
+
+/**
+ * SECTION:gdbusobject
+ * @short_description: Base type for D-Bus objects
+ * @include: gio/gio.h
+ *
+ * The #GDBusObject type is the base type for D-Bus objects on both
+ * the service side (see #GDBusObjectStub) and the client side (see
+ * #GDBusObjectProxy). It is essentially just a container of
+ * interfaces.
+ */
+
+typedef GDBusObjectIface GDBusObjectInterface;
+G_DEFINE_INTERFACE (GDBusObject, g_dbus_object, G_TYPE_OBJECT)
+
+static void
+g_dbus_object_default_init (GDBusObjectIface *iface)
+{
+  /**
+   * GDBusObject::interface-added:
+   * @object: The #GDBusObject emitting the signal.
+   * @interface: The #GDBusInterface that was added.
+   *
+   * Emitted when @interface is added to @object.
+   */
+  g_signal_new ("interface-added",
+                G_TYPE_FROM_INTERFACE (iface),
+                G_SIGNAL_RUN_LAST,
+                G_STRUCT_OFFSET (GDBusObjectIface, interface_added),
+                NULL,
+                NULL,
+                g_cclosure_marshal_VOID__OBJECT,
+                G_TYPE_NONE,
+                1,
+                G_TYPE_DBUS_INTERFACE);
+
+  /**
+   * GDBusObject::interface-removed:
+   * @object: The #GDBusObject emitting the signal.
+   * @interface: The #GDBusInterface that was removed.
+   *
+   * Emitted when @interface is removed from @object.
+   */
+  g_signal_new ("interface-removed",
+                G_TYPE_FROM_INTERFACE (iface),
+                G_SIGNAL_RUN_LAST,
+                G_STRUCT_OFFSET (GDBusObjectIface, interface_removed),
+                NULL,
+                NULL,
+                g_cclosure_marshal_VOID__OBJECT,
+                G_TYPE_NONE,
+                1,
+                G_TYPE_DBUS_INTERFACE);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/**
+ * g_dbus_object_get_object_path:
+ * @object: A #GDBusObject.
+ *
+ * Gets the object path for @object.
+ *
+ * Returns: A string owned by @object. Do not free.
+ */
+const gchar *
+g_dbus_object_get_object_path (GDBusObject *object)
+{
+  GDBusObjectIface *iface = G_DBUS_OBJECT_GET_IFACE (object);
+  return iface->get_object_path (object);
+}
+
+/**
+ * g_dbus_object_get_interfaces:
+ * @object: A #GDBusObject.
+ *
+ * Gets the D-Bus interfaces associated with @object.
+ *
+ * Returns: (element-type GDBusInterface) (transfer full) : A list of #GDBusInterface instances.
+ *   The returned list must be freed by g_list_free() after each element has been freed
+ *   with g_object_unref().
+ */
+GList *
+g_dbus_object_get_interfaces (GDBusObject *object)
+{
+  GDBusObjectIface *iface = G_DBUS_OBJECT_GET_IFACE (object);
+  return iface->get_interfaces (object);
+}
+
+/**
+ * g_dbus_object_get_interface:
+ * @object: A #GDBusObject.
+ * @interface_name: A D-Bus interface name.
+ *
+ * Gets the D-Bus interface with name @interface_name associated with
+ * @object, if any.
+ *
+ * Returns: %NULL if not found, otherwise a #GDBusInterface that must
+ *   be freed with g_object_unref().
+ */
+GDBusInterface *
+g_dbus_object_get_interface (GDBusObject *object,
+                             const gchar *interface_name)
+{
+  GDBusObjectIface *iface = G_DBUS_OBJECT_GET_IFACE (object);
+  g_return_val_if_fail (g_dbus_is_interface_name (interface_name), NULL);
+  return iface->get_interface (object, interface_name);
+}
+
+
+/**
+ * g_dbus_object_peek_with_typecheck:
+ * @object: A #GDBusObject.
+ * @interface_name: A D-Bus interface name.
+ * @type: The #GType that the returned object must conform to.
+ *
+ * Like g_dbus_object_lookup_with_typecheck() except that the caller
+ * does not own a reference to the returned object.
+ *
+ * <note><para>This function is intended to only be used in type
+ * implementations.</para></note>
+ *
+ * Returns: A #GDBusInterface implementing @type or %NULL if
+ * not found. Do not free the returned object, it is owned by
+ * @object.
+ */
+gpointer
+g_dbus_object_peek_with_typecheck (GDBusObject *object,
+                                   const gchar *interface_name,
+                                   GType        type)
+{
+  GDBusObjectIface *iface = G_DBUS_OBJECT_GET_IFACE (object);
+  g_return_val_if_fail (g_dbus_is_interface_name (interface_name), NULL);
+  return iface->peek_with_typecheck (object, interface_name, type);
+}
+
+/**
+ * g_dbus_object_lookup_with_typecheck:
+ * @object: A #GDBusObject.
+ * @interface_name: A D-Bus interface name.
+ * @type: The #GType that the returned object must conform to.
+ *
+ * Like g_dbus_object_get_interface() but warns on stderr if the
+ * returned object, if any, does not conform to @type.
+ *
+ * <note><para>This function is intended to only be used in type
+ * implementations.</para></note>
+ *
+ * Returns: A #GDBusInterface implementing @type or %NULL if
+ * not found. Free with g_object_unref().
+ */
+gpointer
+g_dbus_object_lookup_with_typecheck (GDBusObject *object,
+                                     const gchar *interface_name,
+                                     GType        type)
+{
+  GDBusObjectIface *iface = G_DBUS_OBJECT_GET_IFACE (object);
+  g_return_val_if_fail (g_dbus_is_interface_name (interface_name), NULL);
+  return iface->lookup_with_typecheck (object, interface_name, type);
+}
+
diff --git a/gio/gdbusobject.h b/gio/gdbusobject.h
new file mode 100644
index 0000000..e941ddd
--- /dev/null
+++ b/gio/gdbusobject.h
@@ -0,0 +1,95 @@
+/* GDBus - GLib D-Bus Library
+ *
+ * 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
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#ifndef __G_DBUS_OBJECT_H__
+#define __G_DBUS_OBJECT_H__
+
+#include <gio/giotypes.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_DBUS_OBJECT         (g_dbus_object_get_type())
+#define G_DBUS_OBJECT(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_OBJECT, GDBusObject))
+#define G_IS_DBUS_OBJECT(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_OBJECT))
+#define G_DBUS_OBJECT_GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE((o), G_TYPE_DBUS_OBJECT, GDBusObjectIface))
+
+typedef struct _GDBusObjectIface GDBusObjectIface;
+
+/**
+ * GDBusObjectIface:
+ * @parent_iface: The parent interface.
+ * @get_object_path: Returns the object path. See g_dbus_object_get_object_path().
+ * @get_interfaces: Returns all interfaces. See g_dbus_object_get_interfaces().
+ * @get_interface: Returns an interface by name. See g_dbus_object_get_interface().
+ * @lookup_with_typecheck: Like @get_interface but warns on stderr if the returned object, if any,
+ *   does not conform to @type. Returned object must be freed by the caller.
+ * @peek_with_typecheck: Like @lookup_with_typecheck but does not transfer the reference.
+ * @interface_added: Signal handler for the #GDBusObject::interface-added signal.
+ * @interface_removed: Signal handler for the #GDBusObject::interface-removed signal.
+ *
+ * Base object type for D-Bus objects.
+ *
+ * <note><para>The @lookup_with_typecheck and @peek_with_typecheck
+ * virtual functions should only be used by D-Bus interface
+ * implementations.</para></note>
+ */
+struct _GDBusObjectIface
+{
+  GTypeInterface parent_iface;
+
+  /* Virtual Functions */
+  const gchar     *(*get_object_path) (GDBusObject  *object);
+  GList           *(*get_interfaces)  (GDBusObject  *object);
+  GDBusInterface  *(*get_interface)   (GDBusObject  *object,
+                                       const gchar  *interface_name);
+
+  gpointer         (*lookup_with_typecheck) (GDBusObject *object,
+                                             const gchar *interface_name,
+                                             GType        type);
+  gpointer         (*peek_with_typecheck)   (GDBusObject *object,
+                                             const gchar *interface_name,
+                                             GType        type);
+
+  /* Signals */
+  void (*interface_added)   (GDBusObject     *object,
+                             GDBusInterface  *interface);
+  void (*interface_removed) (GDBusObject     *object,
+                             GDBusInterface  *interface);
+
+};
+
+GType            g_dbus_object_get_type        (void) G_GNUC_CONST;
+const gchar     *g_dbus_object_get_object_path (GDBusObject  *object);
+GList           *g_dbus_object_get_interfaces  (GDBusObject  *object);
+GDBusInterface  *g_dbus_object_get_interface   (GDBusObject  *object,
+                                                const gchar  *interface_name);
+
+gpointer         g_dbus_object_peek_with_typecheck   (GDBusObject *object,
+                                                      const gchar *interface_name,
+                                                      GType        type);
+gpointer         g_dbus_object_lookup_with_typecheck (GDBusObject *object,
+                                                      const gchar *interface_name,
+                                                      GType        type);
+
+G_END_DECLS
+
+#endif /* __G_DBUS_OBJECT_H__ */
diff --git a/gio/gdbusobjectmanager.c b/gio/gdbusobjectmanager.c
new file mode 100644
index 0000000..6b3b291
--- /dev/null
+++ b/gio/gdbusobjectmanager.c
@@ -0,0 +1,216 @@
+/* GDBus - GLib D-Bus Library
+ *
+ * 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
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#include "config.h"
+
+#include "gdbusobject.h"
+#include "gdbusobjectmanager.h"
+#include "gio-marshal.h"
+#include "gdbusinterface.h"
+#include "gdbusutils.h"
+
+#include "glibintl.h"
+
+/**
+ * SECTION:gdbusobjectmanager
+ * @short_description: Base type for D-Bus object managers
+ * @include: gio/gio.h
+ *
+ * The #GDBusObjectManager type is the base type for service- and
+ * client-side implementations of the standardized <ulink
+ * url="http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager";>org.freedesktop.DBus.ObjectManager</ulink>
+ * interface.
+ *
+ * See #GDBusObjectManagerClient for the client-side implementation
+ * and #GDBusObjectManagerServer for the service-side implementation.
+ */
+
+/**
+ * GDBusObjectManager:
+ *
+ * A D-Bus object.
+ */
+
+typedef GDBusObjectManagerIface GDBusObjectManagerInterface;
+G_DEFINE_INTERFACE (GDBusObjectManager, g_dbus_object_manager, G_TYPE_OBJECT)
+
+static void
+g_dbus_object_manager_default_init (GDBusObjectManagerIface *iface)
+{
+  /**
+   * GDBusObjectManager::object-added:
+   * @manager: The #GDBusObjectManager emitting the signal.
+   * @object: The #GDBusObject that was added.
+   *
+   * Emitted when @object is added to @manager.
+   */
+  g_signal_new ("object-added",
+                G_TYPE_FROM_INTERFACE (iface),
+                G_SIGNAL_RUN_LAST,
+                G_STRUCT_OFFSET (GDBusObjectManagerIface, object_added),
+                NULL,
+                NULL,
+                g_cclosure_marshal_VOID__OBJECT,
+                G_TYPE_NONE,
+                1,
+                G_TYPE_DBUS_OBJECT);
+
+  /**
+   * GDBusObjectManager::object-removed:
+   * @manager: The #GDBusObjectManager emitting the signal.
+   * @object: The #GDBusObject that was removed.
+   *
+   * Emitted when @object is removed from @manager.
+   */
+  g_signal_new ("object-removed",
+                G_TYPE_FROM_INTERFACE (iface),
+                G_SIGNAL_RUN_LAST,
+                G_STRUCT_OFFSET (GDBusObjectManagerIface, object_removed),
+                NULL,
+                NULL,
+                g_cclosure_marshal_VOID__OBJECT,
+                G_TYPE_NONE,
+                1,
+                G_TYPE_DBUS_OBJECT);
+
+  /**
+   * GDBusObjectManager::interface-added:
+   * @manager: The #GDBusObjectManager emitting the signal.
+   * @object: The #GDBusObject on which an interface was added.
+   * @interface: The #GDBusInterface that was added.
+   *
+   * Emitted when @interface is added to @object.
+   *
+   * This signal exists purely as a convenience to avoid having to
+   * connect signals to all objects managed by @manager.
+   */
+  g_signal_new ("interface-added",
+                G_TYPE_FROM_INTERFACE (iface),
+                G_SIGNAL_RUN_LAST,
+                G_STRUCT_OFFSET (GDBusObjectManagerIface, interface_added),
+                NULL,
+                NULL,
+                _gio_marshal_VOID__OBJECT_OBJECT,
+                G_TYPE_NONE,
+                2,
+                G_TYPE_DBUS_OBJECT,
+                G_TYPE_DBUS_INTERFACE);
+
+  /**
+   * GDBusObjectManager::interface-removed:
+   * @manager: The #GDBusObjectManager emitting the signal.
+   * @object: The #GDBusObject on which an interface was removed.
+   * @interface: The #GDBusInterface that was removed.
+   *
+   * Emitted when @interface has been removed from @object.
+   *
+   * This signal exists purely as a convenience to avoid having to
+   * connect signals to all objects managed by @manager.
+   */
+  g_signal_new ("interface-removed",
+                G_TYPE_FROM_INTERFACE (iface),
+                G_SIGNAL_RUN_LAST,
+                G_STRUCT_OFFSET (GDBusObjectManagerIface, interface_removed),
+                NULL,
+                NULL,
+                _gio_marshal_VOID__OBJECT_OBJECT,
+                G_TYPE_NONE,
+                2,
+                G_TYPE_DBUS_OBJECT,
+                G_TYPE_DBUS_INTERFACE);
+
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/**
+ * g_dbus_object_manager_get_object_path:
+ * @manager: A #GDBusObjectManager.
+ *
+ * Gets the object path that @manager is for.
+ *
+ * Returns: A string owned by @manager. Do not free.
+ */
+const gchar *
+g_dbus_object_manager_get_object_path (GDBusObjectManager *manager)
+{
+  GDBusObjectManagerIface *iface = G_DBUS_OBJECT_MANAGER_GET_IFACE (manager);
+  return iface->get_object_path (manager);
+}
+
+/**
+ * g_dbus_object_manager_get_objects:
+ * @manager: A #GDBusObjectManager.
+ *
+ * Gets all #GDBusObject objects known to @manager.
+ *
+ * Returns: (transfer full) (element-type GDBusObject): A list of
+ *   #GDBusObject objects. The returned list should be freed with
+ *   g_list_free() after each element has been freed with
+ *   g_object_unref().
+ */
+GList *
+g_dbus_object_manager_get_objects (GDBusObjectManager *manager)
+{
+  GDBusObjectManagerIface *iface = G_DBUS_OBJECT_MANAGER_GET_IFACE (manager);
+  return iface->get_objects (manager);
+}
+
+/**
+ * g_dbus_object_manager_get_object:
+ * @manager: A #GDBusObjectManager.
+ * @object_path: Object path to lookup.
+ *
+ * Gets the #GDBusObjectProxy at @object_path, if any.
+ *
+ * Returns: A #GDBusObject or %NULL. Free with g_object_unref().
+ */
+GDBusObject *
+g_dbus_object_manager_get_object (GDBusObjectManager *manager,
+                                  const gchar        *object_path)
+{
+  GDBusObjectManagerIface *iface = G_DBUS_OBJECT_MANAGER_GET_IFACE (manager);
+  g_return_val_if_fail (g_variant_is_object_path (object_path), NULL);
+  return iface->get_object (manager, object_path);
+}
+
+/**
+ * g_dbus_object_manager_get_interface:
+ * @manager: A #GDBusObjectManager.
+ * @object_path: Object path to lookup.
+ * @interface_name: D-Bus interface name to lookup.
+ *
+ * Gets the interface proxy for @interface_name at @object_path, if
+ * any.
+ *
+ * Returns: A #GDBusInterface instance or %NULL. Free with g_object_unref().
+ */
+GDBusInterface *
+g_dbus_object_manager_get_interface (GDBusObjectManager *manager,
+                                     const gchar        *object_path,
+                                     const gchar        *interface_name)
+{
+  GDBusObjectManagerIface *iface = G_DBUS_OBJECT_MANAGER_GET_IFACE (manager);
+  g_return_val_if_fail (g_variant_is_object_path (object_path), NULL);
+  g_return_val_if_fail (g_dbus_is_interface_name (interface_name), NULL);
+  return iface->get_interface (manager, object_path, interface_name);
+}
diff --git a/gio/gdbusobjectmanager.h b/gio/gdbusobjectmanager.h
new file mode 100644
index 0000000..2b82bf4
--- /dev/null
+++ b/gio/gdbusobjectmanager.h
@@ -0,0 +1,89 @@
+/* GDBus - GLib D-Bus Library
+ *
+ * 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
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#ifndef __G_DBUS_OBJECT_MANAGER_H__
+#define __G_DBUS_OBJECT_MANAGER_H__
+
+#include <gio/giotypes.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_DBUS_OBJECT_MANAGER         (g_dbus_object_manager_get_type())
+#define G_DBUS_OBJECT_MANAGER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_OBJECT_MANAGER, GDBusObjectManager))
+#define G_IS_DBUS_OBJECT_MANAGER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_OBJECT_MANAGER))
+#define G_DBUS_OBJECT_MANAGER_GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE((o), G_TYPE_DBUS_OBJECT_MANAGER, GDBusObjectManagerIface))
+
+typedef struct _GDBusObjectManagerIface GDBusObjectManagerIface;
+
+/**
+ * GDBusObjectManagerIface:
+ * @parent_iface: The parent interface.
+ * @get_object_path: Virtual function for g_dbus_object_manager_get_object_path().
+ * @get_objects: Virtual function for g_dbus_object_manager_get_objects().
+ * @get_object: Virtual function for g_dbus_object_manager_get_object().
+ * @get_interface: Virtual function for g_dbus_object_manager_get_interface().
+ * @object_added: Signal handler for the #GDBusObjectManager::object-added signal.
+ * @object_removed: Signal handler for the #GDBusObjectManager::object-removed signal.
+ * @interface_added: Signal handler for the #GDBusObjectManager::interface-added signal.
+ * @interface_removed: Signal handler for the #GDBusObjectManager::interface-removed signal.
+ *
+ * Base type for D-Bus object managers.
+ */
+struct _GDBusObjectManagerIface
+{
+  GTypeInterface parent_iface;
+
+  /* Virtual Functions */
+  const gchar     *(*get_object_path) (GDBusObjectManager    *manager);
+  GList           *(*get_objects)     (GDBusObjectManager    *manager);
+  GDBusObject     *(*get_object)      (GDBusObjectManager    *manager,
+                                       const gchar           *object_path);
+  GDBusInterface  *(*get_interface)   (GDBusObjectManager    *manager,
+                                       const gchar           *object_path,
+                                       const gchar           *interface_name);
+
+  /* Signals */
+  void    (*object_added)                 (GDBusObjectManager   *manager,
+                                           GDBusObject          *object);
+  void    (*object_removed)               (GDBusObjectManager   *manager,
+                                           GDBusObject          *object);
+
+  void    (*interface_added)              (GDBusObjectManager   *manager,
+                                           GDBusObject          *object,
+                                           GDBusInterface       *interface);
+  void    (*interface_removed)            (GDBusObjectManager   *manager,
+                                           GDBusObject          *object,
+                                           GDBusInterface       *interface);
+};
+
+GType            g_dbus_object_manager_get_type        (void) G_GNUC_CONST;
+const gchar     *g_dbus_object_manager_get_object_path (GDBusObjectManager    *manager);
+GList           *g_dbus_object_manager_get_objects     (GDBusObjectManager    *manager);
+GDBusObject     *g_dbus_object_manager_get_object      (GDBusObjectManager    *manager,
+                                                        const gchar           *object_path);
+GDBusInterface  *g_dbus_object_manager_get_interface   (GDBusObjectManager    *manager,
+                                                        const gchar           *object_path,
+                                                        const gchar           *interface_name);
+
+G_END_DECLS
+
+#endif /* __G_DBUS_OBJECT_MANAGER_H__ */
diff --git a/gio/gdbusobjectmanagerclient.c b/gio/gdbusobjectmanagerclient.c
new file mode 100644
index 0000000..79eb5cc
--- /dev/null
+++ b/gio/gdbusobjectmanagerclient.c
@@ -0,0 +1,1575 @@
+/* GDBus - GLib D-Bus Library
+ *
+ * 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
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#include "config.h"
+
+#include "gdbusobjectmanager.h"
+#include "gdbusobjectmanagerclient.h"
+#include "gdbusobject.h"
+#include "gdbusprivate.h"
+#include "gio-marshal.h"
+#include "gioenumtypes.h"
+#include "ginitable.h"
+#include "gasyncresult.h"
+#include "gsimpleasyncresult.h"
+#include "gasyncinitable.h"
+#include "gdbusconnection.h"
+#include "gdbusutils.h"
+#include "gdbusobject.h"
+#include "gdbusobjectproxy.h"
+#include "gdbusproxy.h"
+#include "gdbusinterface.h"
+
+#include "glibintl.h"
+
+/**
+ * SECTION:gdbusobjectmanagerclient
+ * @short_description: Client-side object manager
+ * @include: gio/gio.h
+ *
+ * #GDBusObjectManagerClient is used to create, monitor and delete object
+ * proxies for remote objects exported by a #GDBusObjectManagerServer (or any
+ * code implementing the <ulink
+ * url="http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager";>org.freedesktop.DBus.ObjectManager</ulink>
+ * interface).
+ *
+ * Once an instance of this type has been created, you can connect to
+ * the #GDBusObjectManager::object-added and
+ * #GDBusObjectManager::object-removed signals and inspect the
+ * #GDBusObjectProxy objects returned by
+ * g_dbus_object_manager_get_objects().
+ *
+ * If the name for a #GDBusObjectManagerClient is not owned by anyone at
+ * object construction time, the default behavior is to request the
+ * message bus to launch an owner for the name. This behavior can be
+ * disabled using the %G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START
+ * flag. It's also worth noting that this only works if the name of
+ * interest is activatable in the first place. E.g. in some cases it
+ * is not possible to launch an owner for the requested name. In this
+ * case, #GDBusObjectManagerClient object construction still succeeds but
+ * there will be no object proxies
+ * (e.g. g_dbus_object_manager_get_objects() returns the empty list) and
+ * the #GDBusObjectManagerClient:name-owner property is %NULL.
+ *
+ * The owner of the requested name can come and go (for example
+ * consider a system service being restarted) â?? #GDBusObjectManagerClient
+ * handles this case too; simply connect to the #GObject::notify
+ * signal to watch for changes on the #GDBusObjectManagerClient:name-owner
+ * property. When the name owner vanishes, the behavior is that
+ * #GDBusObjectManagerClient:name-owner is set to %NULL (this includes
+ * emission of the #GObject::notify signal) and then
+ * #GDBusObjectManager::object-removed signals are synthesized
+ * for all currently existing object proxies. Since
+ * #GDBusObjectManagerClient:name-owner is %NULL when this happens, you can
+ * use this information to disambiguate a synthesized signal from a
+ * genuine signal caused by object removal on the remote
+ * #GDBusObjectManager. Similarly, when a new name owner appears,
+ * #GDBusObjectManager::object-added signals are synthesized
+ * while #GDBusObjectManagerClient:name-owner is still %NULL. Only when all
+ * object proxies have been added, the #GDBusObjectManagerClient:name-owner
+ * is set to the new name owner (this includes emission of the
+ * #GObject::notify signal).  Furthermore, you are guaranteed that
+ * #GDBusObjectManagerClient:name-owner will alternate between a name owner
+ * (e.g. <literal>:1.42</literal>) and %NULL even in the case where
+ * the name of interest is atomically replaced
+ *
+ * Ultimately, #GDBusObjectManagerClient is used to obtain #GDBusProxy
+ * instances. All signals (including the
+ * <literal>org.freedesktop.DBus.Properties::PropertiesChanged</literal>
+ * signal) delivered to #GDBusProxy instances are guaranteed to
+ * originate from the name owner. This guarantee along with the
+ * behavior described above, means that certain race conditions
+ * including the <emphasis><quote>half the proxy is from the old owner
+ * and the other half is from the new owner</quote></emphasis> problem
+ * cannot happen.
+ *
+ * To avoid having the application connect to signals on the returned
+ * #GDBusObjectProxy and #GDBusProxy objects, the
+ * #GDBusObject::interface-added,
+ * #GDBusObject::interface-removed,
+ * #GDBusProxy::g-properties-changed and
+ * #GDBusProxy::g-signal signals
+ * are also emitted on the #GDBusObjectManagerClient instance managing these
+ * objects. The signals emitted are
+ * #GDBusObjectManager::interface-added,
+ * #GDBusObjectManager::interface-removed,
+ * #GDBusObjectManagerClient::interface-proxy-properties-changed and
+ * #GDBusObjectManagerClient::interface-proxy-signal.
+ *
+ * Note that all callbacks and signals are emitted in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * that the #GDBusObjectManagerClient object was constructed
+ * in. Additionally, the #GDBusObjectProxy and #GDBusProxy objects
+ * originating from the #GDBusObjectManagerClient object will be created in
+ * the same context and, consequently, will deliver signals in the
+ * same main loop.
+ */
+
+struct _GDBusObjectManagerClientPrivate
+{
+  GBusType bus_type;
+  GDBusConnection *connection;
+  gchar *object_path;
+  gchar *name;
+  gchar *name_owner;
+  GDBusObjectManagerClientFlags flags;
+
+  GDBusProxy *control_proxy;
+
+  GHashTable *map_object_path_to_object_proxy;
+
+  guint signal_subscription_id;
+  gchar *match_rule;
+
+  GDBusProxyTypeFunc get_proxy_type_func;
+  gpointer get_proxy_type_user_data;
+};
+
+enum
+{
+  PROP_0,
+  PROP_BUS_TYPE,
+  PROP_CONNECTION,
+  PROP_FLAGS,
+  PROP_OBJECT_PATH,
+  PROP_NAME,
+  PROP_NAME_OWNER,
+  PROP_GET_PROXY_TYPE_FUNC,
+  PROP_GET_PROXY_TYPE_USER_DATA
+};
+
+enum
+{
+  INTERFACE_PROXY_SIGNAL_SIGNAL,
+  INTERFACE_PROXY_PROPERTIES_CHANGED_SIGNAL,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void initable_iface_init       (GInitableIface *initable_iface);
+static void async_initable_iface_init (GAsyncInitableIface *async_initable_iface);
+static void dbus_object_manager_interface_init (GDBusObjectManagerIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GDBusObjectManagerClient, g_dbus_object_manager_client, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)
+                         G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init)
+                         G_IMPLEMENT_INTERFACE (G_TYPE_DBUS_OBJECT_MANAGER, dbus_object_manager_interface_init));
+
+static void maybe_unsubscribe_signals (GDBusObjectManagerClient *manager);
+
+static void on_control_proxy_g_signal (GDBusProxy   *proxy,
+                                       const gchar  *sender_name,
+                                       const gchar  *signal_name,
+                                       GVariant     *parameters,
+                                       gpointer      user_data);
+
+static void process_get_all_result (GDBusObjectManagerClient *manager,
+                                    GVariant          *value,
+                                    const gchar       *name_owner);
+
+static void
+g_dbus_object_manager_client_finalize (GObject *object)
+{
+  GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (object);
+
+  maybe_unsubscribe_signals (manager);
+
+  g_hash_table_unref (manager->priv->map_object_path_to_object_proxy);
+
+  if (manager->priv->control_proxy != NULL)
+    {
+      g_warn_if_fail (g_signal_handlers_disconnect_by_func (manager->priv->control_proxy,
+                                                            on_control_proxy_g_signal,
+                                                            manager) == 1);
+      g_object_unref (manager->priv->control_proxy);
+    }
+  g_object_unref (manager->priv->connection);
+  g_free (manager->priv->object_path);
+  g_free (manager->priv->name);
+  g_free (manager->priv->name_owner);
+
+  if (G_OBJECT_CLASS (g_dbus_object_manager_client_parent_class)->finalize != NULL)
+    G_OBJECT_CLASS (g_dbus_object_manager_client_parent_class)->finalize (object);
+}
+
+static void
+g_dbus_object_manager_client_get_property (GObject    *_object,
+                                    guint       prop_id,
+                                    GValue     *value,
+                                    GParamSpec *pspec)
+{
+  GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (_object);
+
+  switch (prop_id)
+    {
+    case PROP_CONNECTION:
+      g_value_set_object (value, g_dbus_object_manager_client_get_connection (manager));
+      break;
+
+    case PROP_OBJECT_PATH:
+      g_value_set_string (value, g_dbus_object_manager_get_object_path (G_DBUS_OBJECT_MANAGER (manager)));
+      break;
+
+    case PROP_NAME:
+      g_value_set_string (value, g_dbus_object_manager_client_get_name (manager));
+      break;
+
+    case PROP_FLAGS:
+      g_value_set_flags (value, g_dbus_object_manager_client_get_flags (manager));
+      break;
+
+    case PROP_NAME_OWNER:
+      g_value_take_string (value, g_dbus_object_manager_client_get_name_owner (manager));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (_object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+g_dbus_object_manager_client_set_property (GObject       *_object,
+                                    guint          prop_id,
+                                    const GValue  *value,
+                                    GParamSpec    *pspec)
+{
+  GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (_object);
+
+  switch (prop_id)
+    {
+    case PROP_BUS_TYPE:
+      manager->priv->bus_type = g_value_get_enum (value);
+      break;
+
+    case PROP_CONNECTION:
+      if (g_value_get_object (value) != NULL)
+        {
+          g_assert (manager->priv->connection == NULL);
+          g_assert (G_IS_DBUS_CONNECTION (g_value_get_object (value)));
+          manager->priv->connection = g_value_dup_object (value);
+        }
+      break;
+
+    case PROP_OBJECT_PATH:
+      g_assert (manager->priv->object_path == NULL);
+      g_assert (g_variant_is_object_path (g_value_get_string (value)));
+      manager->priv->object_path = g_value_dup_string (value);
+      break;
+
+    case PROP_NAME:
+      g_assert (manager->priv->name == NULL);
+      g_assert (g_dbus_is_name (g_value_get_string (value)));
+      manager->priv->name = g_value_dup_string (value);
+      break;
+
+    case PROP_FLAGS:
+      manager->priv->flags = g_value_get_flags (value);
+      break;
+
+    case PROP_GET_PROXY_TYPE_FUNC:
+      manager->priv->get_proxy_type_func = g_value_get_pointer (value);
+      break;
+
+    case PROP_GET_PROXY_TYPE_USER_DATA:
+      manager->priv->get_proxy_type_user_data = g_value_get_pointer (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (_object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+g_dbus_object_manager_client_class_init (GDBusObjectManagerClientClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->finalize     = g_dbus_object_manager_client_finalize;
+  gobject_class->set_property = g_dbus_object_manager_client_set_property;
+  gobject_class->get_property = g_dbus_object_manager_client_get_property;
+
+  /**
+   * GDBusObjectManagerClient:connection:
+   *
+   * The #GDBusConnection to use.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_CONNECTION,
+                                   g_param_spec_object ("connection",
+                                                        "Connection",
+                                                        "The connection to use",
+                                                        G_TYPE_DBUS_CONNECTION,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_WRITABLE |
+                                                        G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GDBusObjectManagerClient:bus-type:
+   *
+   * If this property is not %G_BUS_TYPE_NONE, then
+   * #GDBusObjectManagerClient:connection must be %NULL and will be set to the
+   * #GDBusConnection obtained by calling g_bus_get() with the value
+   * of this property.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_BUS_TYPE,
+                                   g_param_spec_enum ("bus-type",
+                                                      "Bus Type",
+                                                      "The bus to connect to, if any",
+                                                      G_TYPE_BUS_TYPE,
+                                                      G_BUS_TYPE_NONE,
+                                                      G_PARAM_WRITABLE |
+                                                      G_PARAM_CONSTRUCT_ONLY |
+                                                      G_PARAM_STATIC_NAME |
+                                                      G_PARAM_STATIC_BLURB |
+                                                      G_PARAM_STATIC_NICK));
+
+  /**
+   * GDBusObjectManagerClient:flags:
+   *
+   * Flags from the #GDBusObjectManagerClientFlags enumeration.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_FLAGS,
+                                   g_param_spec_flags ("flags",
+                                                       "Flags",
+                                                       "Flags for the proxy manager",
+                                                       G_TYPE_DBUS_OBJECT_MANAGER_CLIENT_FLAGS,
+                                                       G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
+                                                       G_PARAM_READABLE |
+                                                       G_PARAM_WRITABLE |
+                                                       G_PARAM_CONSTRUCT_ONLY |
+                                                       G_PARAM_STATIC_NAME |
+                                                       G_PARAM_STATIC_BLURB |
+                                                       G_PARAM_STATIC_NICK));
+
+  /**
+   * GDBusObjectManagerClient:object-path:
+   *
+   * The object path the manager is for.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_OBJECT_PATH,
+                                   g_param_spec_string ("object-path",
+                                                        "Object Path",
+                                                        "The object path of the control object",
+                                                        NULL,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_WRITABLE |
+                                                        G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GDBusObjectManagerClient:name:
+   *
+   * The well-known name or unique name that the manager is for.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_NAME,
+                                   g_param_spec_string ("name",
+                                                        "Name",
+                                                        "Name that the manager is for",
+                                                        NULL,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_WRITABLE |
+                                                        G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GDBusObjectManagerClient:name-owner:
+   *
+   * The unique name that owns #GDBusObjectManagerClient:name or %NULL if
+   * no-one is currently owning the name. Connect to the
+   * #GObject::notify signal to track changes to this property.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_NAME_OWNER,
+                                   g_param_spec_string ("name-owner",
+                                                        "Name Owner",
+                                                        "The owner of the name we are watching",
+                                                        NULL,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GDBusObjectManagerClient:get-proxy-type-func:
+   *
+   * The #GDBusProxyTypeFunc to use when determining what #GType to
+   * use for interface proxies or %NULL.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_GET_PROXY_TYPE_FUNC,
+                                   g_param_spec_pointer ("get-proxy-type-func",
+                                                         "GDBusProxyTypeFunc Function Pointer",
+                                                         "The GDBusProxyTypeFunc pointer to use",
+                                                         G_PARAM_READABLE |
+                                                         G_PARAM_WRITABLE |
+                                                         G_PARAM_CONSTRUCT_ONLY |
+                                                         G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GDBusObjectManagerClient:get-proxy-type-user-data:
+   *
+   * The #gpointer user_data to pass to #GDBusObjectManagerClient:get-proxy-type-func.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_GET_PROXY_TYPE_USER_DATA,
+                                   g_param_spec_pointer ("get-proxy-type-user-data",
+                                                         "GDBusProxyTypeFunc User Data",
+                                                         "The GDBusProxyTypeFunc user_data",
+                                                         G_PARAM_READABLE |
+                                                         G_PARAM_WRITABLE |
+                                                         G_PARAM_CONSTRUCT_ONLY |
+                                                         G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GDBusObjectManagerClient::interface-proxy-signal:
+   * @manager: The #GDBusObjectManagerClient emitting the signal.
+   * @object_proxy: The #GDBusObjectProxy on which an interface is emitting a D-Bus signal.
+   * @interface_proxy: The #GDBusProxy that is emitting a D-Bus signal.
+   * @sender_name: The sender of the signal or NULL if the connection is not a bus connection.
+   * @signal_name: The signal name.
+   * @parameters: A #GVariant tuple with parameters for the signal.
+   *
+   * Emitted when a D-Bus signal is received on @interface_proxy.
+   *
+   * This signal exists purely as a convenience to avoid having to
+   * connect signals to all interface proxies managed by @manager.
+   *
+   * This signal is emitted in the
+   * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+   * that @manager was constructed in.
+   */
+  signals[INTERFACE_PROXY_SIGNAL_SIGNAL] =
+    g_signal_new ("interface-proxy-signal",
+                  G_TYPE_DBUS_OBJECT_MANAGER_CLIENT,
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (GDBusObjectManagerClientClass, interface_proxy_signal),
+                  NULL,
+                  NULL,
+                  _gio_marshal_VOID__OBJECT_OBJECT_STRING_STRING_VARIANT,
+                  G_TYPE_NONE,
+                  5,
+                  G_TYPE_DBUS_OBJECT_PROXY,
+                  G_TYPE_DBUS_PROXY,
+                  G_TYPE_STRING,
+                  G_TYPE_STRING,
+                  G_TYPE_VARIANT);
+
+  /**
+   * GDBusObjectManagerClient::interface-proxy-properties-changed:
+   * @manager: The #GDBusObjectManagerClient emitting the signal.
+   * @object_proxy: The #GDBusObjectProxy on which an interface has properties that are changing.
+   * @interface_proxy: The #GDBusProxy that has properties that are changing.
+   * @changed_properties: A #GVariant containing the properties that changed.
+   * @invalidated_properties: A %NULL terminated array of properties that was invalidated.
+   *
+   * Emitted when one or more D-Bus properties on proxy changes. The
+   * local cache has already been updated when this signal fires. Note
+   * that both @changed_properties and @invalidated_properties are
+   * guaranteed to never be %NULL (either may be empty though).
+   *
+   * This signal exists purely as a convenience to avoid having to
+   * connect signals to all interface proxies managed by @manager.
+   *
+   * This signal is emitted in the
+   * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+   * that @manager was constructed in.
+   */
+  signals[INTERFACE_PROXY_PROPERTIES_CHANGED_SIGNAL] =
+    g_signal_new ("interface-proxy-properties-changed",
+                  G_TYPE_DBUS_OBJECT_MANAGER_CLIENT,
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (GDBusObjectManagerClientClass, interface_proxy_properties_changed),
+                  NULL,
+                  NULL,
+                  _gio_marshal_VOID__OBJECT_OBJECT_VARIANT_BOXED,
+                  G_TYPE_NONE,
+                  4,
+                  G_TYPE_DBUS_OBJECT_PROXY,
+                  G_TYPE_DBUS_PROXY,
+                  G_TYPE_VARIANT,
+                  G_TYPE_STRV);
+
+  g_type_class_add_private (klass, sizeof (GDBusObjectManagerClientPrivate));
+}
+
+static void
+g_dbus_object_manager_client_init (GDBusObjectManagerClient *manager)
+{
+  manager->priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
+                                               G_TYPE_DBUS_OBJECT_MANAGER_CLIENT,
+                                               GDBusObjectManagerClientPrivate);
+  manager->priv->map_object_path_to_object_proxy = g_hash_table_new_full (g_str_hash,
+                                                                          g_str_equal,
+                                                                          g_free,
+                                                                          (GDestroyNotify) g_object_unref);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/**
+ * g_dbus_object_manager_client_new_sync:
+ * @connection: A #GDBusConnection.
+ * @flags: Zero or more flags from the #GDBusObjectManagerClientFlags enumeration.
+ * @name: The owner of the control object (unique or well-known name).
+ * @object_path: The object path of the control object.
+ * @get_proxy_type_func: A #GDBusProxyTypeFunc function or %NULL to always construct #GDBusProxy proxies.
+ * @get_proxy_type_user_data: User data to pass to @get_proxy_type_func.
+ * @cancellable: A #GCancellable or %NULL
+ * @error: Return location for error or %NULL.
+ *
+ * Creates a new #GDBusObjectManagerClient object.
+ *
+ * This is a synchronous failable constructor - the calling thread is
+ * blocked until a reply is received. See g_dbus_object_manager_client_new()
+ * for the asynchronous version.
+ *
+ * Returns: A #GDBusObjectManagerClient object or %NULL if @error is
+ * set. Free with g_object_unref().
+ */
+GDBusObjectManager *
+g_dbus_object_manager_client_new_sync (GDBusConnection               *connection,
+                                       GDBusObjectManagerClientFlags  flags,
+                                       const gchar                   *name,
+                                       const gchar                   *object_path,
+                                       GDBusProxyTypeFunc             get_proxy_type_func,
+                                       gpointer                       get_proxy_type_user_data,
+                                       GCancellable                  *cancellable,
+                                       GError                       **error)
+{
+  GInitable *initable;
+
+  g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
+  g_return_val_if_fail ((name == NULL && g_dbus_connection_get_unique_name (connection) == NULL) ||
+                        g_dbus_is_name (name), NULL);
+  g_return_val_if_fail (g_variant_is_object_path (object_path), NULL);
+  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+  initable = g_initable_new (G_TYPE_DBUS_OBJECT_MANAGER_CLIENT,
+                             cancellable,
+                             error,
+                             "connection", connection,
+                             "flags", flags,
+                             "name", name,
+                             "object-path", object_path,
+                             "get-proxy-type-func", get_proxy_type_func,
+                             "get-proxy-type-user-data", get_proxy_type_user_data,
+                             NULL);
+  if (initable != NULL)
+    return G_DBUS_OBJECT_MANAGER (initable);
+  else
+    return NULL;
+}
+
+/**
+ * g_dbus_object_manager_client_new:
+ * @connection: A #GDBusConnection.
+ * @flags: Zero or more flags from the #GDBusObjectManagerClientFlags enumeration.
+ * @name: The owner of the control object (unique or well-known name).
+ * @object_path: The object path of the control object.
+ * @get_proxy_type_func: A #GDBusProxyTypeFunc function or %NULL to always construct #GDBusProxy proxies.
+ * @get_proxy_type_user_data: User data to pass to @get_proxy_type_func.
+ * @cancellable: A #GCancellable or %NULL
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied.
+ * @user_data: The data to pass to @callback.
+ *
+ * Asynchronously creates a new #GDBusObjectManagerClient object.
+ *
+ * This is an asynchronous failable constructor. When the result is
+ * ready, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can
+ * then call g_dbus_object_manager_client_new_finish() to get the result. See
+ * g_dbus_object_manager_client_new_sync() for the synchronous version.
+ */
+void
+g_dbus_object_manager_client_new (GDBusConnection               *connection,
+                                  GDBusObjectManagerClientFlags  flags,
+                                  const gchar                   *name,
+                                  const gchar                   *object_path,
+                                  GDBusProxyTypeFunc             get_proxy_type_func,
+                                  gpointer                       get_proxy_type_user_data,
+                                  GCancellable                  *cancellable,
+                                  GAsyncReadyCallback            callback,
+                                  gpointer                       user_data)
+{
+  g_return_if_fail (G_IS_DBUS_CONNECTION (connection));
+  g_return_if_fail ((name == NULL && g_dbus_connection_get_unique_name (connection) == NULL) ||
+                        g_dbus_is_name (name));
+  g_return_if_fail (g_variant_is_object_path (object_path));
+
+  g_async_initable_new_async (G_TYPE_DBUS_OBJECT_MANAGER_CLIENT,
+                              G_PRIORITY_DEFAULT,
+                              cancellable,
+                              callback,
+                              user_data,
+                              "connection", connection,
+                              "flags", flags,
+                              "name", name,
+                              "object-path", object_path,
+                              "get-proxy-type-func", get_proxy_type_func,
+                              "get-proxy-type-user-data", get_proxy_type_user_data,
+                              NULL);
+}
+
+/**
+ * g_dbus_object_manager_client_new_finish:
+ * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_dbus_object_manager_client_new().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with g_dbus_object_manager_client_new().
+ *
+ * Returns: A #GDBusObjectManagerClient object or %NULL if @error is
+ * set. Free with g_object_unref().
+ */
+GDBusObjectManager *
+g_dbus_object_manager_client_new_finish (GAsyncResult   *res,
+                                 GError        **error)
+{
+  GObject *object;
+  GObject *source_object;
+
+  source_object = g_async_result_get_source_object (res);
+  g_assert (source_object != NULL);
+
+  object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object),
+                                        res,
+                                        error);
+  g_object_unref (source_object);
+
+  if (object != NULL)
+    return G_DBUS_OBJECT_MANAGER (object);
+  else
+    return NULL;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/**
+ * g_dbus_object_manager_client_new_for_bus_sync:
+ * @bus_type: A #GBusType.
+ * @flags: Zero or more flags from the #GDBusObjectManagerClientFlags enumeration.
+ * @name: The owner of the control object (unique or well-known name).
+ * @object_path: The object path of the control object.
+ * @get_proxy_type_func: A #GDBusProxyTypeFunc function or %NULL to always construct #GDBusProxy proxies.
+ * @get_proxy_type_user_data: User data to pass to @get_proxy_type_func.
+ * @cancellable: A #GCancellable or %NULL
+ * @error: Return location for error or %NULL.
+ *
+ * Like g_dbus_object_manager_client_new_sync() but takes a #GBusType instead
+ * of a #GDBusConnection.
+ *
+ * This is a synchronous failable constructor - the calling thread is
+ * blocked until a reply is received. See g_dbus_object_manager_client_new_for_bus()
+ * for the asynchronous version.
+ *
+ * Returns: A #GDBusObjectManagerClient object or %NULL if @error is
+ * set. Free with g_object_unref().
+ */
+GDBusObjectManager *
+g_dbus_object_manager_client_new_for_bus_sync (GBusType                       bus_type,
+                                               GDBusObjectManagerClientFlags  flags,
+                                               const gchar                   *name,
+                                               const gchar                   *object_path,
+                                               GDBusProxyTypeFunc             get_proxy_type_func,
+                                               gpointer                       get_proxy_type_user_data,
+                                               GCancellable                  *cancellable,
+                                               GError                       **error)
+{
+  GInitable *initable;
+
+  g_return_val_if_fail (bus_type != G_BUS_TYPE_NONE, NULL);
+  g_return_val_if_fail (g_dbus_is_name (name), NULL);
+  g_return_val_if_fail (g_variant_is_object_path (object_path), NULL);
+  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+  initable = g_initable_new (G_TYPE_DBUS_OBJECT_MANAGER_CLIENT,
+                             cancellable,
+                             error,
+                             "bus-type", bus_type,
+                             "flags", flags,
+                             "name", name,
+                             "object-path", object_path,
+                             "get-proxy-type-func", get_proxy_type_func,
+                             "get-proxy-type-user-data", get_proxy_type_user_data,
+                             NULL);
+  if (initable != NULL)
+    return G_DBUS_OBJECT_MANAGER (initable);
+  else
+    return NULL;
+}
+
+/**
+ * g_dbus_object_manager_client_new_for_bus:
+ * @bus_type: A #GBusType.
+ * @flags: Zero or more flags from the #GDBusObjectManagerClientFlags enumeration.
+ * @name: The owner of the control object (unique or well-known name).
+ * @object_path: The object path of the control object.
+ * @get_proxy_type_func: A #GDBusProxyTypeFunc function or %NULL to always construct #GDBusProxy proxies.
+ * @get_proxy_type_user_data: User data to pass to @get_proxy_type_func.
+ * @cancellable: A #GCancellable or %NULL
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied.
+ * @user_data: The data to pass to @callback.
+ *
+ * Like g_dbus_object_manager_client_new() but takes a #GBusType instead of a
+ * #GDBusConnection.
+ *
+ * This is an asynchronous failable constructor. When the result is
+ * ready, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can
+ * then call g_dbus_object_manager_client_new_for_bus_finish() to get the result. See
+ * g_dbus_object_manager_client_new_for_bus_sync() for the synchronous version.
+ */
+void
+g_dbus_object_manager_client_new_for_bus (GBusType                       bus_type,
+                                          GDBusObjectManagerClientFlags  flags,
+                                          const gchar                   *name,
+                                          const gchar                   *object_path,
+                                          GDBusProxyTypeFunc             get_proxy_type_func,
+                                          gpointer                       get_proxy_type_user_data,
+                                          GCancellable                  *cancellable,
+                                          GAsyncReadyCallback            callback,
+                                          gpointer                       user_data)
+{
+  g_return_if_fail (bus_type != G_BUS_TYPE_NONE);
+  g_return_if_fail (g_dbus_is_name (name));
+  g_return_if_fail (g_variant_is_object_path (object_path));
+
+  g_async_initable_new_async (G_TYPE_DBUS_OBJECT_MANAGER_CLIENT,
+                              G_PRIORITY_DEFAULT,
+                              cancellable,
+                              callback,
+                              user_data,
+                              "bus-type", bus_type,
+                              "flags", flags,
+                              "name", name,
+                              "object-path", object_path,
+                              "get-proxy-type-func", get_proxy_type_func,
+                              "get-proxy-type-user-data", get_proxy_type_user_data,
+                              NULL);
+}
+
+/**
+ * g_dbus_object_manager_client_new_for_bus_finish:
+ * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_dbus_object_manager_client_new_for_bus().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with g_dbus_object_manager_client_new_for_bus().
+ *
+ * Returns: A #GDBusObjectManagerClient object or %NULL if @error is
+ * set. Free with g_object_unref().
+ */
+GDBusObjectManager *
+g_dbus_object_manager_client_new_for_bus_finish (GAsyncResult   *res,
+                                                 GError        **error)
+{
+  GObject *object;
+  GObject *source_object;
+
+  source_object = g_async_result_get_source_object (res);
+  g_assert (source_object != NULL);
+
+  object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object),
+                                        res,
+                                        error);
+  g_object_unref (source_object);
+
+  if (object != NULL)
+    return G_DBUS_OBJECT_MANAGER (object);
+  else
+    return NULL;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/**
+ * g_dbus_object_manager_client_get_connection:
+ * @manager: A #GDBusObjectManagerClient
+ *
+ * Gets the #GDBusConnection used by @manager.
+ *
+ * Returns: A #GDBusConnection object. Do not free, the object belongs
+ * to @manager.
+ */
+GDBusConnection *
+g_dbus_object_manager_client_get_connection (GDBusObjectManagerClient *manager)
+{
+  g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager), NULL);
+  return manager->priv->connection;
+}
+
+/**
+ * g_dbus_object_manager_client_get_name:
+ * @manager: A #GDBusObjectManagerClient
+ *
+ * Gets the name that @manager is for.
+ *
+ * Returns: A unique or well-known name. Do not free, the string
+ * belongs to @manager.
+ */
+const gchar *
+g_dbus_object_manager_client_get_name (GDBusObjectManagerClient *manager)
+{
+  g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager), NULL);
+  return manager->priv->name;
+}
+
+/**
+ * g_dbus_object_manager_client_get_flags:
+ * @manager: A #GDBusObjectManagerClient
+ *
+ * Gets the flags that @manager was constructed with.
+ *
+ * Returns: Zero of more flags from the #GDBusObjectManagerClientFlags
+ * enumeration.
+ */
+GDBusObjectManagerClientFlags
+g_dbus_object_manager_client_get_flags (GDBusObjectManagerClient *manager)
+{
+  g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager), G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE);
+  return manager->priv->flags;
+}
+
+/**
+ * g_dbus_object_manager_client_get_name_owner:
+ * @manager: A #GDBusObjectManagerClient.
+ *
+ * The unique name that owns the name that @manager is for or %NULL if
+ * no-one currently owns that name. You can connect to the
+ * #GObject::notify signal to track changes to the
+ * #GDBusObjectManagerClient:name-owner property.
+ *
+ * Returns: The name owner or %NULL if no name owner exists. Free with
+ * g_free().
+ */
+gchar *
+g_dbus_object_manager_client_get_name_owner (GDBusObjectManagerClient *manager)
+{
+  g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager), NULL);
+  return g_strdup (manager->priv->name_owner);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/* signal handler for all objects we manage - we dispatch signals
+ * from here to the objects
+ */
+static void
+signal_cb (GDBusConnection *connection,
+           const gchar     *sender_name,
+           const gchar     *object_path,
+           const gchar     *interface_name,
+           const gchar     *signal_name,
+           GVariant        *parameters,
+           gpointer         user_data)
+{
+  GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (user_data);
+  GDBusObjectProxy *object_proxy;
+  GDBusInterface *interface;
+
+  object_proxy = g_hash_table_lookup (manager->priv->map_object_path_to_object_proxy, object_path);
+  if (object_proxy == NULL)
+    goto out;
+
+  //g_debug ("yay, signal_cb %s %s: %s\n", signal_name, object_path, g_variant_print (parameters, TRUE));
+
+  if (g_strcmp0 (interface_name, "org.freedesktop.DBus.Properties") == 0)
+    {
+      if (g_strcmp0 (signal_name, "PropertiesChanged") == 0)
+        {
+          const gchar *interface_name;
+          GVariant *changed_properties;
+          const gchar **invalidated_properties;
+
+          g_variant_get (parameters,
+                         "(&s a{sv}^a&s)",
+                         &interface_name,
+                         &changed_properties,
+                         &invalidated_properties);
+
+          interface = g_dbus_object_get_interface (G_DBUS_OBJECT (object_proxy), interface_name);
+          if (interface != NULL)
+            {
+              GVariantIter property_iter;
+              const gchar *property_name;
+              GVariant *property_value;
+              guint n;
+
+              /* update caches... */
+              g_variant_iter_init (&property_iter, changed_properties);
+              while (g_variant_iter_next (&property_iter,
+                                          "{&sv}",
+                                          &property_name,
+                                          &property_value))
+                {
+                  g_dbus_proxy_set_cached_property (G_DBUS_PROXY (interface),
+                                                    property_name,
+                                                    property_value);
+                  g_variant_unref (property_value);
+                }
+
+              for (n = 0; invalidated_properties[n] != NULL; n++)
+                {
+                  g_dbus_proxy_set_cached_property (G_DBUS_PROXY (interface),
+                                                    invalidated_properties[n],
+                                                    NULL);
+                }
+              /* ... and then synthesize the signal */
+              g_signal_emit (manager,
+                             signals[INTERFACE_PROXY_PROPERTIES_CHANGED_SIGNAL],
+                             0,
+                             object_proxy,
+                             interface,
+                             changed_properties,
+                             invalidated_properties);
+              g_signal_emit_by_name (interface,
+                                     "g-properties-changed",
+                                     changed_properties,
+                                     invalidated_properties);
+              g_object_unref (interface);
+            }
+          g_variant_unref (changed_properties);
+          g_free (invalidated_properties);
+        }
+    }
+  else
+    {
+      /* regular signal - just dispatch it */
+      interface = g_dbus_object_get_interface (G_DBUS_OBJECT (object_proxy), interface_name);
+      if (interface != NULL)
+        {
+          g_signal_emit (manager,
+                         signals[INTERFACE_PROXY_SIGNAL_SIGNAL],
+                         0,
+                         object_proxy,
+                         interface,
+                         sender_name,
+                         signal_name,
+                         parameters);
+          g_signal_emit_by_name (interface,
+                                 "g-signal",
+                                 sender_name,
+                                 signal_name,
+                                 parameters);
+          g_object_unref (interface);
+        }
+    }
+
+ out:
+  ;
+}
+
+static void
+subscribe_signals (GDBusObjectManagerClient *manager,
+                   const gchar *name_owner)
+{
+  GError *error;
+  GVariant *ret;
+
+  g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager));
+  g_return_if_fail (manager->priv->signal_subscription_id == 0);
+  g_return_if_fail (g_dbus_is_unique_name (name_owner));
+
+  /* the bus daemon may not implement path_prefix so gracefully
+   * handle this by using a fallback
+   */
+  manager->priv->match_rule = g_strdup_printf ("type='signal',sender='%s',path_namespace='%s'",
+                                               name_owner,
+                                               manager->priv->object_path);
+
+  error = NULL;
+  ret = g_dbus_connection_call_sync (manager->priv->connection,
+                                     "org.freedesktop.DBus",
+                                     "/org/freedeskop/DBus",
+                                     "org.freedesktop.DBus",
+                                     "AddMatch",
+                                     g_variant_new ("(s)",
+                                                    manager->priv->match_rule),
+                                     NULL, /* reply_type */
+                                     G_DBUS_CALL_FLAGS_NONE,
+                                     -1, /* default timeout */
+                                     NULL, /* TODO: Cancellable */
+                                     &error);
+  if (ret != NULL)
+    {
+      /* yay, bus daemon supports path_namespace */
+      g_variant_unref (ret);
+
+      /* still need to ask GDBusConnection for the callbacks */
+      manager->priv->signal_subscription_id =
+        g_dbus_connection_signal_subscribe (manager->priv->connection,
+                                            name_owner,
+                                            NULL, /* interface */
+                                            NULL, /* member */
+                                            NULL, /* path - TODO: really want wilcard support here */
+                                            NULL, /* arg0 */
+                                            G_DBUS_SIGNAL_FLAGS_NONE |
+                                            G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
+                                            signal_cb,
+                                            manager,
+                                            NULL); /* user_data_free_func */
+
+    }
+  else
+    {
+      /* TODO: we could report this to the user
+      g_warning ("Message bus daemon does not support path_namespace: %s (%s %d)",
+                 error->message,
+                 g_quark_to_string (error->domain),
+                 error->code);
+      */
+
+      g_error_free (error);
+
+      /* no need to call RemoveMatch when done since it didn't work */
+      g_free (manager->priv->match_rule);
+      manager->priv->match_rule = NULL;
+
+      /* Fallback is to subscribe to *all* signals from the name owner which
+       * is rather wasteful. It's probably not a big practical problem because
+       * users typically want all objects that the name owner supplies.
+       */
+      manager->priv->signal_subscription_id =
+        g_dbus_connection_signal_subscribe (manager->priv->connection,
+                                            name_owner,
+                                            NULL, /* interface */
+                                            NULL, /* member */
+                                            NULL, /* path - TODO: really want wilcard support here */
+                                            NULL, /* arg0 */
+                                            G_DBUS_SIGNAL_FLAGS_NONE,
+                                            signal_cb,
+                                            manager,
+                                            NULL); /* user_data_free_func */
+    }
+}
+
+static void
+maybe_unsubscribe_signals (GDBusObjectManagerClient *manager)
+{
+  g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager));
+
+  if (manager->priv->signal_subscription_id > 0)
+    {
+      g_dbus_connection_signal_unsubscribe (manager->priv->connection,
+                                            manager->priv->signal_subscription_id);
+      manager->priv->signal_subscription_id = 0;
+    }
+
+  if (manager->priv->match_rule != NULL)
+    {
+      /* Since the AddMatch call succeeded this is guaranteed to not
+       * fail - therefore, don't bother checking the return value
+       */
+      g_dbus_connection_call (manager->priv->connection,
+                              "org.freedesktop.DBus",
+                              "/org/freedeskop/DBus",
+                              "org.freedesktop.DBus",
+                              "RemoveMatch",
+                              g_variant_new ("(s)",
+                                             manager->priv->match_rule),
+                              NULL, /* reply_type */
+                              G_DBUS_CALL_FLAGS_NONE,
+                              -1, /* default timeout */
+                              NULL, /* GCancellable */
+                              NULL, /* GAsyncReadyCallback */
+                              NULL); /* user data */
+      g_free (manager->priv->match_rule);
+      manager->priv->match_rule = NULL;
+    }
+
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+on_notify_g_name_owner (GObject    *object,
+                        GParamSpec *pspec,
+                        gpointer    user_data)
+{
+  GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (user_data);
+  gchar *old_name_owner;
+  gchar *new_name_owner;
+
+  old_name_owner = manager->priv->name_owner;
+  new_name_owner = g_dbus_proxy_get_name_owner (manager->priv->control_proxy);
+  manager->priv->name_owner = NULL;
+
+  if (g_strcmp0 (old_name_owner, new_name_owner) != 0)
+    {
+      GList *l;
+      GList *proxies;
+
+      /* do the :name-owner notify with a NULL name - this way the user knows
+       * the ::object-proxy-removed following is because the name owner went
+       * away
+       */
+      g_object_notify (G_OBJECT (manager), "name-owner");
+
+      /* remote manager changed; nuke all local proxies  */
+      proxies = g_hash_table_get_values (manager->priv->map_object_path_to_object_proxy);
+      g_list_foreach (proxies, (GFunc) g_object_ref, NULL);
+      g_hash_table_remove_all (manager->priv->map_object_path_to_object_proxy);
+      for (l = proxies; l != NULL; l = l->next)
+        {
+          GDBusObjectProxy *object_proxy = G_DBUS_OBJECT_PROXY (l->data);
+          g_signal_emit_by_name (manager, "object-removed", object_proxy);
+        }
+      g_list_foreach (proxies, (GFunc) g_object_unref, NULL);
+      g_list_free (proxies);
+
+      /* nuke local filter */
+      maybe_unsubscribe_signals (manager);
+    }
+
+  if (new_name_owner != NULL)
+    {
+      GError *error;
+      GVariant *value;
+
+      //g_debug ("repopulating for %s", new_name_owner);
+
+      /* TODO: do this async! */
+      subscribe_signals (manager,
+                         new_name_owner);
+      error = NULL;
+      value = g_dbus_proxy_call_sync (manager->priv->control_proxy,
+                                      "GetManagedObjects",
+                                      NULL, /* parameters */
+                                      G_DBUS_CALL_FLAGS_NONE,
+                                      -1,
+                                      NULL,
+                                      &error);
+      if (value == NULL)
+        {
+          maybe_unsubscribe_signals (manager);
+          g_warning ("Error calling GetManagedObjects() when name owner %s for name %s came back: %s",
+                     new_name_owner,
+                     manager->priv->name,
+                     error->message);
+          g_error_free (error);
+        }
+      else
+        {
+          process_get_all_result (manager, value, new_name_owner);
+          g_variant_unref (value);
+        }
+
+      /* do the :name-owner notify *AFTER* emitting ::object-proxy-added signals - this
+       * way the user knows that the signals were emitted because the name owner came back
+       */
+      manager->priv->name_owner = new_name_owner;
+      g_object_notify (G_OBJECT (manager), "name-owner");
+
+    }
+  g_free (old_name_owner);
+}
+
+static gboolean
+initable_init (GInitable     *initable,
+               GCancellable  *cancellable,
+               GError       **error)
+{
+  GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (initable);
+  gboolean ret;
+  GVariant *value;
+  GDBusProxyFlags proxy_flags;
+
+  ret = FALSE;
+
+  if (manager->priv->bus_type != G_BUS_TYPE_NONE)
+    {
+      g_assert (manager->priv->connection == NULL);
+      manager->priv->connection = g_bus_get_sync (manager->priv->bus_type, cancellable, error);
+      if (manager->priv->connection == NULL)
+        goto out;
+    }
+
+  proxy_flags = G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES;
+  if (manager->priv->flags & G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START)
+    proxy_flags |= G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START;;
+
+  manager->priv->control_proxy = g_dbus_proxy_new_sync (manager->priv->connection,
+                                                        proxy_flags,
+                                                        NULL, /* GDBusInterfaceInfo* */
+                                                        manager->priv->name,
+                                                        manager->priv->object_path,
+                                                        "org.freedesktop.DBus.ObjectManager",
+                                                        cancellable,
+                                                        error);
+  if (manager->priv->control_proxy == NULL)
+    goto out;
+
+  g_signal_connect (G_OBJECT (manager->priv->control_proxy),
+                    "notify::g-name-owner",
+                    G_CALLBACK (on_notify_g_name_owner),
+                    manager);
+
+  manager->priv->name_owner = g_dbus_proxy_get_name_owner (manager->priv->control_proxy);
+  if (manager->priv->name_owner == NULL)
+    {
+      /* it's perfectly fine if there's no name owner.. we're just going to
+       * wait until one is ready
+       */
+    }
+  else
+    {
+      /* yay, we have a name owner */
+      g_signal_connect (manager->priv->control_proxy,
+                        "g-signal",
+                        G_CALLBACK (on_control_proxy_g_signal),
+                        manager);
+      subscribe_signals (manager,
+                         manager->priv->name_owner);
+      value = g_dbus_proxy_call_sync (manager->priv->control_proxy,
+                                      "GetManagedObjects",
+                                      NULL, /* parameters */
+                                      G_DBUS_CALL_FLAGS_NONE,
+                                      -1,
+                                      cancellable,
+                                      error);
+      if (value == NULL)
+        {
+          maybe_unsubscribe_signals (manager);
+          g_warn_if_fail (g_signal_handlers_disconnect_by_func (manager->priv->control_proxy,
+                                                                on_control_proxy_g_signal,
+                                                                manager) == 1);
+          g_object_unref (manager->priv->control_proxy);
+          manager->priv->control_proxy = NULL;
+          goto out;
+        }
+
+      process_get_all_result (manager, value, manager->priv->name_owner);
+      g_variant_unref (value);
+    }
+
+  ret = TRUE;
+
+ out:
+  return ret;
+}
+
+static void
+initable_iface_init (GInitableIface *initable_iface)
+{
+  initable_iface->init = initable_init;
+}
+
+static void
+async_initable_iface_init (GAsyncInitableIface *async_initable_iface)
+{
+  /* for now, just use default: run GInitable code in thread */
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+add_interfaces (GDBusObjectManagerClient *manager,
+                const gchar       *object_path,
+                GVariant          *ifaces_and_properties,
+                const gchar       *name_owner)
+{
+  GDBusObjectProxy *op;
+  gboolean added;
+  GVariantIter iter;
+  const gchar *interface_name;
+  GVariant *properties;
+
+  g_return_if_fail (g_dbus_is_unique_name (name_owner));
+
+  added = FALSE;
+  op = g_hash_table_lookup (manager->priv->map_object_path_to_object_proxy, object_path);
+  if (op == NULL)
+    {
+      op = _g_dbus_object_proxy_new (manager->priv->connection, object_path);
+      added = TRUE;
+    }
+
+  g_variant_iter_init (&iter, ifaces_and_properties);
+  while (g_variant_iter_next (&iter,
+                              "{&s a{sv}}",
+                              &interface_name,
+                              &properties))
+    {
+      GDBusProxy *interface_proxy;
+      GError *error;
+      GType interface_proxy_type;
+
+      if (manager->priv->get_proxy_type_func != NULL)
+        {
+          interface_proxy_type = manager->priv->get_proxy_type_func (manager,
+                                                                     object_path,
+                                                                     interface_name,
+                                                                     manager->priv->get_proxy_type_user_data);
+          g_warn_if_fail (g_type_is_a (interface_proxy_type, G_TYPE_DBUS_PROXY));
+        }
+      else
+        {
+          interface_proxy_type = G_TYPE_DBUS_PROXY;
+        }
+
+      /* this is fine - there is no blocking IO because we pass DO_NOT_LOAD_PROPERTIES and
+       * DO_NOT_CONNECT_SIGNALS and use a unique name
+       */
+      error = NULL;
+      interface_proxy = g_initable_new (interface_proxy_type,
+                                        NULL, /* GCancellable */
+                                        &error,
+                                        "g-connection", manager->priv->connection,
+                                        "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
+                                                   G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
+                                        "g-name", name_owner,
+                                        "g-object-path", object_path,
+                                        "g-interface-name", interface_name,
+                                        NULL);
+      if (interface_proxy == NULL)
+        {
+          g_warning ("%s: Error constructing proxy for path %s and interface %s: %s",
+                     G_STRLOC,
+                     object_path,
+                     interface_name,
+                     error->message);
+          g_error_free (error);
+        }
+      else
+        {
+          GVariantIter property_iter;
+          const gchar *property_name;
+          GVariant *property_value;
+
+          /* associate the interface proxy with the object */
+          g_dbus_interface_set_object (G_DBUS_INTERFACE (interface_proxy),
+                                       G_DBUS_OBJECT (op));
+
+          g_variant_iter_init (&property_iter, properties);
+          while (g_variant_iter_next (&property_iter,
+                                      "{&sv}",
+                                      &property_name,
+                                      &property_value))
+            {
+              g_dbus_proxy_set_cached_property (interface_proxy,
+                                                property_name,
+                                                property_value);
+              g_variant_unref (property_value);
+            }
+
+          _g_dbus_object_proxy_add_interface (op, interface_proxy);
+          if (!added)
+            g_signal_emit_by_name (manager, "interface-added", op, interface_proxy);
+          g_object_unref (interface_proxy);
+        }
+      g_variant_unref (properties);
+    }
+
+  if (added)
+    {
+      g_hash_table_insert (manager->priv->map_object_path_to_object_proxy,
+                           g_strdup (object_path),
+                           op);
+      g_signal_emit_by_name (manager, "object-added", op);
+    }
+}
+
+static void
+remove_interfaces (GDBusObjectManagerClient   *manager,
+                   const gchar         *object_path,
+                   const gchar *const  *interface_names)
+{
+  GDBusObjectProxy *op;
+  GList *interfaces;
+  guint n;
+  guint num_interfaces;
+  guint num_interfaces_to_remove;
+
+  op = g_hash_table_lookup (manager->priv->map_object_path_to_object_proxy, object_path);
+  if (op == NULL)
+    {
+      g_warning ("%s: Processing InterfaceRemoved signal for path %s but no object proxy exists",
+                 G_STRLOC,
+                 object_path);
+      goto out;
+    }
+
+  interfaces = g_dbus_object_get_interfaces (G_DBUS_OBJECT (op));
+  num_interfaces = g_list_length (interfaces);
+  g_list_foreach (interfaces, (GFunc) g_object_unref, NULL);
+  g_list_free (interfaces);
+
+  num_interfaces_to_remove = g_strv_length ((gchar **) interface_names);
+
+  /* see if we are going to completety remove the object */
+  if (num_interfaces_to_remove == num_interfaces)
+    {
+      g_object_ref (op);
+      g_warn_if_fail (g_hash_table_remove (manager->priv->map_object_path_to_object_proxy, object_path));
+      g_signal_emit_by_name (manager, "object-removed", op);
+      g_object_unref (op);
+    }
+  else
+    {
+      for (n = 0; interface_names != NULL && interface_names[n] != NULL; n++)
+        {
+          GDBusInterface *interface;
+          interface = g_dbus_object_get_interface (G_DBUS_OBJECT (op), interface_names[n]);
+          _g_dbus_object_proxy_remove_interface (op, interface_names[n]);
+          if (interface != NULL)
+            {
+              g_signal_emit_by_name (manager, "interface-removed", op, interface);
+              g_object_unref (interface);
+            }
+        }
+    }
+ out:
+  ;
+}
+
+static void
+process_get_all_result (GDBusObjectManagerClient *manager,
+                        GVariant          *value,
+                        const gchar       *name_owner)
+{
+  GVariant *arg0;
+  const gchar *object_path;
+  GVariant *ifaces_and_properties;
+  GVariantIter iter;
+
+  g_return_if_fail (g_dbus_is_unique_name (name_owner));
+
+  arg0 = g_variant_get_child_value (value, 0);
+  g_variant_iter_init (&iter, arg0);
+  while (g_variant_iter_next (&iter,
+                              "{&o a{sa{sv}}}",
+                              &object_path,
+                              &ifaces_and_properties))
+    {
+      add_interfaces (manager, object_path, ifaces_and_properties, name_owner);
+      g_variant_unref (ifaces_and_properties);
+    }
+  g_variant_unref (arg0);
+}
+
+static void
+on_control_proxy_g_signal (GDBusProxy   *proxy,
+                           const gchar  *sender_name,
+                           const gchar  *signal_name,
+                           GVariant     *parameters,
+                           gpointer      user_data)
+{
+  GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (user_data);
+  const gchar *object_path;
+
+  //g_debug ("yay, g_signal %s: %s\n", signal_name, g_variant_print (parameters, TRUE));
+
+  if (g_strcmp0 (signal_name, "InterfacesAdded") == 0)
+    {
+      GVariant *ifaces_and_properties;
+      g_variant_get (parameters,
+                     "(&o a{sa{sv}})",
+                     &object_path,
+                     &ifaces_and_properties);
+      add_interfaces (manager, object_path, ifaces_and_properties, manager->priv->name_owner);
+      g_variant_unref (ifaces_and_properties);
+    }
+  else if (g_strcmp0 (signal_name, "InterfacesRemoved") == 0)
+    {
+      const gchar **ifaces;
+      g_variant_get (parameters,
+                     "(&o^a&s)",
+                     &object_path,
+                     &ifaces);
+      remove_interfaces (manager, object_path, ifaces);
+      g_free (ifaces);
+    }
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static const gchar *
+g_dbus_object_manager_client_get_object_path (GDBusObjectManager *_manager)
+{
+  GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (_manager);
+  return manager->priv->object_path;
+}
+
+static GDBusObject *
+g_dbus_object_manager_client_get_object (GDBusObjectManager *_manager,
+                                         const gchar        *object_path)
+{
+  GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (_manager);
+  GDBusObject *ret;
+
+  ret = g_hash_table_lookup (manager->priv->map_object_path_to_object_proxy, object_path);
+  if (ret != NULL)
+    g_object_ref (ret);
+  return ret;
+}
+
+static GDBusInterface *
+g_dbus_object_manager_client_get_interface  (GDBusObjectManager  *_manager,
+                                             const gchar         *object_path,
+                                             const gchar         *interface_name)
+{
+  GDBusInterface *ret;
+  GDBusObject *object;
+
+  ret = NULL;
+
+  object = g_dbus_object_manager_get_object (_manager, object_path);
+  if (object == NULL)
+    goto out;
+
+  ret = g_dbus_object_get_interface (object, interface_name);
+  g_object_unref (object);
+
+ out:
+  return ret;
+}
+
+static GList *
+g_dbus_object_manager_client_get_objects (GDBusObjectManager *_manager)
+{
+  GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (_manager);
+  GList *ret;
+
+  g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager), NULL);
+
+  ret = g_hash_table_get_values (manager->priv->map_object_path_to_object_proxy);
+  g_list_foreach (ret, (GFunc) g_object_ref, NULL);
+  return ret;
+}
+
+
+static void
+dbus_object_manager_interface_init (GDBusObjectManagerIface *iface)
+{
+  iface->get_object_path = g_dbus_object_manager_client_get_object_path;
+  iface->get_objects     = g_dbus_object_manager_client_get_objects;
+  iface->get_object      = g_dbus_object_manager_client_get_object;
+  iface->get_interface   = g_dbus_object_manager_client_get_interface;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
diff --git a/gio/gdbusobjectmanagerclient.h b/gio/gdbusobjectmanagerclient.h
new file mode 100644
index 0000000..386c63d
--- /dev/null
+++ b/gio/gdbusobjectmanagerclient.h
@@ -0,0 +1,129 @@
+/* GDBus - GLib D-Bus Library
+ *
+ * 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
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#ifndef __G_DBUS_OBJECT_MANAGER_CLIENT_H__
+#define __G_DBUS_OBJECT_MANAGER_CLIENT_H__
+
+#include <gio/giotypes.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_DBUS_OBJECT_MANAGER_CLIENT         (g_dbus_object_manager_client_get_type ())
+#define G_DBUS_OBJECT_MANAGER_CLIENT(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_OBJECT_MANAGER_CLIENT, GDBusObjectManagerClient))
+#define G_DBUS_OBJECT_MANAGER_CLIENT_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_OBJECT_MANAGER_CLIENT, GDBusObjectManagerClientClass))
+#define G_DBUS_OBJECT_MANAGER_CLIENT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_OBJECT_MANAGER_CLIENT, GDBusObjectManagerClientClass))
+#define G_IS_DBUS_OBJECT_MANAGER_CLIENT(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_OBJECT_MANAGER_CLIENT))
+#define G_IS_DBUS_OBJECT_MANAGER_CLIENT_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_OBJECT_MANAGER_CLIENT))
+
+typedef struct _GDBusObjectManagerClientClass   GDBusObjectManagerClientClass;
+typedef struct _GDBusObjectManagerClientPrivate GDBusObjectManagerClientPrivate;
+
+/**
+ * GDBusObjectManagerClient:
+  *
+ * The #GDBusObjectManagerClient structure contains private data and should
+ * only be accessed using the provided API.
+ */
+struct _GDBusObjectManagerClient
+{
+  /*< private >*/
+  GObject parent_instance;
+  GDBusObjectManagerClientPrivate *priv;
+};
+
+/**
+ * GDBusObjectManagerClientClass:
+ * @parent_class: The parent class.
+ * @interface_proxy_signal: Signal class handler for the #GDBusObjectManagerClient::interface-proxy-signal signal.
+ * @interface_proxy_properties_changed: Signal class handler for the #GDBusObjectManagerClient::interface-proxy-properties-changed signal.
+ *
+ * Class structure for #GDBusObjectManagerClient.
+ */
+struct _GDBusObjectManagerClientClass
+{
+  GObjectClass parent_class;
+
+  /* signals */
+  void    (*interface_proxy_signal)             (GDBusObjectManagerClient *manager,
+                                                 GDBusObjectProxy         *object_proxy,
+                                                 GDBusProxy               *interface_proxy,
+                                                 const gchar              *sender_name,
+                                                 const gchar              *signal_name,
+                                                 GVariant                 *parameters);
+
+  void    (*interface_proxy_properties_changed) (GDBusObjectManagerClient   *manager,
+                                                 GDBusObjectProxy           *object_proxy,
+                                                 GDBusProxy                 *interface_proxy,
+                                                 GVariant                   *changed_properties,
+                                                 const gchar* const         *invalidated_properties);
+
+  /*< private >*/
+  gpointer padding[8];
+};
+
+GType                         g_dbus_object_manager_client_get_type           (void) G_GNUC_CONST;
+void                          g_dbus_object_manager_client_new                (GDBusConnection               *connection,
+                                                                               GDBusObjectManagerClientFlags  flags,
+                                                                               const gchar                   *name,
+                                                                               const gchar                   *object_path,
+                                                                               GDBusProxyTypeFunc             get_proxy_type_func,
+                                                                               gpointer                       get_proxy_type_user_data,
+                                                                               GCancellable                  *cancellable,
+                                                                               GAsyncReadyCallback            callback,
+                                                                               gpointer                       user_data);
+GDBusObjectManager           *g_dbus_object_manager_client_new_finish         (GAsyncResult                  *res,
+                                                                               GError                       **error);
+GDBusObjectManager           *g_dbus_object_manager_client_new_sync           (GDBusConnection               *connection,
+                                                                               GDBusObjectManagerClientFlags  flags,
+                                                                               const gchar                   *name,
+                                                                               const gchar                   *object_path,
+                                                                               GDBusProxyTypeFunc             get_proxy_type_func,
+                                                                               gpointer                       get_proxy_type_user_data,
+                                                                               GCancellable                  *cancellable,
+                                                                               GError                       **error);
+void                          g_dbus_object_manager_client_new_for_bus        (GBusType                       bus_type,
+                                                                               GDBusObjectManagerClientFlags  flags,
+                                                                               const gchar                   *name,
+                                                                               const gchar                   *object_path,
+                                                                               GDBusProxyTypeFunc             get_proxy_type_func,
+                                                                               gpointer                       get_proxy_type_user_data,
+                                                                               GCancellable                  *cancellable,
+                                                                               GAsyncReadyCallback            callback,
+                                                                               gpointer                       user_data);
+GDBusObjectManager           *g_dbus_object_manager_client_new_for_bus_finish (GAsyncResult                  *res,
+                                                                               GError                       **error);
+GDBusObjectManager           *g_dbus_object_manager_client_new_for_bus_sync   (GBusType                       bus_type,
+                                                                               GDBusObjectManagerClientFlags  flags,
+                                                                               const gchar                   *name,
+                                                                               const gchar                   *object_path,
+                                                                               GDBusProxyTypeFunc             get_proxy_type_func,
+                                                                               gpointer                       get_proxy_type_user_data,
+                                                                               GCancellable                  *cancellable,
+                                                                               GError                       **error);
+GDBusConnection              *g_dbus_object_manager_client_get_connection     (GDBusObjectManagerClient      *manager);
+GDBusObjectManagerClientFlags g_dbus_object_manager_client_get_flags          (GDBusObjectManagerClient      *manager);
+const gchar                  *g_dbus_object_manager_client_get_name           (GDBusObjectManagerClient      *manager);
+gchar                        *g_dbus_object_manager_client_get_name_owner     (GDBusObjectManagerClient      *manager);
+
+G_END_DECLS
+
+#endif /* __G_DBUS_OBJECT_MANAGER_CLIENT_H */
diff --git a/gio/gdbusobjectmanagerserver.c b/gio/gdbusobjectmanagerserver.c
new file mode 100644
index 0000000..ed7c575
--- /dev/null
+++ b/gio/gdbusobjectmanagerserver.c
@@ -0,0 +1,898 @@
+/* GDBus - GLib D-Bus Library
+ *
+ * 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
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#include "config.h"
+
+#include "gdbusobjectmanager.h"
+#include "gdbusobjectmanagerserver.h"
+#include "gdbusobject.h"
+#include "gdbusobjectstub.h"
+#include "gdbusinterfacestub.h"
+#include "gdbusconnection.h"
+#include "gdbusintrospection.h"
+#include "gdbusmethodinvocation.h"
+#include "gdbuserror.h"
+
+#include "glibintl.h"
+
+/**
+ * SECTION:gdbusobjectmanagerserver
+ * @short_description: Service-side object manager
+ * @include: gio/gio.h
+ *
+ * #GDBusObjectManagerServer is used to export #GDBusObject instances using
+ * the standardized <ulink
+ * url="http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager";>org.freedesktop.DBus.ObjectManager</ulink>
+ * interface. For example, remote D-Bus clients can get all objects
+ * and properties in a single call. Additionally, any change in the
+ * object hierarchy is broadcast using signals. This means that D-Bus
+ * clients can keep caches up to date by only listening to D-Bus
+ * signals.
+ *
+ * See #GDBusObjectManagerClient for the client-side code that is intended to
+ * be used with #GDBusObjectManagerServer.
+ */
+
+typedef struct
+{
+  GDBusObjectStub *object;
+  GDBusObjectManagerServer *manager;
+  GHashTable *map_iface_name_to_iface;
+  gboolean exported;
+} RegistrationData;
+
+static void registration_data_free (RegistrationData *data);
+
+static void g_dbus_object_manager_server_emit_interfaces_added (GDBusObjectManagerServer *manager,
+                                                         RegistrationData   *data,
+                                                         const gchar *const *interfaces);
+
+static void g_dbus_object_manager_server_emit_interfaces_removed (GDBusObjectManagerServer *manager,
+                                                           RegistrationData   *data,
+                                                           const gchar *const *interfaces);
+
+struct _GDBusObjectManagerServerPrivate
+{
+  GDBusConnection *connection;
+  gchar *object_path;
+  gchar *object_path_ending_in_slash;
+  GHashTable *map_object_path_to_data;
+  guint manager_reg_id;
+};
+
+enum
+{
+  PROP_0,
+  PROP_CONNECTION,
+  PROP_OBJECT_PATH
+};
+
+static void dbus_object_manager_interface_init (GDBusObjectManagerIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GDBusObjectManagerServer, g_dbus_object_manager_server, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_DBUS_OBJECT_MANAGER, dbus_object_manager_interface_init));
+
+static void g_dbus_object_manager_server_constructed (GObject *object);
+
+static void
+g_dbus_object_manager_server_finalize (GObject *object)
+{
+  GDBusObjectManagerServer *manager = G_DBUS_OBJECT_MANAGER_SERVER (object);
+
+  g_hash_table_unref (manager->priv->map_object_path_to_data);
+  if (manager->priv->manager_reg_id > 0)
+    g_warn_if_fail (g_dbus_connection_unregister_object (manager->priv->connection, manager->priv->manager_reg_id));
+  g_object_unref (manager->priv->connection);
+  g_free (manager->priv->object_path);
+  g_free (manager->priv->object_path_ending_in_slash);
+
+  if (G_OBJECT_CLASS (g_dbus_object_manager_server_parent_class)->finalize != NULL)
+    G_OBJECT_CLASS (g_dbus_object_manager_server_parent_class)->finalize (object);
+}
+
+static void
+g_dbus_object_manager_server_get_property (GObject    *_object,
+                                    guint       prop_id,
+                                    GValue     *value,
+                                    GParamSpec *pspec)
+{
+  GDBusObjectManagerServer *manager = G_DBUS_OBJECT_MANAGER_SERVER (_object);
+
+  switch (prop_id)
+    {
+    case PROP_CONNECTION:
+      g_value_set_object (value, g_dbus_object_manager_server_get_connection (manager));
+      break;
+
+    case PROP_OBJECT_PATH:
+      g_value_set_string (value, g_dbus_object_manager_get_object_path (G_DBUS_OBJECT_MANAGER (manager)));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (_object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+g_dbus_object_manager_server_set_property (GObject       *_object,
+                                    guint          prop_id,
+                                    const GValue  *value,
+                                    GParamSpec    *pspec)
+{
+  GDBusObjectManagerServer *manager = G_DBUS_OBJECT_MANAGER_SERVER (_object);
+
+  switch (prop_id)
+    {
+    case PROP_CONNECTION:
+      g_assert (manager->priv->connection == NULL);
+      g_assert (G_IS_DBUS_CONNECTION (g_value_get_object (value)));
+      manager->priv->connection = g_value_dup_object (value);
+      break;
+
+    case PROP_OBJECT_PATH:
+      g_assert (manager->priv->object_path == NULL);
+      g_assert (g_variant_is_object_path (g_value_get_string (value)));
+      manager->priv->object_path = g_value_dup_string (value);
+      manager->priv->object_path_ending_in_slash = g_strdup_printf ("%s/", manager->priv->object_path);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (_object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+g_dbus_object_manager_server_class_init (GDBusObjectManagerServerClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->finalize     = g_dbus_object_manager_server_finalize;
+  gobject_class->constructed  = g_dbus_object_manager_server_constructed;
+  gobject_class->set_property = g_dbus_object_manager_server_set_property;
+  gobject_class->get_property = g_dbus_object_manager_server_get_property;
+
+  /**
+   * GDBusObjectManagerServer:connection:
+   *
+   * The #GDBusConnection to export objects on.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_CONNECTION,
+                                   g_param_spec_object ("connection",
+                                                        "Connection",
+                                                        "The connection to export objects on",
+                                                        G_TYPE_DBUS_CONNECTION,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_WRITABLE |
+                                                        G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GDBusObjectManagerServer:object-path:
+   *
+   * The object path to register the manager object at.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_OBJECT_PATH,
+                                   g_param_spec_string ("object-path",
+                                                        "Object Path",
+                                                        "The object path to register the manager object at",
+                                                        NULL,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_WRITABLE |
+                                                        G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_STATIC_STRINGS));
+
+  g_type_class_add_private (klass, sizeof (GDBusObjectManagerServerPrivate));
+}
+
+static void
+g_dbus_object_manager_server_init (GDBusObjectManagerServer *manager)
+{
+  manager->priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
+                                               G_TYPE_DBUS_OBJECT_MANAGER_SERVER,
+                                               GDBusObjectManagerServerPrivate);
+  manager->priv->map_object_path_to_data = g_hash_table_new_full (g_str_hash,
+                                                                  g_str_equal,
+                                                                  g_free,
+                                                                  (GDestroyNotify) registration_data_free);
+}
+
+/**
+ * g_dbus_object_manager_server_new:
+ * @connection: A #GDBusConnection.
+ * @object_path: The object path to export the manager object at.
+ *
+ * Creates a new #GDBusObjectManagerServer object.
+ *
+ * TODO: make it so that the objects are not exported yet -
+ * e.g. start()/stop() semantics.
+ *
+ * Returns: A #GDBusObjectManagerServer object. Free with g_object_unref().
+ */
+GDBusObjectManagerServer *
+g_dbus_object_manager_server_new (GDBusConnection *connection,
+                           const gchar     *object_path)
+{
+  g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
+  g_return_val_if_fail (g_variant_is_object_path (object_path), NULL);
+  return G_DBUS_OBJECT_MANAGER_SERVER (g_object_new (G_TYPE_DBUS_OBJECT_MANAGER_SERVER,
+                                              "connection", connection,
+                                              "object-path", object_path,
+                                              NULL));
+}
+
+/**
+ * g_dbus_object_manager_server_get_connection:
+ * @manager: A #GDBusObjectManagerServer
+ *
+ * Gets the #GDBusConnection used by @manager.
+ *
+ * Returns: A #GDBusConnection object. Do not free, the object belongs
+ * to @manager.
+ */
+GDBusConnection *
+g_dbus_object_manager_server_get_connection (GDBusObjectManagerServer *manager)
+{
+  g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_SERVER (manager), NULL);
+  return manager->priv->connection;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+registration_data_export_interface (RegistrationData     *data,
+                                    GDBusInterfaceStub   *interface_stub)
+{
+  GDBusInterfaceInfo *info;
+  GError *error;
+  const gchar *object_path;
+
+  object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (data->object));
+
+  info = g_dbus_interface_stub_get_info (interface_stub);
+  error = NULL;
+  if (!g_dbus_interface_stub_export (interface_stub,
+                                     data->manager->priv->connection,
+                                     object_path,
+                                     &error))
+    {
+      /* TODO: probably wrong to complain on stderr */
+      g_warning ("%s: Error registering object at %s with interface %s: %s",
+                 G_STRLOC,
+                 object_path,
+                 info->name,
+                 error->message);
+      g_error_free (error);
+      goto out;
+    }
+
+  g_assert (g_hash_table_lookup (data->map_iface_name_to_iface, info->name) == NULL);
+  g_hash_table_insert (data->map_iface_name_to_iface,
+                       info->name,
+                       g_object_ref (interface_stub));
+
+  /* if we are already exported, then... */
+  if (data->exported)
+    {
+      const gchar *interfaces[2];
+      /* emit InterfacesAdded on the ObjectManager object */
+      interfaces[0] = info->name;
+      interfaces[1] = NULL;
+      g_dbus_object_manager_server_emit_interfaces_added (data->manager, data, interfaces);
+    }
+
+ out:
+  ;
+}
+
+static void
+registration_data_unexport_interface (RegistrationData     *data,
+                                      GDBusInterfaceStub   *interface_stub)
+{
+  GDBusInterfaceInfo *info;
+  GDBusInterfaceStub *iface;
+
+  info = g_dbus_interface_stub_get_info (interface_stub);
+  iface = g_hash_table_lookup (data->map_iface_name_to_iface, info->name);
+  g_assert (iface != NULL);
+
+  g_dbus_interface_stub_unexport (iface);
+
+  g_warn_if_fail (g_hash_table_remove (data->map_iface_name_to_iface, info->name));
+
+  /* if we are already exported, then... */
+  if (data->exported)
+    {
+      const gchar *interfaces[2];
+      /* emit InterfacesRemoved on the ObjectManager object */
+      interfaces[0] = info->name;
+      interfaces[1] = NULL;
+      g_dbus_object_manager_server_emit_interfaces_removed (data->manager, data, interfaces);
+    }
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+on_interface_added (GDBusObject    *object,
+                    GDBusInterface *interface,
+                    gpointer        user_data)
+{
+  RegistrationData *data = user_data;
+  registration_data_export_interface (data, G_DBUS_INTERFACE_STUB (interface));
+}
+
+static void
+on_interface_removed (GDBusObject    *object,
+                      GDBusInterface *interface,
+                      gpointer        user_data)
+{
+  RegistrationData *data = user_data;
+  registration_data_unexport_interface (data, G_DBUS_INTERFACE_STUB (interface));
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+
+static void
+registration_data_free (RegistrationData *data)
+{
+  GHashTableIter iter;
+  GDBusInterfaceStub *iface;
+
+  data->exported = FALSE;
+
+  g_hash_table_iter_init (&iter, data->map_iface_name_to_iface);
+  while (g_hash_table_iter_next (&iter, NULL, (gpointer) &iface))
+    g_dbus_interface_stub_unexport (iface);
+
+  g_signal_handlers_disconnect_by_func (data->object, G_CALLBACK (on_interface_added), data);
+  g_signal_handlers_disconnect_by_func (data->object, G_CALLBACK (on_interface_removed), data);
+  g_object_unref (data->object);
+  g_hash_table_destroy (data->map_iface_name_to_iface);
+  g_free (data);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/**
+ * g_dbus_object_manager_server_export:
+ * @manager: A #GDBusObjectManagerServer.
+ * @object: A #GDBusObjectStub.
+ *
+ * Exports @object on @manager.
+ *
+ * If there is already a #GDBusObject exported at the object path,
+ * then the old object is removed.
+ *
+ * The object path for @object must be in the hierarchy rooted by the
+ * object path for @manager.
+ *
+ * Note that @manager will take a reference on @object for as long as
+ * it is exported.
+ */
+void
+g_dbus_object_manager_server_export (GDBusObjectManagerServer  *manager,
+                              GDBusObjectStub     *object)
+{
+  RegistrationData *data;
+  GList *existing_interfaces;
+  GList *l;
+  GPtrArray *interface_names;
+  const gchar *object_path;
+
+  object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (object));
+
+  g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER_SERVER (manager));
+  g_return_if_fail (G_IS_DBUS_OBJECT (object));
+  g_return_if_fail (g_str_has_prefix (object_path, manager->priv->object_path_ending_in_slash));
+
+  interface_names = g_ptr_array_new ();
+
+  data = g_hash_table_lookup (manager->priv->map_object_path_to_data, object_path);
+  if (data != NULL)
+    g_dbus_object_manager_server_unexport (manager, object_path);
+
+  data = g_new0 (RegistrationData, 1);
+  data->object = g_object_ref (object);
+  data->manager = manager;
+  data->map_iface_name_to_iface = g_hash_table_new_full (g_str_hash,
+                                                         g_str_equal,
+                                                         NULL,
+                                                         (GDestroyNotify) g_object_unref);
+
+  g_signal_connect (object,
+                    "interface-added",
+                    G_CALLBACK (on_interface_added),
+                    data);
+  g_signal_connect (object,
+                    "interface-removed",
+                    G_CALLBACK (on_interface_removed),
+                    data);
+
+  /* Register all known interfaces - note that data->exported is FALSE so
+   * we don't emit any InterfacesAdded signals.
+   */
+  existing_interfaces = g_dbus_object_get_interfaces (G_DBUS_OBJECT (object));
+  for (l = existing_interfaces; l != NULL; l = l->next)
+    {
+      GDBusInterfaceStub *interface_stub = G_DBUS_INTERFACE_STUB (l->data);
+      registration_data_export_interface (data, interface_stub);
+      g_ptr_array_add (interface_names, g_dbus_interface_stub_get_info (interface_stub)->name);
+    }
+  g_list_foreach (existing_interfaces, (GFunc) g_object_unref, NULL);
+  g_list_free (existing_interfaces);
+  g_ptr_array_add (interface_names, NULL);
+
+  data->exported = TRUE;
+
+  /* now emit InterfacesAdded() for all the interfaces */
+  g_dbus_object_manager_server_emit_interfaces_added (manager, data, (const gchar *const *) interface_names->pdata);
+  g_ptr_array_unref (interface_names);
+
+  g_hash_table_insert (manager->priv->map_object_path_to_data,
+                       g_strdup (object_path),
+                       data);
+}
+
+/**
+ * g_dbus_object_manager_server_export_and_uniquify:
+ * @manager: A #GDBusObjectManagerServer.
+ * @object: An object.
+ *
+ * Like g_dbus_object_manager_server_export() but appends a string of
+ * the form <literal>_N</literal> (with N being a natural number) to
+ * @object<!-- -->'s object path if an object with the given path
+ * already exists. As such, the #GDBusObjectProxy:object-path property
+ * of @object may be modified.
+ */
+void
+g_dbus_object_manager_server_export_and_uniquify (GDBusObjectManagerServer  *manager,
+                                           GDBusObjectStub     *object)
+{
+  gchar *orig_object_path;
+  gchar *object_path;
+  guint count;
+  gboolean modified;
+
+  orig_object_path = g_strdup (g_dbus_object_get_object_path (G_DBUS_OBJECT (object)));
+
+  g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER_SERVER (manager));
+  g_return_if_fail (G_IS_DBUS_OBJECT (object));
+  g_return_if_fail (g_str_has_prefix (orig_object_path, manager->priv->object_path_ending_in_slash));
+
+  object_path = g_strdup (orig_object_path);
+  count = 1;
+  modified = FALSE;
+  while (TRUE)
+    {
+      RegistrationData *data;
+      data = g_hash_table_lookup (manager->priv->map_object_path_to_data, object_path);
+      if (data == NULL)
+        {
+          break;
+        }
+      g_free (object_path);
+      object_path = g_strdup_printf ("%s_%d", orig_object_path, count++);
+      modified = TRUE;
+    }
+
+  if (modified)
+    g_dbus_object_stub_set_object_path (G_DBUS_OBJECT_STUB (object), object_path);
+
+  g_dbus_object_manager_server_export (manager, object);
+
+  g_free (object_path);
+  g_free (orig_object_path);
+}
+
+/**
+ * g_dbus_object_manager_server_unexport:
+ * @manager: A #GDBusObjectManagerServer.
+ * @object_path: An object path.
+ *
+ * If @manager has an object at @path, removes the object. Otherwise
+ * does nothing.
+ *
+ * Note that @object_path must be in the hierarchy rooted by the
+ * object path for @manager.
+ */
+void
+g_dbus_object_manager_server_unexport (GDBusObjectManagerServer  *manager,
+                                const gchar         *object_path)
+{
+  RegistrationData *data;
+
+  g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER_SERVER (manager));
+  g_return_if_fail (g_variant_is_object_path (object_path));
+  g_return_if_fail (g_str_has_prefix (object_path, manager->priv->object_path_ending_in_slash));
+
+  data = g_hash_table_lookup (manager->priv->map_object_path_to_data, object_path);
+  if (data != NULL)
+    {
+      GPtrArray *interface_names;
+      GHashTableIter iter;
+      const gchar *iface_name;
+
+      interface_names = g_ptr_array_new ();
+      g_hash_table_iter_init (&iter, data->map_iface_name_to_iface);
+      while (g_hash_table_iter_next (&iter, (gpointer) &iface_name, NULL))
+        g_ptr_array_add (interface_names, (gpointer) iface_name);
+      g_ptr_array_add (interface_names, NULL);
+      /* now emit InterfacesRemoved() for all the interfaces */
+      g_dbus_object_manager_server_emit_interfaces_removed (manager, data, (const gchar *const *) interface_names->pdata);
+      g_ptr_array_unref (interface_names);
+
+      g_hash_table_remove (manager->priv->map_object_path_to_data, object_path);
+    }
+}
+
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static const GDBusArgInfo manager_interfaces_added_signal_info_arg0 =
+{
+  -1,
+  "object_path",
+  "o",
+  (GDBusAnnotationInfo**) NULL,
+};
+
+static const GDBusArgInfo manager_interfaces_added_signal_info_arg1 =
+{
+  -1,
+  "interfaces_and_properties",
+  "a{sa{sv}}",
+  (GDBusAnnotationInfo**) NULL,
+};
+
+static const GDBusArgInfo * const manager_interfaces_added_signal_info_arg_pointers[] =
+{
+  &manager_interfaces_added_signal_info_arg0,
+  &manager_interfaces_added_signal_info_arg1,
+  NULL
+};
+
+static const GDBusSignalInfo manager_interfaces_added_signal_info =
+{
+  -1,
+  "InterfacesAdded",
+  (GDBusArgInfo**) &manager_interfaces_added_signal_info_arg_pointers,
+  (GDBusAnnotationInfo**) NULL
+};
+
+/* ---------- */
+
+static const GDBusArgInfo manager_interfaces_removed_signal_info_arg0 =
+{
+  -1,
+  "object_path",
+  "o",
+  (GDBusAnnotationInfo**) NULL,
+};
+
+static const GDBusArgInfo manager_interfaces_removed_signal_info_arg1 =
+{
+  -1,
+  "interfaces",
+  "as",
+  (GDBusAnnotationInfo**) NULL,
+};
+
+static const GDBusArgInfo * const manager_interfaces_removed_signal_info_arg_pointers[] =
+{
+  &manager_interfaces_removed_signal_info_arg0,
+  &manager_interfaces_removed_signal_info_arg1,
+  NULL
+};
+
+static const GDBusSignalInfo manager_interfaces_removed_signal_info =
+{
+  -1,
+  "InterfacesRemoved",
+  (GDBusArgInfo**) &manager_interfaces_removed_signal_info_arg_pointers,
+  (GDBusAnnotationInfo**) NULL
+};
+
+/* ---------- */
+
+static const GDBusSignalInfo * const manager_signal_info_pointers[] =
+{
+  &manager_interfaces_added_signal_info,
+  &manager_interfaces_removed_signal_info,
+  NULL
+};
+
+/* ---------- */
+
+static const GDBusArgInfo manager_get_all_method_info_out_arg0 =
+{
+  -1,
+  "object_paths_interfaces_and_properties",
+  "a{oa{sa{sv}}}",
+  (GDBusAnnotationInfo**) NULL,
+};
+
+static const GDBusArgInfo * const manager_get_all_method_info_out_arg_pointers[] =
+{
+  &manager_get_all_method_info_out_arg0,
+  NULL
+};
+
+static const GDBusMethodInfo manager_get_all_method_info =
+{
+  -1,
+  "GetManagedObjects",
+  (GDBusArgInfo**) NULL,
+  (GDBusArgInfo**) &manager_get_all_method_info_out_arg_pointers,
+  (GDBusAnnotationInfo**) NULL
+};
+
+static const GDBusMethodInfo * const manager_method_info_pointers[] =
+{
+  &manager_get_all_method_info,
+  NULL
+};
+
+/* ---------- */
+
+static const GDBusInterfaceInfo manager_interface_info =
+{
+  -1,
+  "org.freedesktop.DBus.ObjectManager",
+  (GDBusMethodInfo **) manager_method_info_pointers,
+  (GDBusSignalInfo **) manager_signal_info_pointers,
+  (GDBusPropertyInfo **) NULL,
+  (GDBusAnnotationInfo **) NULL
+};
+
+static void
+manager_method_call (GDBusConnection       *connection,
+                     const gchar           *sender,
+                     const gchar           *object_path,
+                     const gchar           *interface_name,
+                     const gchar           *method_name,
+                     GVariant              *parameters,
+                     GDBusMethodInvocation *invocation,
+                     gpointer               user_data)
+{
+  GDBusObjectManagerServer *manager = G_DBUS_OBJECT_MANAGER_SERVER (user_data);
+  GVariantBuilder array_builder;
+  GHashTableIter object_iter;
+  RegistrationData *data;
+
+  if (g_strcmp0 (method_name, "GetManagedObjects") == 0)
+    {
+      g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("a{oa{sa{sv}}}"));
+      g_hash_table_iter_init (&object_iter, manager->priv->map_object_path_to_data);
+      while (g_hash_table_iter_next (&object_iter, NULL, (gpointer) &data))
+        {
+          GVariantBuilder interfaces_builder;
+          GHashTableIter interface_iter;
+          GDBusInterfaceStub *iface;
+          const gchar *iter_object_path;
+
+          g_variant_builder_init (&interfaces_builder, G_VARIANT_TYPE ("a{sa{sv}}"));
+          g_hash_table_iter_init (&interface_iter, data->map_iface_name_to_iface);
+          while (g_hash_table_iter_next (&interface_iter, NULL, (gpointer) &iface))
+            {
+              g_variant_builder_add_value (&interfaces_builder,
+                                           g_variant_new ("{s a{sv}}",
+                                                          g_dbus_interface_stub_get_info (iface)->name,
+                                                          g_dbus_interface_stub_get_properties (iface)));
+            }
+          iter_object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (data->object));
+          g_variant_builder_add (&array_builder,
+                                 "{oa{sa{sv}}}",
+                                 iter_object_path,
+                                 &interfaces_builder);
+        }
+
+      g_dbus_method_invocation_return_value (invocation,
+                                             g_variant_new ("(a{oa{sa{sv}}})",
+                                                            &array_builder));
+    }
+  else
+    {
+      g_dbus_method_invocation_return_error (invocation,
+                                             G_DBUS_ERROR,
+                                             G_DBUS_ERROR_UNKNOWN_METHOD,
+                                             "Unknown method %s - only GetManagedObjects() is supported",
+                                             method_name);
+    }
+}
+
+static const GDBusInterfaceVTable manager_interface_vtable =
+{
+  manager_method_call, /* handle_method_call */
+  NULL, /* get_property */
+  NULL  /* set_property */
+};
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+g_dbus_object_manager_server_constructed (GObject *object)
+{
+  GDBusObjectManagerServer *manager = G_DBUS_OBJECT_MANAGER_SERVER (object);
+  GError *error;
+
+  error = NULL;
+  manager->priv->manager_reg_id = g_dbus_connection_register_object (manager->priv->connection,
+                                                                     manager->priv->object_path,
+                                                                     (GDBusInterfaceInfo *) &manager_interface_info,
+                                                                     &manager_interface_vtable,
+                                                                     manager,
+                                                                     NULL, /* user_data_free_func */
+                                                                     &error);
+  if (manager->priv->manager_reg_id == 0)
+    {
+      /* TODO: probably wrong to complain on stderr */
+      g_warning ("%s: Error registering manager at %s: %s",
+                 G_STRLOC,
+                 manager->priv->object_path,
+                 error->message);
+      g_error_free (error);
+    }
+
+  if (G_OBJECT_CLASS (g_dbus_object_manager_server_parent_class)->constructed != NULL)
+    G_OBJECT_CLASS (g_dbus_object_manager_server_parent_class)->constructed (object);
+}
+
+static void
+g_dbus_object_manager_server_emit_interfaces_added (GDBusObjectManagerServer *manager,
+                                             RegistrationData   *data,
+                                             const gchar *const *interfaces)
+{
+  GVariantBuilder array_builder;
+  GError *error;
+  guint n;
+  const gchar *object_path;
+
+  g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("a{sa{sv}}"));
+  for (n = 0; interfaces[n] != NULL; n++)
+    {
+      GDBusInterfaceStub *iface;
+      iface = g_hash_table_lookup (data->map_iface_name_to_iface, interfaces[n]);
+      g_assert (iface != NULL);
+      g_variant_builder_add_value (&array_builder,
+                                   g_variant_new ("{s a{sv}}",
+                                                  interfaces[n],
+                                                  g_dbus_interface_stub_get_properties (iface)));
+    }
+
+  error = NULL;
+  object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (data->object));
+  g_dbus_connection_emit_signal (data->manager->priv->connection,
+                                 NULL, /* destination_bus_name */
+                                 manager->priv->object_path,
+                                 manager_interface_info.name,
+                                 "InterfacesAdded",
+                                 g_variant_new ("(oa{sa{sv}})",
+                                                object_path,
+                                                &array_builder),
+                                 &error);
+  g_assert_no_error (error);
+}
+
+static void
+g_dbus_object_manager_server_emit_interfaces_removed (GDBusObjectManagerServer *manager,
+                                               RegistrationData   *data,
+                                               const gchar *const *interfaces)
+{
+  GVariantBuilder array_builder;
+  GError *error;
+  guint n;
+  const gchar *object_path;
+
+  g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("as"));
+  for (n = 0; interfaces[n] != NULL; n++)
+    g_variant_builder_add (&array_builder, "s", interfaces[n]);
+
+  error = NULL;
+  object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (data->object));
+  g_dbus_connection_emit_signal (data->manager->priv->connection,
+                                 NULL, /* destination_bus_name */
+                                 manager->priv->object_path,
+                                 manager_interface_info.name,
+                                 "InterfacesRemoved",
+                                 g_variant_new ("(oas)",
+                                                object_path,
+                                                &array_builder),
+                                 &error);
+  g_assert_no_error (error);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static GList *
+g_dbus_object_manager_server_get_objects (GDBusObjectManager  *_manager)
+{
+  GDBusObjectManagerServer *manager = G_DBUS_OBJECT_MANAGER_SERVER (_manager);
+  GList *ret;
+  GHashTableIter iter;
+  RegistrationData *data;
+
+  ret = NULL;
+  g_hash_table_iter_init (&iter, manager->priv->map_object_path_to_data);
+  while (g_hash_table_iter_next (&iter, NULL, (gpointer) &data))
+    {
+      ret = g_list_prepend (ret, g_object_ref (data->object));
+    }
+
+  return ret;
+}
+
+static const gchar *
+g_dbus_object_manager_server_get_object_path (GDBusObjectManager *_manager)
+{
+  GDBusObjectManagerServer *manager = G_DBUS_OBJECT_MANAGER_SERVER (_manager);
+  return manager->priv->object_path;
+}
+
+static GDBusObject *
+g_dbus_object_manager_server_get_object (GDBusObjectManager *_manager,
+                                         const gchar        *object_path)
+{
+  GDBusObjectManagerServer *manager = G_DBUS_OBJECT_MANAGER_SERVER (_manager);
+  GDBusObject *ret;
+  RegistrationData *data;
+
+  ret = NULL;
+  data = g_hash_table_lookup (manager->priv->map_object_path_to_data, object_path);
+  if (data != NULL)
+    ret = g_object_ref (data->object);
+  return ret;
+}
+
+static GDBusInterface *
+g_dbus_object_manager_server_get_interface  (GDBusObjectManager  *_manager,
+                                             const gchar         *object_path,
+                                             const gchar         *interface_name)
+{
+  GDBusInterface *ret;
+  GDBusObject *object;
+
+  ret = NULL;
+
+  object = g_dbus_object_manager_get_object (_manager, object_path);
+  if (object == NULL)
+    goto out;
+
+  ret = g_dbus_object_get_interface (object, interface_name);
+  g_object_unref (object);
+
+ out:
+  return ret;
+}
+
+static void
+dbus_object_manager_interface_init (GDBusObjectManagerIface *iface)
+{
+  iface->get_object_path = g_dbus_object_manager_server_get_object_path;
+  iface->get_objects     = g_dbus_object_manager_server_get_objects;
+  iface->get_object      = g_dbus_object_manager_server_get_object;
+  iface->get_interface   = g_dbus_object_manager_server_get_interface;
+}
diff --git a/gio/gdbusobjectmanagerserver.h b/gio/gdbusobjectmanagerserver.h
new file mode 100644
index 0000000..baf964e
--- /dev/null
+++ b/gio/gdbusobjectmanagerserver.h
@@ -0,0 +1,80 @@
+/* GDBus - GLib D-Bus Library
+ *
+ * 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
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#ifndef __G_DBUS_OBJECT_MANAGER_SERVER_H__
+#define __G_DBUS_OBJECT_MANAGER_SERVER_H__
+
+#include <gio/giotypes.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_DBUS_OBJECT_MANAGER_SERVER         (g_dbus_object_manager_server_get_type ())
+#define G_DBUS_OBJECT_MANAGER_SERVER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_OBJECT_MANAGER_SERVER, GDBusObjectManagerServer))
+#define G_DBUS_OBJECT_MANAGER_SERVER_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_OBJECT_MANAGER_SERVER, GDBusObjectManagerServerClass))
+#define G_DBUS_OBJECT_MANAGER_SERVER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_OBJECT_MANAGER_SERVER, GDBusObjectManagerServerClass))
+#define G_IS_DBUS_OBJECT_MANAGER_SERVER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_OBJECT_MANAGER_SERVER))
+#define G_IS_DBUS_OBJECT_MANAGER_SERVER_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_OBJECT_MANAGER_SERVER))
+
+typedef struct _GDBusObjectManagerServerClass   GDBusObjectManagerServerClass;
+typedef struct _GDBusObjectManagerServerPrivate GDBusObjectManagerServerPrivate;
+
+/**
+ * GDBusObjectManagerServer:
+  *
+ * The #GDBusObjectManagerServer structure contains private data and should
+ * only be accessed using the provided API.
+ */
+struct _GDBusObjectManagerServer
+{
+  /*< private >*/
+  GObject parent_instance;
+  GDBusObjectManagerServerPrivate *priv;
+};
+
+/**
+ * GDBusObjectManagerServerClass:
+ * @parent_class: The parent class.
+ *
+ * Class structure for #GDBusObjectManagerServer.
+ */
+struct _GDBusObjectManagerServerClass
+{
+  GObjectClass parent_class;
+
+  /*< private >*/
+  gpointer padding[8];
+};
+
+GType                     g_dbus_object_manager_server_get_type            (void) G_GNUC_CONST;
+GDBusObjectManagerServer *g_dbus_object_manager_server_new                 (GDBusConnection           *connection,
+                                                                            const gchar               *object_path);
+GDBusConnection          *g_dbus_object_manager_server_get_connection      (GDBusObjectManagerServer  *manager);
+void                      g_dbus_object_manager_server_export              (GDBusObjectManagerServer  *manager,
+                                                                            GDBusObjectStub           *object);
+void                      g_dbus_object_manager_server_export_and_uniquify (GDBusObjectManagerServer  *manager,
+                                                                            GDBusObjectStub           *object);
+void                      g_dbus_object_manager_server_unexport            (GDBusObjectManagerServer  *manager,
+                                                                            const gchar               *object_path);
+
+G_END_DECLS
+
+#endif /* __G_DBUS_OBJECT_MANAGER_SERVER_H */
diff --git a/gio/gdbusobjectproxy.c b/gio/gdbusobjectproxy.c
new file mode 100644
index 0000000..3c26495
--- /dev/null
+++ b/gio/gdbusobjectproxy.c
@@ -0,0 +1,315 @@
+/* GDBus - GLib D-Bus Library
+ *
+ * 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
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#include "config.h"
+
+#include "gdbusobject.h"
+#include "gdbusobjectproxy.h"
+#include "gdbusconnection.h"
+#include "gdbusprivate.h"
+#include "gdbusutils.h"
+#include "gdbusproxy.h"
+
+#include "glibintl.h"
+
+/**
+ * SECTION:gdbusobjectproxy
+ * @short_description: Client-side D-Bus object
+ * @include: gio/gio.h
+ *
+ * A #GDBusObjectProxy is an object used to represent a remote object
+ * with one or more D-Bus interfaces. You cannot instantiate a
+ * #GDBusObjectProxy yourself - you need to use a
+ * #GDBusObjectManagerClient to get one.
+ */
+
+struct _GDBusObjectProxyPrivate
+{
+  GHashTable *map_name_to_iface;
+  gchar *object_path;
+  GDBusConnection *connection;
+};
+
+enum
+{
+  PROP_0,
+  PROP_OBJECT_PATH,
+  PROP_CONNECTION
+};
+
+static void dbus_object_interface_init (GDBusObjectIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GDBusObjectProxy, g_dbus_object_proxy, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_DBUS_OBJECT, dbus_object_interface_init));
+
+static void
+g_dbus_object_proxy_finalize (GObject *object)
+{
+  GDBusObjectProxy *proxy = G_DBUS_OBJECT_PROXY (object);
+
+  g_hash_table_unref (proxy->priv->map_name_to_iface);
+
+  if (G_OBJECT_CLASS (g_dbus_object_proxy_parent_class)->finalize != NULL)
+    G_OBJECT_CLASS (g_dbus_object_proxy_parent_class)->finalize (object);
+}
+
+static void
+g_dbus_object_proxy_get_property (GObject    *object,
+                                  guint       prop_id,
+                                  GValue     *value,
+                                  GParamSpec *pspec)
+{
+  GDBusObjectProxy *proxy = G_DBUS_OBJECT_PROXY (object);
+
+  switch (prop_id)
+    {
+    case PROP_OBJECT_PATH:
+      g_value_set_string (value, proxy->priv->object_path);
+      break;
+
+    case PROP_CONNECTION:
+      g_value_set_object (value, g_dbus_object_proxy_get_connection (proxy));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (_object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+g_dbus_object_proxy_set_property (GObject       *object,
+                                  guint          prop_id,
+                                  const GValue  *value,
+                                  GParamSpec    *pspec)
+{
+  switch (prop_id)
+    {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (_object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+g_dbus_object_proxy_class_init (GDBusObjectProxyClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->finalize     = g_dbus_object_proxy_finalize;
+  gobject_class->set_property = g_dbus_object_proxy_set_property;
+  gobject_class->get_property = g_dbus_object_proxy_get_property;
+
+  /**
+   * GDBusObjectProxy:object-path:
+   *
+   * The object path of the proxy.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_OBJECT_PATH,
+                                   g_param_spec_string ("object-path",
+                                                        "Object Path",
+                                                        "The object path of the proxy",
+                                                        NULL,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GDBusObjectProxy:connection:
+   *
+   * The connection of the proxy.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_CONNECTION,
+                                   g_param_spec_string ("connection",
+                                                        "Connection",
+                                                        "The connection of the proxy",
+                                                        NULL,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_STATIC_STRINGS));
+
+  g_type_class_add_private (klass, sizeof (GDBusObjectProxyPrivate));
+}
+
+static void
+g_dbus_object_proxy_init (GDBusObjectProxy *proxy)
+{
+  proxy->priv = G_TYPE_INSTANCE_GET_PRIVATE (proxy,
+                                             G_TYPE_DBUS_OBJECT_PROXY,
+                                             GDBusObjectProxyPrivate);
+  proxy->priv->map_name_to_iface = g_hash_table_new_full (g_str_hash,
+                                                          g_str_equal,
+                                                          g_free,
+                                                          (GDestroyNotify) g_object_unref);
+}
+
+static const gchar *
+g_dbus_object_proxy_get_object_path (GDBusObject *object)
+{
+  GDBusObjectProxy *proxy = G_DBUS_OBJECT_PROXY (object);
+  return proxy->priv->object_path;
+}
+
+/**
+ * g_dbus_object_proxy_get_connection:
+ * @proxy: A #GDBusObjectProxy.
+ *
+ * Gets the connection that @proxy is for.
+ *
+ * Returns: A #GDBusConnection. Do not free, the object is owned by @proxy.
+ */
+GDBusConnection *
+g_dbus_object_proxy_get_connection (GDBusObjectProxy *proxy)
+{
+  g_return_val_if_fail (G_IS_DBUS_OBJECT_PROXY (proxy), NULL);
+  return proxy->priv->connection;
+}
+
+static GDBusInterface *
+g_dbus_object_proxy_get_interface (GDBusObject *object,
+                                   const gchar *interface_name)
+{
+  GDBusObjectProxy *proxy = G_DBUS_OBJECT_PROXY (object);
+  GDBusProxy *ret;
+
+  g_return_val_if_fail (G_IS_DBUS_OBJECT_PROXY (proxy), NULL);
+  g_return_val_if_fail (g_dbus_is_interface_name (interface_name), NULL);
+
+  ret = g_hash_table_lookup (proxy->priv->map_name_to_iface, interface_name);
+  if (ret != NULL)
+    g_object_ref (ret);
+  return (GDBusInterface *) ret; /* TODO: proper cast */
+}
+
+static GList *
+g_dbus_object_proxy_get_interfaces (GDBusObject *object)
+{
+  GDBusObjectProxy *proxy = G_DBUS_OBJECT_PROXY (object);
+  GList *ret;
+  GHashTableIter iter;
+  GDBusProxy *interface_proxy;
+
+  g_return_val_if_fail (G_IS_DBUS_OBJECT_PROXY (proxy), NULL);
+
+  ret = NULL;
+
+  g_hash_table_iter_init (&iter, proxy->priv->map_name_to_iface);
+  while (g_hash_table_iter_next (&iter, NULL, (gpointer) &interface_proxy))
+    ret = g_list_prepend (ret, g_object_ref (interface_proxy));
+
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+GDBusObjectProxy *
+_g_dbus_object_proxy_new (GDBusConnection *connection,
+                          const gchar *object_path)
+{
+  GDBusObjectProxy *proxy;
+
+  g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
+  g_return_val_if_fail (g_variant_is_object_path (object_path), NULL);
+
+  proxy = G_DBUS_OBJECT_PROXY (g_object_new (G_TYPE_DBUS_OBJECT_PROXY, NULL));
+  proxy->priv->object_path = g_strdup (object_path);
+  proxy->priv->connection = g_object_ref (connection);
+  return proxy;
+}
+
+void
+_g_dbus_object_proxy_add_interface (GDBusObjectProxy *proxy,
+                                    GDBusProxy       *interface_proxy)
+{
+  const gchar *interface_name;
+
+  g_return_if_fail (G_IS_DBUS_OBJECT_PROXY (proxy));
+  g_return_if_fail (G_IS_DBUS_PROXY (interface_proxy));
+
+  interface_name = g_dbus_proxy_get_interface_name (interface_proxy);
+  _g_dbus_object_proxy_remove_interface (proxy, interface_name);
+  g_hash_table_insert (proxy->priv->map_name_to_iface,
+                       g_strdup (interface_name),
+                       g_object_ref (interface_proxy));
+  g_signal_emit_by_name (proxy, "interface-added", interface_proxy);
+}
+
+void
+_g_dbus_object_proxy_remove_interface (GDBusObjectProxy *proxy,
+                                       const gchar      *interface_name)
+{
+  GDBusProxy *interface_proxy;
+
+  g_return_if_fail (G_IS_DBUS_OBJECT_PROXY (proxy));
+  g_return_if_fail (g_dbus_is_interface_name (interface_name));
+
+  interface_proxy = g_hash_table_lookup (proxy->priv->map_name_to_iface, interface_name);
+  if (interface_proxy != NULL)
+    {
+      g_object_ref (interface_proxy);
+      g_warn_if_fail (g_hash_table_remove (proxy->priv->map_name_to_iface, interface_name));
+      g_signal_emit_by_name (proxy, "interface-removed", interface_proxy);
+      g_object_unref (interface_proxy);
+    }
+}
+
+static gpointer
+g_dbus_object_proxy_lookup_with_typecheck (GDBusObject *object,
+                                           const gchar *interface_name,
+                                           GType        type)
+{
+  GDBusObjectProxy *proxy = G_DBUS_OBJECT_PROXY (object);
+  GDBusProxy *ret;
+
+  g_return_val_if_fail (G_IS_DBUS_OBJECT_PROXY (proxy), NULL);
+  g_return_val_if_fail (g_dbus_is_interface_name (interface_name), NULL);
+
+  ret = g_hash_table_lookup (proxy->priv->map_name_to_iface, interface_name);
+  if (ret != NULL)
+    {
+      g_warn_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (ret, type));
+      g_object_ref (ret);
+    }
+  return ret;
+}
+
+static gpointer
+g_dbus_object_proxy_peek_with_typecheck (GDBusObject  *object,
+                                         const gchar  *interface_name,
+                                         GType         type)
+{
+  GDBusProxy *ret;
+  ret = g_dbus_object_proxy_lookup_with_typecheck (object, interface_name, type);
+  if (ret != NULL)
+    g_object_unref (ret);
+  return ret;
+}
+
+static void
+dbus_object_interface_init (GDBusObjectIface *iface)
+{
+  iface->get_object_path       = g_dbus_object_proxy_get_object_path;
+  iface->get_interfaces        = g_dbus_object_proxy_get_interfaces;
+  iface->get_interface         = g_dbus_object_proxy_get_interface;
+  iface->peek_with_typecheck   = g_dbus_object_proxy_peek_with_typecheck;
+  iface->lookup_with_typecheck = g_dbus_object_proxy_lookup_with_typecheck;
+}
diff --git a/gio/gdbusobjectproxy.h b/gio/gdbusobjectproxy.h
new file mode 100644
index 0000000..d5cd619
--- /dev/null
+++ b/gio/gdbusobjectproxy.h
@@ -0,0 +1,72 @@
+/* GDBus - GLib D-Bus Library
+ *
+ * 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
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#ifndef __G_DBUS_OBJECT_PROXY_H__
+#define __G_DBUS_OBJECT_PROXY_H__
+
+#include <gio/giotypes.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_DBUS_OBJECT_PROXY         (g_dbus_object_proxy_get_type ())
+#define G_DBUS_OBJECT_PROXY(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_OBJECT_PROXY, GDBusObjectProxy))
+#define G_DBUS_OBJECT_PROXY_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_OBJECT_PROXY, GDBusObjectProxyClass))
+#define G_DBUS_OBJECT_PROXY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_OBJECT_PROXY, GDBusObjectProxyClass))
+#define G_IS_DBUS_OBJECT_PROXY(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_OBJECT_PROXY))
+#define G_IS_DBUS_OBJECT_PROXY_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_OBJECT_PROXY))
+
+typedef struct _GDBusObjectProxyClass   GDBusObjectProxyClass;
+typedef struct _GDBusObjectProxyPrivate GDBusObjectProxyPrivate;
+
+/**
+ * GDBusObjectProxy:
+ *
+ * The #GDBusObjectProxy structure contains private data and should
+ * only be accessed using the provided API.
+ */
+struct _GDBusObjectProxy
+{
+  /*< private >*/
+  GObject parent_instance;
+  GDBusObjectProxyPrivate *priv;
+};
+
+/**
+ * GDBusObjectProxyClass:
+ * @parent_class: The parent class.
+ *
+ * Class structure for #GDBusObjectProxy.
+ */
+struct _GDBusObjectProxyClass
+{
+  GObjectClass parent_class;
+
+  /*< private >*/
+  gpointer padding[8];
+};
+
+GType            g_dbus_object_proxy_get_type         (void) G_GNUC_CONST;
+GDBusConnection *g_dbus_object_proxy_get_connection   (GDBusObjectProxy *proxy);
+
+G_END_DECLS
+
+#endif /* __G_DBUS_OBJECT_PROXY_H */
diff --git a/gio/gdbusobjectstub.c b/gio/gdbusobjectstub.c
new file mode 100644
index 0000000..7b60db5
--- /dev/null
+++ b/gio/gdbusobjectstub.c
@@ -0,0 +1,475 @@
+/* GDBus - GLib D-Bus Library
+ *
+ * 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
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#include "config.h"
+
+#include "gdbusobject.h"
+#include "gdbusobjectstub.h"
+#include "gdbusinterfacestub.h"
+#include "gio-marshal.h"
+#include "gdbusprivate.h"
+#include "gdbusmethodinvocation.h"
+#include "gdbusintrospection.h"
+#include "gdbusinterface.h"
+#include "gdbusutils.h"
+
+#include "glibintl.h"
+
+/**
+ * SECTION:gdbusobjectstub
+ * @short_description: Service-side D-Bus object
+ * @include: gio/gio.h
+ *
+ * A #GDBusObjectStub instance is essentially a group of D-Bus
+ * interfaces. The set of exported interfaces on the object may be
+ * dynamic and change at runtime.
+ *
+ * This type is intended to be used with #GDBusObjectManager.
+ */
+
+struct _GDBusObjectStubPrivate
+{
+  gchar *object_path;
+  GHashTable *map_name_to_iface;
+};
+
+enum
+{
+  PROP_0,
+  PROP_OBJECT_PATH
+};
+
+enum
+{
+  AUTHORIZE_METHOD_SIGNAL,
+  LAST_SIGNAL,
+};
+
+static guint signals[LAST_SIGNAL] = {0};
+
+static void dbus_object_interface_init (GDBusObjectIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GDBusObjectStub, g_dbus_object_stub, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_DBUS_OBJECT, dbus_object_interface_init));
+
+
+static void
+g_dbus_object_stub_finalize (GObject *_object)
+{
+  GDBusObjectStub *object = G_DBUS_OBJECT_STUB (_object);
+
+  g_free (object->priv->object_path);
+  g_hash_table_unref (object->priv->map_name_to_iface);
+
+  if (G_OBJECT_CLASS (g_dbus_object_stub_parent_class)->finalize != NULL)
+    G_OBJECT_CLASS (g_dbus_object_stub_parent_class)->finalize (_object);
+}
+
+static void
+g_dbus_object_stub_get_property (GObject    *_object,
+                                 guint       prop_id,
+                                 GValue     *value,
+                                 GParamSpec *pspec)
+{
+  GDBusObjectStub *object = G_DBUS_OBJECT_STUB (_object);
+
+  switch (prop_id)
+    {
+    case PROP_OBJECT_PATH:
+      g_value_take_string (value, object->priv->object_path);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (_object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+g_dbus_object_stub_set_property (GObject       *_object,
+                                 guint          prop_id,
+                                 const GValue  *value,
+                                 GParamSpec    *pspec)
+{
+  GDBusObjectStub *object = G_DBUS_OBJECT_STUB (_object);
+
+  switch (prop_id)
+    {
+    case PROP_OBJECT_PATH:
+      g_dbus_object_stub_set_object_path (object, g_value_get_string (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (_object, prop_id, pspec);
+      break;
+    }
+}
+
+static gboolean
+g_dbus_object_stub_authorize_method_default (GDBusObjectStub       *object,
+                                             GDBusInterfaceStub    *interface,
+                                             GDBusMethodInvocation *invocation)
+{
+  return TRUE;
+}
+
+static void
+g_dbus_object_stub_class_init (GDBusObjectStubClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->finalize     = g_dbus_object_stub_finalize;
+  gobject_class->set_property = g_dbus_object_stub_set_property;
+  gobject_class->get_property = g_dbus_object_stub_get_property;
+
+  klass->authorize_method = g_dbus_object_stub_authorize_method_default;
+
+  /**
+   * GDBusObjectStub:object-path:
+   *
+   * The object path where the object is exported.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_OBJECT_PATH,
+                                   g_param_spec_string ("object-path",
+                                                        "Object Path",
+                                                        "The object path where the object is exported",
+                                                        NULL,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_WRITABLE |
+                                                        G_PARAM_CONSTRUCT |
+                                                        G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GDBusObjectStub::authorize-method:
+   * @object: The #GDBusObjectStub emitting the signal.
+   * @interface: The #GDBusInterfaceStub that @invocation is on.
+   * @invocation: A #GDBusMethodInvocation.
+   *
+   * Emitted when a method is invoked by a remote caller and used to
+   * determine if the method call is authorized.
+   *
+   * This signal is like #GDBusInterfaceStub<!-- -->'s
+   * #GDBusInterfaceStub::g-authorize-method signal, except that it is
+   * for the enclosing object.
+   *
+   * The default class handler just returns %TRUE.
+   *
+   * Returns: %TRUE if the call is authorized, %FALSE otherwise.
+   */
+  signals[AUTHORIZE_METHOD_SIGNAL] =
+    g_signal_new ("authorize-method",
+                  G_TYPE_DBUS_OBJECT_STUB,
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (GDBusObjectStubClass, authorize_method),
+                  _g_signal_accumulator_false_handled,
+                  NULL,
+                  _gio_marshal_BOOLEAN__OBJECT_OBJECT,
+                  G_TYPE_BOOLEAN,
+                  2,
+                  G_TYPE_DBUS_INTERFACE_STUB,
+                  G_TYPE_DBUS_METHOD_INVOCATION);
+
+  g_type_class_add_private (klass, sizeof (GDBusObjectStubPrivate));
+}
+
+static void
+g_dbus_object_stub_init (GDBusObjectStub *object)
+{
+  object->priv = G_TYPE_INSTANCE_GET_PRIVATE (object, G_TYPE_DBUS_OBJECT_STUB, GDBusObjectStubPrivate);
+  object->priv->map_name_to_iface = g_hash_table_new_full (g_str_hash,
+                                                           g_str_equal,
+                                                           g_free,
+                                                           (GDestroyNotify) g_object_unref);
+}
+
+/**
+ * g_dbus_object_stub_new:
+ * @object_path: An object path.
+ *
+ * Creates a new #GDBusObjectStub.
+ *
+ * Returns: A #GDBusObjectStub. Free with g_object_unref().
+ */
+GDBusObjectStub *
+g_dbus_object_stub_new (const gchar *object_path)
+{
+  g_return_val_if_fail (g_variant_is_object_path (object_path), NULL);
+  return G_DBUS_OBJECT_STUB (g_object_new (G_TYPE_DBUS_OBJECT_STUB,
+                                           "object-path", object_path,
+                                           NULL));
+}
+
+/**
+ * g_dbus_object_stub_set_object_path:
+ * @object: A #GDBusObjectStub.
+ * @object_path: A valid D-Bus object path.
+ *
+ * Sets the object path for @object.
+ */
+void
+g_dbus_object_stub_set_object_path (GDBusObjectStub *object,
+                                    const gchar     *object_path)
+{
+  g_return_if_fail (G_IS_DBUS_OBJECT_STUB (object));
+  g_return_if_fail (object_path == NULL || g_variant_is_object_path (object_path));
+  /* TODO: fail if object is currently exported */
+  if (g_strcmp0 (object->priv->object_path, object_path) != 0)
+    {
+      g_free (object->priv->object_path);
+      object->priv->object_path = g_strdup (object_path);
+      g_object_notify (G_OBJECT (object), "object-path");
+    }
+}
+
+static const gchar *
+g_dbus_object_stub_get_object_path (GDBusObject *_object)
+{
+  GDBusObjectStub *object = G_DBUS_OBJECT_STUB (_object);
+  return object->priv->object_path;
+}
+
+/**
+ * g_dbus_object_stub_add_interface:
+ * @object: A #GDBusObjectStub.
+ * @interface: A #GDBusInterfaceStub.
+ *
+ * Adds @interface to @object.
+ *
+ * If @object already contains a #GDBusInterfaceStub with the same
+ * interface name, it is removed before @interface is added.
+ *
+ * Note that @object takes its own reference on @interface and holds
+ * it until removed.
+ */
+void
+g_dbus_object_stub_add_interface (GDBusObjectStub     *object,
+                                  GDBusInterfaceStub  *interface)
+{
+  GDBusInterfaceInfo *info;
+
+  g_return_if_fail (G_IS_DBUS_OBJECT_STUB (object));
+  g_return_if_fail (G_IS_DBUS_INTERFACE_STUB (interface));
+
+  info = g_dbus_interface_stub_get_info (interface);
+  g_object_ref (interface);
+  g_dbus_object_stub_remove_interface_by_name (object, info->name);
+  g_hash_table_insert (object->priv->map_name_to_iface,
+                       g_strdup (info->name),
+                       interface);
+  g_dbus_interface_set_object (G_DBUS_INTERFACE (interface), G_DBUS_OBJECT (object));
+  g_signal_emit_by_name (object,
+                         "interface-added",
+                         interface);
+}
+
+/**
+ * g_dbus_object_stub_remove_interface:
+ * @object: A #GDBusObjectStub.
+ * @interface: A #GDBusInterfaceStub.
+ *
+ * Removes @interface from @object.
+ */
+void
+g_dbus_object_stub_remove_interface  (GDBusObjectStub    *object,
+                                      GDBusInterfaceStub *interface)
+{
+  GDBusInterfaceStub *other_interface;
+  GDBusInterfaceInfo *info;
+
+  g_return_if_fail (G_IS_DBUS_OBJECT_STUB (object));
+  g_return_if_fail (G_IS_DBUS_INTERFACE (interface));
+
+  info = g_dbus_interface_stub_get_info (interface);
+
+  other_interface = g_hash_table_lookup (object->priv->map_name_to_iface, info->name);
+  if (other_interface == NULL)
+    {
+      g_warning ("Tried to remove interface with name %s from object "
+                 "at path %s but no such interface exists",
+                 info->name,
+                 object->priv->object_path);
+    }
+  else if (other_interface != interface)
+    {
+      g_warning ("Tried to remove interface %p with name %s from object "
+                 "at path %s but the object has the interface %p",
+                 interface,
+                 info->name,
+                 object->priv->object_path,
+                 other_interface);
+    }
+  else
+    {
+      g_object_ref (interface);
+      g_warn_if_fail (g_hash_table_remove (object->priv->map_name_to_iface, info->name));
+      g_dbus_interface_set_object (G_DBUS_INTERFACE (interface), NULL);
+      g_signal_emit_by_name (object,
+                             "interface-removed",
+                             interface);
+      g_object_unref (interface);
+    }
+}
+
+
+/**
+ * g_dbus_object_stub_remove_interface_by_name:
+ * @object: A #GDBusObjectStub.
+ * @interface_name: A D-Bus interface name.
+ *
+ * Removes the #GDBusInterface with @interface_name from @object.
+ *
+ * If no D-Bus interface of the given interface exists, this function
+ * does nothing.
+ */
+void
+g_dbus_object_stub_remove_interface_by_name (GDBusObjectStub *object,
+                                             const gchar     *interface_name)
+{
+  GDBusInterface *interface;
+
+  g_return_if_fail (G_IS_DBUS_OBJECT_STUB (object));
+  g_return_if_fail (g_dbus_is_interface_name (interface_name));
+
+  interface = g_hash_table_lookup (object->priv->map_name_to_iface, interface_name);
+  if (interface != NULL)
+    {
+      g_object_ref (interface);
+      g_warn_if_fail (g_hash_table_remove (object->priv->map_name_to_iface, interface_name));
+      g_dbus_interface_set_object (interface, NULL);
+      g_signal_emit_by_name (object,
+                             "interface-removed",
+                             interface);
+      g_object_unref (interface);
+    }
+}
+
+static GDBusInterface *
+g_dbus_object_stub_get_interface (GDBusObject *_object,
+                                  const gchar  *interface_name)
+{
+  GDBusObjectStub *object = G_DBUS_OBJECT_STUB (_object);
+  GDBusInterface *ret;
+
+  g_return_val_if_fail (G_IS_DBUS_OBJECT_STUB (object), NULL);
+  g_return_val_if_fail (g_dbus_is_interface_name (interface_name), NULL);
+
+  ret = g_hash_table_lookup (object->priv->map_name_to_iface, interface_name);
+  if (ret != NULL)
+    g_object_ref (ret);
+  return ret;
+}
+
+static GList *
+g_dbus_object_stub_get_interfaces (GDBusObject *_object)
+{
+  GDBusObjectStub *object = G_DBUS_OBJECT_STUB (_object);
+  GList *ret;
+  GHashTableIter iter;
+  GDBusInterface *interface;
+
+  g_return_val_if_fail (G_IS_DBUS_OBJECT_STUB (object), NULL);
+
+  ret = NULL;
+
+  g_hash_table_iter_init (&iter, object->priv->map_name_to_iface);
+  while (g_hash_table_iter_next (&iter, NULL, (gpointer) &interface))
+    ret = g_list_prepend (ret, g_object_ref (interface));
+
+  return ret;
+}
+
+/**
+ * g_dbus_object_stub_flush:
+ * @object: A #GDBusObjectStub.
+ *
+ * This method simply calls g_dbus_interface_stub_flush() on all
+ * interfaces stubs belonging to @object. See that method for when
+ * flushing is useful.
+ */
+void
+g_dbus_object_stub_flush (GDBusObjectStub *object)
+{
+  GHashTableIter iter;
+  GDBusInterfaceStub *interface_stub;
+
+  g_hash_table_iter_init (&iter, object->priv->map_name_to_iface);
+  while (g_hash_table_iter_next (&iter, NULL, (gpointer) &interface_stub))
+    {
+      g_dbus_interface_stub_flush (interface_stub);
+    }
+}
+
+static gpointer
+g_dbus_object_stub_lookup_with_typecheck (GDBusObject *object,
+                                          const gchar *interface_name,
+                                          GType        type)
+{
+  GDBusObjectStub *stub = G_DBUS_OBJECT_STUB (object);
+  GDBusProxy *ret;
+
+  ret = g_hash_table_lookup (stub->priv->map_name_to_iface, interface_name);
+  if (ret != NULL)
+    {
+      g_warn_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (ret, type));
+      g_object_ref (ret);
+    }
+  return ret;
+}
+
+static gpointer
+g_dbus_object_stub_peek_with_typecheck   (GDBusObject *object,
+                                          const gchar *interface_name,
+                                          GType        type)
+{
+  GDBusInterfaceStub *ret;
+  ret = g_dbus_object_stub_lookup_with_typecheck (object, interface_name, type);
+  if (ret != NULL)
+    g_object_unref (ret);
+  return ret;
+}
+
+static void
+dbus_object_interface_init (GDBusObjectIface *iface)
+{
+  iface->get_object_path = g_dbus_object_stub_get_object_path;
+  iface->get_interfaces  = g_dbus_object_stub_get_interfaces;
+  iface->get_interface  = g_dbus_object_stub_get_interface;
+  iface->lookup_with_typecheck = g_dbus_object_stub_lookup_with_typecheck;
+  iface->peek_with_typecheck = g_dbus_object_stub_peek_with_typecheck;
+}
+
+gboolean
+_g_dbus_object_stub_has_authorize_method_handlers (GDBusObjectStub *stub)
+{
+  gboolean has_handlers;
+  gboolean has_default_class_handler;
+
+  has_handlers = g_signal_has_handler_pending (stub,
+                                               signals[AUTHORIZE_METHOD_SIGNAL],
+                                               0,
+                                               TRUE);
+  has_default_class_handler = (G_DBUS_OBJECT_STUB_GET_CLASS (stub)->authorize_method ==
+                               g_dbus_object_stub_authorize_method_default);
+
+  return has_handlers || !has_default_class_handler;
+}
diff --git a/gio/gdbusobjectstub.h b/gio/gdbusobjectstub.h
new file mode 100644
index 0000000..e5ebfc4
--- /dev/null
+++ b/gio/gdbusobjectstub.h
@@ -0,0 +1,87 @@
+/* GDBus - GLib D-Bus Library
+ *
+ * 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
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#ifndef __G_DBUS_OBJECT_STUB_H__
+#define __G_DBUS_OBJECT_STUB_H__
+
+#include <gio/giotypes.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_DBUS_OBJECT_STUB         (g_dbus_object_stub_get_type ())
+#define G_DBUS_OBJECT_STUB(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_OBJECT_STUB, GDBusObjectStub))
+#define G_DBUS_OBJECT_STUB_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_OBJECT_STUB, GDBusObjectStubClass))
+#define G_DBUS_OBJECT_STUB_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_OBJECT_STUB, GDBusObjectStubClass))
+#define G_IS_DBUS_OBJECT_STUB(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_OBJECT_STUB))
+#define G_IS_DBUS_OBJECT_STUB_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_OBJECT_STUB))
+
+typedef struct _GDBusObjectStubClass   GDBusObjectStubClass;
+typedef struct _GDBusObjectStubPrivate GDBusObjectStubPrivate;
+
+/**
+ * GDBusObjectStub:
+  *
+ * The #GDBusObjectStub structure contains private data and should only be
+ * accessed using the provided API.
+ */
+struct _GDBusObjectStub
+{
+  /*< private >*/
+  GObject parent_instance;
+  GDBusObjectStubPrivate *priv;
+};
+
+/**
+ * GDBusObjectStubClass:
+ * @parent_class: The parent class.
+ * @authorize_method: Signal class handler for the #GDBusObjectStub::authorize-method signal.
+ *
+ * Class structure for #GDBusObjectStub.
+ */
+struct _GDBusObjectStubClass
+{
+  GObjectClass parent_class;
+
+  /* Signals */
+  gboolean (*authorize_method) (GDBusObjectStub       *stub,
+                                GDBusInterfaceStub    *interface_stub,
+                                GDBusMethodInvocation *invocation);
+
+  /*< private >*/
+  gpointer padding[8];
+};
+
+GType                g_dbus_object_stub_get_type                  (void) G_GNUC_CONST;
+GDBusObjectStub     *g_dbus_object_stub_new                       (const gchar        *object_path);
+void                 g_dbus_object_stub_flush                     (GDBusObjectStub    *object);
+void                 g_dbus_object_stub_add_interface             (GDBusObjectStub    *object,
+                                                                   GDBusInterfaceStub *interface);
+void                 g_dbus_object_stub_remove_interface          (GDBusObjectStub    *object,
+                                                                   GDBusInterfaceStub *interface);
+void                 g_dbus_object_stub_remove_interface_by_name  (GDBusObjectStub    *object,
+                                                                   const gchar        *interface_name);
+void                 g_dbus_object_stub_set_object_path           (GDBusObjectStub    *object,
+                                                                   const gchar        *object_path);
+
+G_END_DECLS
+
+#endif /* __G_DBUS_OBJECT_STUB_H */
diff --git a/gio/gdbusprivate.c b/gio/gdbusprivate.c
index 9c2c58c..1e5bcf8 100644
--- a/gio/gdbusprivate.c
+++ b/gio/gdbusprivate.c
@@ -1869,3 +1869,21 @@ read_message_print_transport_debug (gssize bytes_read,
  out:
   ;
 }
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+gboolean
+_g_signal_accumulator_false_handled (GSignalInvocationHint *ihint,
+                                     GValue                *return_accu,
+                                     const GValue          *handler_return,
+                                     gpointer               dummy)
+{
+  gboolean continue_emission;
+  gboolean signal_return;
+
+  signal_return = g_value_get_boolean (handler_return);
+  g_value_set_boolean (return_accu, signal_return);
+  continue_emission = signal_return;
+
+  return continue_emission;
+}
diff --git a/gio/gdbusprivate.h b/gio/gdbusprivate.h
index 659e9d4..a39c192 100644
--- a/gio/gdbusprivate.h
+++ b/gio/gdbusprivate.h
@@ -112,8 +112,6 @@ gchar *_g_dbus_get_machine_id (GError **error);
 
 gchar *_g_dbus_enum_to_string (GType enum_type, gint value);
 
-G_END_DECLS
-
 /* ---------------------------------------------------------------------------------------------------- */
 
 GDBusMethodInvocation *_g_dbus_method_invocation_new (const gchar           *sender,
@@ -126,4 +124,22 @@ GDBusMethodInvocation *_g_dbus_method_invocation_new (const gchar           *sen
                                                       GVariant              *parameters,
                                                       gpointer               user_data);
 
+/* ---------------------------------------------------------------------------------------------------- */
+
+gboolean _g_signal_accumulator_false_handled (GSignalInvocationHint *ihint,
+                                              GValue                *return_accu,
+                                              const GValue          *handler_return,
+                                              gpointer               dummy);
+
+gboolean _g_dbus_object_stub_has_authorize_method_handlers (GDBusObjectStub *stub);
+
+GDBusObjectProxy *_g_dbus_object_proxy_new (GDBusConnection *connection,
+                                            const gchar *object_path);
+void _g_dbus_object_proxy_add_interface (GDBusObjectProxy *proxy,
+                                         GDBusProxy       *interface_proxy);
+void _g_dbus_object_proxy_remove_interface (GDBusObjectProxy *proxy,
+                                            const gchar      *interface_name);
+
+G_END_DECLS
+
 #endif /* __G_DBUS_PRIVATE_H__ */
diff --git a/gio/gdbusproxy.c b/gio/gdbusproxy.c
index 67af1fb..14cb2ba 100644
--- a/gio/gdbusproxy.c
+++ b/gio/gdbusproxy.c
@@ -38,6 +38,7 @@
 #include "gasyncresult.h"
 #include "gsimpleasyncresult.h"
 #include "gcancellable.h"
+#include "gdbusinterface.h"
 
 #include "glibintl.h"
 
@@ -129,10 +130,12 @@ enum
 
 guint signals[LAST_SIGNAL] = {0};
 
+static void dbus_interface_iface_init (GDBusInterfaceIface *dbus_interface_iface);
 static void initable_iface_init       (GInitableIface *initable_iface);
 static void async_initable_iface_init (GAsyncInitableIface *async_initable_iface);
 
 G_DEFINE_TYPE_WITH_CODE (GDBusProxy, g_dbus_proxy, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_DBUS_INTERFACE, dbus_interface_iface_init)
                          G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)
                          G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init)
                          );
@@ -2548,3 +2551,36 @@ g_dbus_proxy_call_sync (GDBusProxy      *proxy,
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
+/* Hack until this is merged into libgio (extending types at run-time isn't really safe in any way) */
+
+static GDBusInterfaceInfo *
+_g_dbus_proxy_get_info (GDBusInterface *interface)
+{
+  GDBusProxy *proxy = G_DBUS_PROXY (interface);
+  return g_dbus_proxy_get_interface_info (proxy);
+}
+
+static GDBusObject *
+_g_dbus_proxy_get_object (GDBusInterface *interface)
+{
+  /* TODO */
+  return g_object_get_data (G_OBJECT (interface), "-x-gdbus-binding-tool-object");
+}
+
+static void
+_g_dbus_proxy_set_object (GDBusInterface *interface,
+                          GDBusObject    *object)
+{
+  /* TODO */
+  g_object_set_data (G_OBJECT (interface), "-x-gdbus-binding-tool-object", object);
+}
+
+static void
+dbus_interface_iface_init (GDBusInterfaceIface *dbus_interface_iface)
+{
+  dbus_interface_iface->get_info   = _g_dbus_proxy_get_info;
+  dbus_interface_iface->get_object = _g_dbus_proxy_get_object;
+  dbus_interface_iface->set_object = _g_dbus_proxy_set_object;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
diff --git a/gio/gio-marshal.list b/gio/gio-marshal.list
index 8a42ad7..ad6d185 100644
--- a/gio/gio-marshal.list
+++ b/gio/gio-marshal.list
@@ -29,3 +29,6 @@ VOID:UINT64
 BOOLEAN:FLAGS
 BOOLEAN:OBJECT,FLAGS
 OBJECT:VOID
+VOID:OBJECT,OBJECT
+VOID:OBJECT,OBJECT,STRING,STRING,VARIANT
+VOID:OBJECT,OBJECT,VARIANT,BOXED
diff --git a/gio/gio.h b/gio/gio.h
index 288da44..70cfe9f 100644
--- a/gio/gio.h
+++ b/gio/gio.h
@@ -130,6 +130,14 @@
 #include <gio/gvolumemonitor.h>
 #include <gio/gzlibcompressor.h>
 #include <gio/gzlibdecompressor.h>
+#include <gio/gdbusinterface.h>
+#include <gio/gdbusinterfacestub.h>
+#include <gio/gdbusobject.h>
+#include <gio/gdbusobjectstub.h>
+#include <gio/gdbusobjectproxy.h>
+#include <gio/gdbusobjectmanager.h>
+#include <gio/gdbusobjectmanagerclient.h>
+#include <gio/gdbusobjectmanagerserver.h>
 
 #undef __GIO_GIO_H_INSIDE__
 
diff --git a/gio/gio.symbols b/gio/gio.symbols
index 64275ae..dcc5ac8 100644
--- a/gio/gio.symbols
+++ b/gio/gio.symbols
@@ -2086,3 +2086,99 @@ g_time_zone_monitor_get_type G_GNUC_CONST
 g_time_zone_monitor_get
 #endif
 #endif
+
+#if IN_HEADER(__G_DBUS_INTERFACE_H__)
+#if IN_FILE(__G_DBUS_INTERFACE_C__)
+g_dbus_interface_get_info
+g_dbus_interface_get_object
+g_dbus_interface_get_type
+g_dbus_interface_set_object
+g_dbus_gvalue_to_gvariant
+g_dbus_gvariant_to_gvalue
+#endif
+#endif
+
+#if IN_HEADER(__G_DBUS_INTERFACE_STUB_H__)
+#if IN_FILE(__G_DBUS_INTERFACE_STUB_C__)
+g_dbus_interface_stub_export
+g_dbus_interface_stub_flags_get_type
+g_dbus_interface_stub_flush
+g_dbus_interface_stub_get_connection
+g_dbus_interface_stub_get_flags
+g_dbus_interface_stub_get_info
+g_dbus_interface_stub_get_object_path
+g_dbus_interface_stub_get_properties
+g_dbus_interface_stub_get_type
+g_dbus_interface_stub_get_vtable
+g_dbus_interface_stub_set_flags
+g_dbus_interface_stub_unexport
+#endif
+#endif
+
+#if IN_HEADER(__G_DBUS_OBJECT_H__)
+#if IN_FILE(__G_DBUS_OBJECT_C__)
+g_dbus_object_get_interface
+g_dbus_object_get_interfaces
+g_dbus_object_get_object_path
+g_dbus_object_get_type
+g_dbus_object_lookup_with_typecheck
+g_dbus_object_peek_with_typecheck
+#endif
+#endif
+
+#if IN_HEADER(__G_DBUS_OBJECT_PROXY_H__)
+#if IN_FILE(__G_DBUS_OBJECT_PROXY_C__)
+g_dbus_object_proxy_get_connection
+g_dbus_object_proxy_get_type
+#endif
+#endif
+
+#if IN_HEADER(__G_DBUS_OBJECT_STUB_H__)
+#if IN_FILE(__G_DBUS_OBJECT_STUB_C__)
+g_dbus_object_stub_add_interface
+g_dbus_object_stub_flush
+g_dbus_object_stub_get_type
+g_dbus_object_stub_new
+g_dbus_object_stub_remove_interface
+g_dbus_object_stub_remove_interface_by_name
+g_dbus_object_stub_set_object_path
+#endif
+#endif
+
+#if IN_HEADER(__G_DBUS_OBJECT_MANAGER_H__)
+#if IN_FILE(__G_DBUS_OBJECT_MANAGER_C__)
+g_dbus_object_manager_get_interface
+g_dbus_object_manager_get_object
+g_dbus_object_manager_get_object_path
+g_dbus_object_manager_get_objects
+g_dbus_object_manager_get_type
+#endif
+#endif
+
+#if IN_HEADER(__G_DBUS_OBJECT_MANAGER_CLIENT_H__)
+#if IN_FILE(__G_DBUS_OBJECT_MANAGER_CLIENT_C__)
+g_dbus_object_manager_client_flags_get_type
+g_dbus_object_manager_client_get_connection
+g_dbus_object_manager_client_get_flags
+g_dbus_object_manager_client_get_name
+g_dbus_object_manager_client_get_name_owner
+g_dbus_object_manager_client_get_type
+g_dbus_object_manager_client_new
+g_dbus_object_manager_client_new_finish
+g_dbus_object_manager_client_new_for_bus
+g_dbus_object_manager_client_new_for_bus_finish
+g_dbus_object_manager_client_new_for_bus_sync
+g_dbus_object_manager_client_new_sync
+#endif
+#endif
+
+#if IN_HEADER(__G_DBUS_OBJECT_MANAGER_SERVER_H__)
+#if IN_FILE(__G_DBUS_OBJECT_MANAGER_SERVER_C__)
+g_dbus_object_manager_server_export
+g_dbus_object_manager_server_export_and_uniquify
+g_dbus_object_manager_server_get_connection
+g_dbus_object_manager_server_get_type
+g_dbus_object_manager_server_new
+g_dbus_object_manager_server_unexport
+#endif
+#endif
diff --git a/gio/gioenums.h b/gio/gioenums.h
index 0989799..8961914 100644
--- a/gio/gioenums.h
+++ b/gio/gioenums.h
@@ -1383,6 +1383,42 @@ typedef enum {
   G_TLS_REHANDSHAKE_UNSAFELY
 } GTlsRehandshakeMode;
 
+/**
+ * GDBusInterfaceStubFlags:
+ * @G_DBUS_INTERFACE_STUB_FLAGS_NONE: No flags set.
+ * @G_DBUS_INTERFACE_STUB_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD: Each method invocation is handled in
+ *   a thread dedicated to the invocation. This means that the method implementation can use blocking IO
+ *   without blocking any other part of the process. It also means that the method implementation must
+ *   use locking to access data structures used by other threads.
+ *
+ * Flags describing the behavior of a #GDBusInterfaceStub class.
+ *
+ * Since: 2.30
+ */
+typedef enum
+{
+  G_DBUS_INTERFACE_STUB_FLAGS_NONE = 0,
+  G_DBUS_INTERFACE_STUB_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD = (1<<0)
+} GDBusInterfaceStubFlags;
+
+/**
+ * GDBusObjectManagerClientFlags:
+ * @G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE: No flags set.
+ * @G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START: If not set and the
+ *   manager is for a well-known name, then request the bus to launch
+ *   an owner for the name if no-one owns the name. This flag can only
+ *   be used in managers for well-known names.
+ *
+ * Flags used when constructing a #GDBusObjectManagerClient.
+ *
+ * Since: 2.30
+ */
+typedef enum
+{
+  G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE = 0,
+  G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START = (1<<0),
+} GDBusObjectManagerClientFlags;
+
 G_END_DECLS
 
 #endif /* __GIO_ENUMS_H__ */
diff --git a/gio/giotypes.h b/gio/giotypes.h
index 1c35083..848336a 100644
--- a/gio/giotypes.h
+++ b/gio/giotypes.h
@@ -418,6 +418,39 @@ typedef gboolean (*GCancellableSourceFunc) (GCancellable *cancellable,
 typedef gboolean (*GPollableSourceFunc) (GObject  *pollable_stream,
 					 gpointer  user_data);
 
+typedef struct _GDBusInterface              GDBusInterface; /* Dummy typedef */
+typedef struct _GDBusInterfaceStub          GDBusInterfaceStub;
+typedef struct _GDBusObject                 GDBusObject;  /* Dummy typedef */
+typedef struct _GDBusObjectStub             GDBusObjectStub;
+typedef struct _GDBusObjectProxy            GDBusObjectProxy;
+typedef struct _GDBusObjectManager          GDBusObjectManager;  /* Dummy typedef */
+typedef struct _GDBusObjectManagerClient    GDBusObjectManagerClient;
+typedef struct _GDBusObjectManagerServer    GDBusObjectManagerServer;
+
+/**
+ * GDBusProxyTypeFunc:
+ * @manager: A #GDBusObjectManagerClient.
+ * @object_path: The object path of the remote object.
+ * @interface_name: The interface name of the remote object.
+ * @user_data: User data.
+ *
+ * Function signature for a function used to determine the #GType to
+ * use for an interface proxy.
+ *
+ * This function is called in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * that @manager was constructed in.
+ *
+ * Returns: A #GType to use for the remote object. The returned type
+ * must be a #GDBusProxy derived type.
+ *
+ * Since: 2.30
+ */
+typedef GType (*GDBusProxyTypeFunc) (GDBusObjectManagerClient   *manager,
+                                     const gchar                *object_path,
+                                     const gchar                *interface_name,
+                                     gpointer                    user_data);
+
 G_END_DECLS
 
 #endif /* __GIO_TYPES_H__ */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]