gvfs r1815 - in trunk: . hal monitor monitor/proxy programs



Author: davidz
Date: Tue Jul  8 16:36:45 2008
New Revision: 1815
URL: http://svn.gnome.org/viewvc/gvfs?rev=1815&view=rev

Log:
2008-07-08  David Zeuthen  <davidz redhat com>

        Provide infrastructure for out of process volume monitors and
        port the hal volume monitor to use it (#520132).

        * Makefile.am:
        * configure.ac:
        Add the monitor and monitor/proxy directories.

        * hal/Makefile.am:
        Don't build a gio module for the hal volume monitor; instead
        build a volume monitor daemon.

        * hal/ghaldrive.[ch]:
        * hal/ghalmount.[ch]:
        * hal/ghalvolume.[ch]:
        * hal/ghalvolumemonitor.[ch]:
        * hal/hal-device.[ch]:
        * hal/hal-device.[ch]:
        * hal/hal-pool.[ch]:
        Make all types static and implement g_volume_get_activation_root()
        added to gio (#541793). Also emit the drive-eject-button
        signal (#541794).

        * hal/hal-module.c:
        Removed since the monitor is being moved out of process.

        * hal/hal-volume-monitor-daemon.c:
        * hal/hal.monitor:
        * hal/org.gtk.Private.HalVolumeMonitor.service.in:
        New files for remote volume monitor.

        * monitor/Makefile.am:
        New file.

        * monitor/proxy/*:
        Add proxy volume monitor gio module (the D-Bus client side of
        out-of-process volume monitors) and a static library for providing
        the D-Bus server side of out of process volume monitors.

        * programs/gvfs-mount.c:
        Print activation uri for a volumes and icons for drives.
        Also unref volume monitor when no longer in use.



Added:
   trunk/hal/hal-volume-monitor-daemon.c
   trunk/hal/hal.monitor
   trunk/hal/org.gtk.Private.HalVolumeMonitor.service.in
   trunk/monitor/
   trunk/monitor/Makefile.am
   trunk/monitor/proxy/
   trunk/monitor/proxy/Makefile.am
   trunk/monitor/proxy/gproxydrive.c
   trunk/monitor/proxy/gproxydrive.h
   trunk/monitor/proxy/gproxymount.c
   trunk/monitor/proxy/gproxymount.h
   trunk/monitor/proxy/gproxyvolume.c
   trunk/monitor/proxy/gproxyvolume.h
   trunk/monitor/proxy/gproxyvolumemonitor.c
   trunk/monitor/proxy/gproxyvolumemonitor.h
   trunk/monitor/proxy/gvfsproxyvolumemonitordaemon.c
   trunk/monitor/proxy/gvfsproxyvolumemonitordaemon.h
   trunk/monitor/proxy/remote-volume-monitor-module.c
Removed:
   trunk/hal/hal-module.c
Modified:
   trunk/ChangeLog
   trunk/Makefile.am
   trunk/configure.ac
   trunk/hal/Makefile.am
   trunk/hal/ghaldrive.c
   trunk/hal/ghaldrive.h
   trunk/hal/ghalmount.c
   trunk/hal/ghalmount.h
   trunk/hal/ghalvolume.c
   trunk/hal/ghalvolume.h
   trunk/hal/ghalvolumemonitor.c
   trunk/hal/ghalvolumemonitor.h
   trunk/hal/hal-device.c
   trunk/hal/hal-device.h
   trunk/hal/hal-pool.c
   trunk/hal/hal-pool.h
   trunk/programs/gvfs-mount.c

Modified: trunk/Makefile.am
==============================================================================
--- trunk/Makefile.am	(original)
+++ trunk/Makefile.am	Tue Jul  8 16:36:45 2008
@@ -4,6 +4,7 @@
 	common \
 	client \
 	daemon \
+	monitor \
 	gconf \
 	po   \
 	programs \

Modified: trunk/configure.ac
==============================================================================
--- trunk/configure.ac	(original)
+++ trunk/configure.ac	Tue Jul  8 16:36:45 2008
@@ -554,6 +554,8 @@
 common/Makefile
 client/Makefile
 daemon/Makefile
+monitor/Makefile
+monitor/proxy/Makefile
 hal/Makefile
 gconf/Makefile
 programs/Makefile

Modified: trunk/hal/Makefile.am
==============================================================================
--- trunk/hal/Makefile.am	(original)
+++ trunk/hal/Makefile.am	Tue Jul  8 16:36:45 2008
@@ -1,9 +1,7 @@
 
 NULL =
 
-module_flags = -export_dynamic -avoid-version -module -no-undefined -export-symbols-regex '^g_io_module_(load|unload)'
-
-giomodules_LTLIBRARIES = libgiohal-volume-monitor.la
+libexec_PROGRAMS = gvfs-hal-volume-monitor
 
 BUILT_SOURCES =                                         \
 	hal-marshal.h           hal-marshal.c
@@ -15,9 +13,9 @@
 	echo "#include \"hal-marshal.h\"" > $@ && glib-genmarshal $< --prefix=hal_marshal --body >> $@
 
 
-libgiohal_volume_monitor_la_SOURCES =				\
-	hal-utils.c 		hal-utils.h \
-	hal-module.c					\
+gvfs_hal_volume_monitor_SOURCES =			\
+	hal-utils.c 		hal-utils.h 		\
+	hal-volume-monitor-daemon.c			\
 	hal-marshal.c		hal-marshal.h		\
 	hal-device.c		hal-device.h		\
 	hal-pool.c		hal-pool.h		\
@@ -27,9 +25,10 @@
 	ghalvolumemonitor.c	ghalvolumemonitor.h	\
 	$(NULL)
 
-libgiohal_volume_monitor_la_CFLAGS =		\
+gvfs_hal_volume_monitor_CFLAGS =		\
 	-DG_LOG_DOMAIN=\"GVFS-Hal\"		\
 	-I$(top_srcdir)/common                  \
+	-I$(top_srcdir)/monitor/proxy           \
 	$(GLIB_CFLAGS)                          \
 	$(HAL_CFLAGS)                           \
 	-DGIO_MODULE_DIR=\"$(GIO_MODULE_DIR)\"	\
@@ -37,17 +36,27 @@
 	-DG_DISABLE_DEPRECATED			\
 	$(NULL)
 
-libgiohal_volume_monitor_la_LDFLAGS = \
-	$(module_flags)               \
+gvfs_hal_volume_monitor_LDFLAGS =	\
 	$(NULL)
 
-libgiohal_volume_monitor_la_LIBADD  =		     \
-	$(GLIB_LIBS)                                 \
-	$(HAL_LIBS)                                  \
-	$(top_builddir)/common/libgvfscommon-noin.la \
+gvfs_hal_volume_monitor_LDADD  =		     			      \
+	$(GLIB_LIBS)                                 			      \
+	$(HAL_LIBS)                                  			      \
+	$(top_builddir)/common/libgvfscommon-noin.la 			      \
+	$(top_builddir)/monitor/proxy/libgvfsproxyvolumemonitordaemon-noin.la \
 	$(NULL)
 
+remote_volume_monitorsdir = $(datadir)/gvfs/remote-volume-monitors
+remote_volume_monitors_DATA = hal.monitor
+
+servicedir       = $(datadir)/dbus-1/services
+service_in_files = org.gtk.Private.HalVolumeMonitor.service.in
+service_DATA     = $(service_in_files:.service.in=.service)
+
+$(service_DATA): $(service_in_files) Makefile
+	@sed -e "s|\ libexecdir\@|$(libexecdir)|" $< > $@
+
 clean-local:
 	rm -f *~ *.loT $(BUILT_SOURCES)
 
-EXTRA_DIST = hal-marshal.list
+EXTRA_DIST = hal-marshal.list $(service_in_files)

Modified: trunk/hal/ghaldrive.c
==============================================================================
--- trunk/hal/ghaldrive.c	(original)
+++ trunk/hal/ghaldrive.c	Tue Jul  8 16:36:45 2008
@@ -58,16 +58,9 @@
 
 static void g_hal_drive_drive_iface_init (GDriveIface *iface);
 
-#define _G_IMPLEMENT_INTERFACE_DYNAMIC(TYPE_IFACE, iface_init)       { \
-  const GInterfaceInfo g_implement_interface_info = { \
-    (GInterfaceInitFunc) iface_init, NULL, NULL \
-  }; \
-  g_type_module_add_interface (type_module, g_define_type_id, TYPE_IFACE, &g_implement_interface_info); \
-}
-
-G_DEFINE_DYNAMIC_TYPE_EXTENDED (GHalDrive, g_hal_drive, G_TYPE_OBJECT, 0,
-                                _G_IMPLEMENT_INTERFACE_DYNAMIC (G_TYPE_DRIVE,
-                                                                g_hal_drive_drive_iface_init))
+G_DEFINE_TYPE_EXTENDED (GHalDrive, g_hal_drive, G_TYPE_OBJECT, 0,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_DRIVE,
+                                               g_hal_drive_drive_iface_init))
 
 static void
 g_hal_drive_finalize (GObject *object)
@@ -108,11 +101,6 @@
 }
 
 static void
-g_hal_drive_class_finalize (GHalDriveClass *klass)
-{
-}
-
-static void
 g_hal_drive_init (GHalDrive *hal_drive)
 {
 }
@@ -374,7 +362,12 @@
   GHalDrive *hal_drive = G_HAL_DRIVE (user_data);
 
   if (strcmp (name, "EjectPressed") == 0)
-    g_signal_emit_by_name (hal_drive, "eject-button");
+    {
+      g_signal_emit_by_name (hal_drive, "eject-button");
+      if (hal_drive->volume_monitor != NULL)
+        g_signal_emit_by_name (hal_drive->volume_monitor, "drive-eject-button", hal_drive);
+    }
+
 }
 
 static void
@@ -1008,9 +1001,3 @@
   iface->get_identifier = g_hal_drive_get_identifier;
   iface->enumerate_identifiers = g_hal_drive_enumerate_identifiers;
 }
-
-void 
-g_hal_drive_register (GIOModule *module)
-{
-  g_hal_drive_register_type (G_TYPE_MODULE (module));
-}

Modified: trunk/hal/ghaldrive.h
==============================================================================
--- trunk/hal/ghaldrive.h	(original)
+++ trunk/hal/ghaldrive.h	Tue Jul  8 16:36:45 2008
@@ -44,7 +44,6 @@
 };
 
 GType g_hal_drive_get_type (void) G_GNUC_CONST;
-void  g_hal_drive_register (GIOModule *module);
 
 GHalDrive *g_hal_drive_new          (GVolumeMonitor *volume_monitor,
 				     HalDevice      *device,

Modified: trunk/hal/ghalmount.c
==============================================================================
--- trunk/hal/ghalmount.c	(original)
+++ trunk/hal/ghalmount.c	Tue Jul  8 16:36:45 2008
@@ -79,15 +79,9 @@
 
 static void g_hal_mount_mount_iface_init (GMountIface *iface);
 
-#define _G_IMPLEMENT_INTERFACE_DYNAMIC(TYPE_IFACE, iface_init)       { \
-  const GInterfaceInfo g_implement_interface_info = { \
-    (GInterfaceInitFunc) iface_init, NULL, NULL \
-  }; \
-  g_type_module_add_interface (type_module, g_define_type_id, TYPE_IFACE, &g_implement_interface_info); \
-}
-G_DEFINE_DYNAMIC_TYPE_EXTENDED (GHalMount, g_hal_mount, G_TYPE_OBJECT, 0, 
-                                _G_IMPLEMENT_INTERFACE_DYNAMIC (G_TYPE_MOUNT,
-                                                                g_hal_mount_mount_iface_init))
+G_DEFINE_TYPE_EXTENDED (GHalMount, g_hal_mount, G_TYPE_OBJECT, 0,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_MOUNT,
+                                               g_hal_mount_mount_iface_init))
 
 static void
 g_hal_mount_finalize (GObject *object)
@@ -135,11 +129,6 @@
 }
 
 static void
-g_hal_mount_class_finalize (GHalMountClass *klass)
-{
-}
-
-static void
 g_hal_mount_init (GHalMount *hal_mount)
 {
 }
@@ -1162,12 +1151,6 @@
   iface->eject_finish = g_hal_mount_eject_finish;
 }
 
-void 
-g_hal_mount_register (GIOModule *module)
-{
-  g_hal_mount_register_type (G_TYPE_MODULE (module));
-}
-
 #define INSENSITIVE_SEARCH_ITEMS_PER_CALLBACK 100
  
 static void

Modified: trunk/hal/ghalmount.h
==============================================================================
--- trunk/hal/ghalmount.h	(original)
+++ trunk/hal/ghalmount.h	Tue Jul  8 16:36:45 2008
@@ -44,7 +44,6 @@
 };
 
 GType g_hal_mount_get_type (void) G_GNUC_CONST;
-void  g_hal_mount_register (GIOModule *module);
 
 GHalMount *  g_hal_mount_new_for_hal_device    (GVolumeMonitor    *volume_monitor,
 						HalDevice         *device,

Modified: trunk/hal/ghalvolume.c
==============================================================================
--- trunk/hal/ghalvolume.c	(original)
+++ trunk/hal/ghalvolume.c	Tue Jul  8 16:36:45 2008
@@ -68,16 +68,9 @@
 
 static void g_hal_volume_volume_iface_init (GVolumeIface *iface);
 
-#define _G_IMPLEMENT_INTERFACE_DYNAMIC(TYPE_IFACE, iface_init)       { \
-  const GInterfaceInfo g_implement_interface_info = { \
-    (GInterfaceInitFunc) iface_init, NULL, NULL \
-  }; \
-  g_type_module_add_interface (type_module, g_define_type_id, TYPE_IFACE, &g_implement_interface_info); \
-}
-
-G_DEFINE_DYNAMIC_TYPE_EXTENDED (GHalVolume, g_hal_volume, G_TYPE_OBJECT, 0,
-                                _G_IMPLEMENT_INTERFACE_DYNAMIC (G_TYPE_VOLUME,
-                                                                g_hal_volume_volume_iface_init))
+G_DEFINE_TYPE_EXTENDED (GHalVolume, g_hal_volume, G_TYPE_OBJECT, 0,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_VOLUME,
+                                               g_hal_volume_volume_iface_init))
 
 static void
 g_hal_volume_finalize (GObject *object)
@@ -126,11 +119,6 @@
 }
 
 static void
-g_hal_volume_class_finalize (GHalVolumeClass *klass)
-{
-}
-
-static void
 g_hal_volume_init (GHalVolume *hal_volume)
 {
 }
@@ -1158,6 +1146,14 @@
   return (char **)g_ptr_array_free (res, FALSE);
 }
 
+static GFile *
+g_hal_volume_get_activation_root (GVolume *volume)
+{
+  GHalVolume *hal_volume = G_HAL_VOLUME (volume);
+
+  return hal_volume->foreign_mount_root != NULL ? g_object_ref (hal_volume->foreign_mount_root) : NULL;
+}
+
 static void
 g_hal_volume_volume_iface_init (GVolumeIface *iface)
 {
@@ -1175,10 +1171,5 @@
   iface->eject_finish = g_hal_volume_eject_finish;
   iface->get_identifier = g_hal_volume_get_identifier;
   iface->enumerate_identifiers = g_hal_volume_enumerate_identifiers;
-}
-
-void 
-g_hal_volume_register (GIOModule *module)
-{
-  g_hal_volume_register_type (G_TYPE_MODULE (module));
+  iface->get_activation_root = g_hal_volume_get_activation_root;
 }

Modified: trunk/hal/ghalvolume.h
==============================================================================
--- trunk/hal/ghalvolume.h	(original)
+++ trunk/hal/ghalvolume.h	Tue Jul  8 16:36:45 2008
@@ -44,7 +44,6 @@
 };
 
 GType g_hal_volume_get_type (void) G_GNUC_CONST;
-void  g_hal_volume_register (GIOModule *module);
 
 GHalVolume *g_hal_volume_new            (GVolumeMonitor   *volume_monitor,
 					 HalDevice        *device,

Modified: trunk/hal/ghalvolumemonitor.c
==============================================================================
--- trunk/hal/ghalvolumemonitor.c	(original)
+++ trunk/hal/ghalvolumemonitor.c	Tue Jul  8 16:36:45 2008
@@ -104,8 +104,7 @@
                                       GList **removed_volumes);
 
 
-#define g_hal_volume_monitor_get_type g_hal_volume_monitor_get_type
-G_DEFINE_DYNAMIC_TYPE (GHalVolumeMonitor, g_hal_volume_monitor, G_TYPE_NATIVE_VOLUME_MONITOR)
+G_DEFINE_TYPE (GHalVolumeMonitor, g_hal_volume_monitor, G_TYPE_NATIVE_VOLUME_MONITOR)
 
 static void
 list_free (GList *objects)
@@ -472,16 +471,6 @@
 {
 }
 
-static void
-g_hal_volume_monitor_class_finalize (GHalVolumeMonitorClass *klass)
-{
-  if (pool)
-    {
-      g_object_unref (pool);
-      pool = NULL;
-    }
-}
-
 static gboolean
 is_supported (void)
 {
@@ -1568,13 +1557,3 @@
   monitor->last_camera_devices = new_camera_devices;
 #endif
 }
-
-void 
-g_hal_volume_monitor_register (GIOModule *module)
-{
-  g_hal_volume_monitor_register_type (G_TYPE_MODULE (module));
-  g_io_extension_point_implement (G_NATIVE_VOLUME_MONITOR_EXTENSION_POINT_NAME,
-				  G_TYPE_HAL_VOLUME_MONITOR,
-				  "hal",
-				  1);
-}

Modified: trunk/hal/ghalvolumemonitor.h
==============================================================================
--- trunk/hal/ghalvolumemonitor.h	(original)
+++ trunk/hal/ghalvolumemonitor.h	Tue Jul  8 16:36:45 2008
@@ -53,9 +53,6 @@
 GVolumeMonitor *g_hal_volume_monitor_new                          (void);
 void            g_hal_volume_monitor_force_update                 (GHalVolumeMonitor *monitor);
 
-void            g_hal_volume_monitor_register                     (GIOModule         *module);
-
-
 G_END_DECLS
 
 #endif /* __G_HAL_VOLUME_MONITOR_H__ */

Modified: trunk/hal/hal-device.c
==============================================================================
--- trunk/hal/hal-device.c	(original)
+++ trunk/hal/hal-device.c	Tue Jul  8 16:36:45 2008
@@ -39,7 +39,7 @@
 
 static guint signals[LAST_SIGNAL] = { 0 };
 
-G_DEFINE_DYNAMIC_TYPE (HalDevice, hal_device, G_TYPE_OBJECT)
+G_DEFINE_TYPE (HalDevice, hal_device, G_TYPE_OBJECT)
 
 static void
 hal_device_finalize (HalDevice *device)
@@ -82,11 +82,6 @@
 }
 
 static void
-hal_device_class_finalize (HalDeviceClass *klass)
-{
-}
-
-static void
 hal_device_init (HalDevice *device)
 {
   device->priv = g_new0 (HalDevicePrivate, 1);
@@ -287,12 +282,6 @@
   return device->priv->properties;
 }
 
-void 
-hal_device_register (GIOModule *module)
-{
-  hal_device_register_type (G_TYPE_MODULE (module));
-}
-
 gboolean
 hal_device_is_recently_plugged_in (HalDevice *device)
 {

Modified: trunk/hal/hal-device.h
==============================================================================
--- trunk/hal/hal-device.h	(original)
+++ trunk/hal/hal-device.h	Tue Jul  8 16:36:45 2008
@@ -58,7 +58,6 @@
 
 
 GType               hal_device_get_type                    (void);
-void                hal_device_register                    (GIOModule         *module);
 
 HalDevice *         hal_device_new_from_udi                (LibHalContext     *hal_ctx, 
                                                             const char        *udi);

Modified: trunk/hal/hal-pool.c
==============================================================================
--- trunk/hal/hal-pool.c	(original)
+++ trunk/hal/hal-pool.c	Tue Jul  8 16:36:45 2008
@@ -47,7 +47,7 @@
   GHashTable *devices;
 };
 
-G_DEFINE_DYNAMIC_TYPE (HalPool, hal_pool, G_TYPE_OBJECT)
+G_DEFINE_TYPE (HalPool, hal_pool, G_TYPE_OBJECT)
 
 static void
 hal_pool_finalize (HalPool *pool)
@@ -120,12 +120,6 @@
 }
 
 static void
-hal_pool_class_finalize (HalPoolClass *klass)
-{
-  g_type_class_unref (g_type_class_peek (HAL_TYPE_DEVICE));
-}
-
-static void
 hal_pool_init (HalPool *pool)
 {
   pool->priv = g_new0 (HalPoolPrivate, 1);
@@ -462,9 +456,3 @@
  out:
   return devices;
 }
-
-void 
-hal_pool_register (GIOModule *module)
-{
-  hal_pool_register_type (G_TYPE_MODULE (module));
-}

Modified: trunk/hal/hal-pool.h
==============================================================================
--- trunk/hal/hal-pool.h	(original)
+++ trunk/hal/hal-pool.h	Tue Jul  8 16:36:45 2008
@@ -59,7 +59,6 @@
 };
 
 GType            hal_pool_get_type                            (void);
-void             hal_pool_register                            (GIOModule    *module);
 HalPool *        hal_pool_new                                 (char        **cap_only);
 LibHalContext *  hal_pool_get_hal_ctx                         (HalPool      *pool);
 DBusConnection * hal_pool_get_dbus_connection                 (HalPool      *pool);

Added: trunk/hal/hal-volume-monitor-daemon.c
==============================================================================
--- (empty file)
+++ trunk/hal/hal-volume-monitor-daemon.c	Tue Jul  8 16:36:45 2008
@@ -0,0 +1,43 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2006-2008 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 <glib.h>
+#include <glib/gi18n-lib.h>
+#include <gmodule.h>
+#include <gio/gio.h>
+
+#include <gvfsproxyvolumemonitordaemon.h>
+
+#include "ghalvolumemonitor.h"
+
+int
+main (int argc, char *argv[])
+{
+  g_vfs_proxy_volume_monitor_daemon_init ();
+  return g_vfs_proxy_volume_monitor_daemon_main (argc,
+                                                 argv,
+                                                 "org.gtk.Private.HalVolumeMonitor",
+                                                 G_TYPE_HAL_VOLUME_MONITOR);
+}

Added: trunk/hal/hal.monitor
==============================================================================
--- (empty file)
+++ trunk/hal/hal.monitor	Tue Jul  8 16:36:45 2008
@@ -0,0 +1,5 @@
+[RemoteVolumeMonitor]
+Name=GProxyVolumeMonitorHal
+DBusName=org.gtk.Private.HalVolumeMonitor
+IsNative=true
+NativePriority=2

Added: trunk/hal/org.gtk.Private.HalVolumeMonitor.service.in
==============================================================================
--- (empty file)
+++ trunk/hal/org.gtk.Private.HalVolumeMonitor.service.in	Tue Jul  8 16:36:45 2008
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.gtk.Private.HalVolumeMonitor
+Exec= libexecdir@/gvfs-hal-volume-monitor

Added: trunk/monitor/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/monitor/Makefile.am	Tue Jul  8 16:36:45 2008
@@ -0,0 +1,2 @@
+
+SUBDIRS = proxy

Added: trunk/monitor/proxy/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/monitor/proxy/Makefile.am	Tue Jul  8 16:36:45 2008
@@ -0,0 +1,67 @@
+
+NULL =
+
+remote_volume_monitorsdir = $(datadir)/gvfs/remote-volume-monitors
+
+module_flags = -export_dynamic -avoid-version -module -no-undefined -export-symbols-regex '^g_io_module_(load|unload)'
+
+giomodules_LTLIBRARIES = libgioremote-volume-monitor.la
+
+libgioremote_volume_monitor_la_SOURCES =				\
+	remote-volume-monitor-module.c					\
+	gproxydrive.c			gproxydrive.h			\
+	gproxyvolume.c			gproxyvolume.h			\
+	gproxymount.c			gproxymount.h			\
+	gproxyvolumemonitor.c		gproxyvolumemonitor.h		\
+	$(NULL)
+
+libgioremote_volume_monitor_la_CFLAGS =					\
+	-DG_LOG_DOMAIN=\"GVFS-RemoteVolumeMonitor\"			\
+	-I$(top_srcdir)/common                  			\
+	$(GLIB_CFLAGS)                          			\
+	$(DBUS_CFLAGS)                          			\
+	-DGIO_MODULE_DIR=\"$(GIO_MODULE_DIR)\"				\
+	-DREMOTE_VOLUME_MONITORS_DIR=\"$(remote_volume_monitorsdir)\" 	\
+	-DGVFS_LOCALEDIR=\""$(localedir)"\"				\
+	-DG_DISABLE_DEPRECATED						\
+	$(NULL)
+
+libgioremote_volume_monitor_la_LDFLAGS = 				\
+	$(module_flags)               					\
+	$(NULL)
+
+libgioremote_volume_monitor_la_LIBADD  =		     		\
+	$(GLIB_LIBS)                                 			\
+	$(DBUS_LIBS)                                 			\
+	$(top_builddir)/common/libgvfscommon-noin.la 			\
+	$(NULL)
+
+############################################################################
+
+noinst_LTLIBRARIES = libgvfsproxyvolumemonitordaemon-noin.la
+
+libgvfsproxyvolumemonitordaemon_noin_la_SOURCES =		\
+	gvfsproxyvolumemonitordaemon.c				\
+	gvfsproxyvolumemonitordaemon.h
+
+libgvfsproxyvolumemonitordaemon_noin_la_CFLAGS =		\
+	-I$(top_srcdir)/common                  		\
+	$(GLIB_CFLAGS)                          		\
+	$(DBUS_CFLAGS)                          		\
+	$(GDU_CFLAGS)                           		\
+	-DG_LOG_DOMAIN=\"GVFS-RemoteVolumeMonitorDaemon\"	\
+	-DGVFS_LOCALEDIR=\""$(localedir)"\"			\
+	-DG_DISABLE_DEPRECATED					\
+	$(NULL)
+
+libgvfsproxyvolumemonitordaemon_noin_la_LIBADD  =     		\
+	$(GLIB_LIBS)                                 		\
+	$(DBUS_LIBS)                                 		\
+	$(top_builddir)/common/libgvfscommon-noin.la 		\
+	$(NULL)
+
+clean-local:
+	rm -f *~ *.loT
+
+install-data-local:
+	mkdir -p $(DESTDIR)$(remote_volume_monitorsdir)

Added: trunk/monitor/proxy/gproxydrive.c
==============================================================================
--- (empty file)
+++ trunk/monitor/proxy/gproxydrive.c	Tue Jul  8 16:36:45 2008
@@ -0,0 +1,578 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2006-2008 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 <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include <gdbusutils.h>
+
+#include "gproxyvolumemonitor.h"
+#include "gproxydrive.h"
+#include "gproxyvolume.h"
+
+/* Protects all fields of GProxyDrive that can change */
+G_LOCK_DEFINE_STATIC(proxy_drive);
+
+struct _GProxyDrive {
+  GObject parent;
+
+  GProxyVolumeMonitor  *volume_monitor;
+
+  char *id;
+  char *name;
+  GIcon *icon;
+  char **volume_ids;
+  gboolean can_eject;
+  gboolean can_poll_for_media;
+  gboolean is_media_check_automatic;
+  gboolean has_media;
+  gboolean is_media_removable;
+
+  GHashTable *identifiers;
+};
+
+static void g_proxy_drive_drive_iface_init (GDriveIface *iface);
+
+#define _G_IMPLEMENT_INTERFACE_DYNAMIC(TYPE_IFACE, iface_init)       { \
+  const GInterfaceInfo g_implement_interface_info = { \
+    (GInterfaceInitFunc) iface_init, NULL, NULL \
+  }; \
+  g_type_module_add_interface (type_module, g_define_type_id, TYPE_IFACE, &g_implement_interface_info); \
+}
+
+G_DEFINE_DYNAMIC_TYPE_EXTENDED (GProxyDrive, g_proxy_drive, G_TYPE_OBJECT, 0,
+                                _G_IMPLEMENT_INTERFACE_DYNAMIC (G_TYPE_DRIVE,
+                                                                g_proxy_drive_drive_iface_init))
+
+static void
+g_proxy_drive_finalize (GObject *object)
+{
+  GProxyDrive *drive;
+
+  drive = G_PROXY_DRIVE (object);
+
+  if (drive->volume_monitor != NULL)
+    g_object_unref (drive->volume_monitor);
+  g_free (drive->id);
+  g_free (drive->name);
+  if (drive->icon != NULL)
+    g_object_unref (drive->icon);
+  g_strfreev (drive->volume_ids);
+  if (drive->identifiers != NULL)
+    g_hash_table_unref (drive->identifiers);
+
+  if (G_OBJECT_CLASS (g_proxy_drive_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_proxy_drive_parent_class)->finalize) (object);
+}
+
+static void
+g_proxy_drive_class_init (GProxyDriveClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->finalize = g_proxy_drive_finalize;
+}
+
+static void
+g_proxy_drive_class_finalize (GProxyDriveClass *klass)
+{
+}
+
+static void
+g_proxy_drive_init (GProxyDrive *proxy_drive)
+{
+}
+
+GProxyDrive *
+g_proxy_drive_new (GProxyVolumeMonitor *volume_monitor)
+{
+  GProxyDrive *drive;
+  drive = g_object_new (G_TYPE_PROXY_DRIVE, NULL);
+  drive->volume_monitor = g_object_ref (volume_monitor);
+  return drive;
+}
+
+/* string               id
+ * string               name
+ * string               gicon_data
+ * boolean              can-eject
+ * boolean              can-poll-for-media
+ * boolean              has-media
+ * boolean              is-media-removable
+ * array:string         volume-ids
+ * dict:string->string  identifiers
+ */
+#define DRIVE_STRUCT_TYPE "(sssbbbasa{ss})"
+
+void
+g_proxy_drive_update (GProxyDrive         *drive,
+                      DBusMessageIter     *iter)
+{
+  DBusMessageIter iter_struct;
+  DBusMessageIter iter_volume_ids_iter;
+  const char *id;
+  const char *name;
+  const char *gicon_data;
+  dbus_bool_t can_eject;
+  dbus_bool_t can_poll_for_media;
+  dbus_bool_t has_media;
+  dbus_bool_t is_media_removable;
+  GPtrArray *volume_ids;
+  GHashTable *identifiers;
+
+  dbus_message_iter_recurse (iter, &iter_struct);
+  dbus_message_iter_get_basic (&iter_struct, &id);
+  dbus_message_iter_next (&iter_struct);
+  dbus_message_iter_get_basic (&iter_struct, &name);
+  dbus_message_iter_next (&iter_struct);
+  dbus_message_iter_get_basic (&iter_struct, &gicon_data);
+  dbus_message_iter_next (&iter_struct);
+  dbus_message_iter_get_basic (&iter_struct, &can_eject);
+  dbus_message_iter_next (&iter_struct);
+  dbus_message_iter_get_basic (&iter_struct, &can_poll_for_media);
+  dbus_message_iter_next (&iter_struct);
+  dbus_message_iter_get_basic (&iter_struct, &has_media);
+  dbus_message_iter_next (&iter_struct);
+  dbus_message_iter_get_basic (&iter_struct, &is_media_removable);
+  dbus_message_iter_next (&iter_struct);
+
+  volume_ids = g_ptr_array_new ();
+  dbus_message_iter_recurse (&iter_struct, &iter_volume_ids_iter);
+  while (dbus_message_iter_get_arg_type (&iter_volume_ids_iter) != DBUS_TYPE_INVALID)
+    {
+      const char *volume_id;
+      dbus_message_iter_get_basic (&iter_volume_ids_iter, &volume_id);
+      dbus_message_iter_next (&iter_volume_ids_iter);
+      g_ptr_array_add (volume_ids, (gpointer) volume_id);
+    }
+  g_ptr_array_add (volume_ids, NULL);
+  dbus_message_iter_next (&iter_struct);
+
+  identifiers = _get_identifiers (&iter_struct);
+  dbus_message_iter_next (&iter_struct);
+
+  if (drive->id != NULL && strcmp (drive->id, id) != 0)
+    {
+      g_warning ("id mismatch during update of drive");
+      goto out;
+    }
+
+  if (strlen (name) == 0)
+    name = NULL;
+
+  /* out with the old */
+  g_free (drive->id);
+  g_free (drive->name);
+  if (drive->icon != NULL)
+    g_object_unref (drive->icon);
+  g_strfreev (drive->volume_ids);
+  if (drive->identifiers != NULL)
+    g_hash_table_unref (drive->identifiers);
+
+  /* in with the new */
+  drive->id = g_strdup (id);
+  drive->name = g_strdup (name);
+  drive->icon = _g_icon_new_from_serialized_data (gicon_data);
+  drive->can_eject = can_eject;
+  drive->can_poll_for_media = can_poll_for_media;
+  drive->has_media = has_media;
+  drive->is_media_removable = is_media_removable;
+  drive->identifiers = identifiers != NULL ? g_hash_table_ref (identifiers) : NULL;
+  drive->volume_ids = g_strdupv ((char **) volume_ids->pdata);
+
+ out:
+  g_ptr_array_free (volume_ids, TRUE);
+  g_hash_table_unref (identifiers);
+}
+
+static GIcon *
+g_proxy_drive_get_icon (GDrive *drive)
+{
+  GProxyDrive *proxy_drive = G_PROXY_DRIVE (drive);
+  GIcon *icon;
+
+  G_LOCK (proxy_drive);
+  icon = proxy_drive->icon != NULL ? g_object_ref (proxy_drive->icon) : NULL;
+  G_UNLOCK (proxy_drive);
+
+  return icon;
+}
+
+static char *
+g_proxy_drive_get_name (GDrive *drive)
+{
+  GProxyDrive *proxy_drive = G_PROXY_DRIVE (drive);
+  char *name;
+
+  G_LOCK (proxy_drive);
+  name = g_strdup (proxy_drive->name);
+  G_UNLOCK (proxy_drive);
+
+  return name;
+}
+
+static GList *
+g_proxy_drive_get_volumes (GDrive *drive)
+{
+  GProxyDrive *proxy_drive = G_PROXY_DRIVE (drive);
+  GList *l;
+
+  l = NULL;
+
+  G_LOCK (proxy_drive);
+  if (proxy_drive->volume_monitor != NULL && proxy_drive->volume_ids != NULL)
+    {
+      int n;
+
+      for (n = 0; proxy_drive->volume_ids[n] != NULL; n++)
+        {
+          GProxyVolume *volume;
+          volume = g_proxy_volume_monitor_get_volume_for_id (proxy_drive->volume_monitor, proxy_drive->volume_ids[n]);
+          if (volume != NULL)
+            l = g_list_append (l, volume);
+        }
+    }
+  G_UNLOCK (proxy_drive);
+
+  return l;
+}
+
+static gboolean
+g_proxy_drive_has_volumes (GDrive *drive)
+{
+  GProxyDrive *proxy_drive = G_PROXY_DRIVE (drive);
+  gboolean res;
+
+  G_LOCK (proxy_drive);
+  res = (proxy_drive->volume_ids != NULL && g_strv_length (proxy_drive->volume_ids) > 0);
+  G_UNLOCK (proxy_drive);
+
+  return res;
+}
+
+static gboolean
+g_proxy_drive_is_media_removable (GDrive *drive)
+{
+  GProxyDrive *proxy_drive = G_PROXY_DRIVE (drive);
+  gboolean res;
+
+  G_LOCK (proxy_drive);
+  res = proxy_drive->is_media_removable;
+  G_UNLOCK (proxy_drive);
+
+  return res;
+}
+
+static gboolean
+g_proxy_drive_has_media (GDrive *drive)
+{
+  GProxyDrive *proxy_drive = G_PROXY_DRIVE (drive);
+  gboolean res;
+
+  G_LOCK (proxy_drive);
+  res = proxy_drive->has_media;
+  G_UNLOCK (proxy_drive);
+
+  return res;
+}
+
+static gboolean
+g_proxy_drive_is_media_check_automatic (GDrive *drive)
+{
+  GProxyDrive *proxy_drive = G_PROXY_DRIVE (drive);
+  gboolean res;
+
+  G_LOCK (proxy_drive);
+  res = proxy_drive->is_media_check_automatic;
+  G_UNLOCK (proxy_drive);
+
+  return res;
+}
+
+static gboolean
+g_proxy_drive_can_eject (GDrive *drive)
+{
+  GProxyDrive *proxy_drive = G_PROXY_DRIVE (drive);
+  gboolean res;
+
+  G_LOCK (proxy_drive);
+  res = proxy_drive->can_eject;
+  G_UNLOCK (proxy_drive);
+
+  return res;
+}
+
+static gboolean
+g_proxy_drive_can_poll_for_media (GDrive *drive)
+{
+  GProxyDrive *proxy_drive = G_PROXY_DRIVE (drive);
+  gboolean res;
+
+  G_LOCK (proxy_drive);
+  res = proxy_drive->can_poll_for_media;
+  G_UNLOCK (proxy_drive);
+
+  return res;
+}
+
+static char *
+g_proxy_drive_get_identifier (GDrive              *drive,
+                              const char          *kind)
+{
+  GProxyDrive *proxy_drive = G_PROXY_DRIVE (drive);
+  char *res;
+
+  G_LOCK (proxy_drive);
+  if (proxy_drive->identifiers != NULL)
+    res = g_strdup (g_hash_table_lookup (proxy_drive->identifiers, kind));
+  else
+    res = NULL;
+  G_UNLOCK (proxy_drive);
+
+  return res;
+}
+
+static void
+add_identifier_key (const char *key, const char *value, GPtrArray *res)
+{
+  g_ptr_array_add (res, g_strdup (key));
+}
+
+static char **
+g_proxy_drive_enumerate_identifiers (GDrive *drive)
+{
+  GProxyDrive *proxy_drive = G_PROXY_DRIVE (drive);
+  GPtrArray *res;
+
+  res = g_ptr_array_new ();
+
+  G_LOCK (proxy_drive);
+  if (proxy_drive->identifiers != NULL)
+    g_hash_table_foreach (proxy_drive->identifiers, (GHFunc) add_identifier_key, res);
+  G_UNLOCK (proxy_drive);
+
+  /* Null-terminate */
+  g_ptr_array_add (res, NULL);
+
+  return (char **) g_ptr_array_free (res, FALSE);
+}
+
+const char *
+g_proxy_drive_get_id (GProxyDrive *drive)
+{
+  return drive->id;
+}
+
+typedef struct {
+  GObject *object;
+  GAsyncReadyCallback callback;
+  gpointer user_data;
+  GCancellable *cancellable;
+} DBusOp;
+
+static void
+eject_cb (DBusMessage *reply,
+          GError *error,
+          DBusOp *data)
+{
+  GSimpleAsyncResult *simple;
+  if (error != NULL)
+    simple = g_simple_async_result_new_from_error (data->object,
+                                                   data->callback,
+                                                   data->user_data,
+                                                   error);
+  else
+    simple = g_simple_async_result_new (data->object,
+                                        data->callback,
+                                        data->user_data,
+                                        NULL);
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+
+  g_object_unref (data->object);
+  g_free (data);
+}
+
+static void
+g_proxy_drive_eject (GDrive              *drive,
+                     GMountUnmountFlags   flags,
+                     GCancellable        *cancellable,
+                     GAsyncReadyCallback  callback,
+                     gpointer             user_data)
+{
+  GProxyDrive *proxy_drive = G_PROXY_DRIVE (drive);
+  DBusConnection *connection;
+  const char *name;
+  DBusMessage *message;
+  DBusOp *data;
+  dbus_uint32_t _flags = flags;
+
+  G_LOCK (proxy_drive);
+
+  data = g_new0 (DBusOp, 1);
+  data->object = g_object_ref (drive);
+  data->callback = callback;
+  data->user_data = user_data;
+  data->cancellable = cancellable;
+
+  connection = g_proxy_volume_monitor_get_dbus_connection (proxy_drive->volume_monitor);
+  name = g_proxy_volume_monitor_get_dbus_name (proxy_drive->volume_monitor);
+
+  message = dbus_message_new_method_call (name,
+                                          "/",
+                                          "org.gtk.Private.RemoteVolumeMonitor",
+                                          "DriveEject");
+  dbus_message_append_args (message,
+                            DBUS_TYPE_STRING,
+                            &(proxy_drive->id),
+                            DBUS_TYPE_UINT32,
+                            &_flags,
+                            DBUS_TYPE_INVALID);
+  G_UNLOCK (proxy_drive);
+
+  _g_dbus_connection_call_async (connection,
+                                 message,
+                                 -1,
+                                 (GAsyncDBusCallback) eject_cb,
+                                 data);
+  dbus_connection_unref (connection);
+  dbus_message_unref (message);
+}
+
+static gboolean
+g_proxy_drive_eject_finish (GDrive        *drive,
+                            GAsyncResult  *result,
+                            GError       **error)
+{
+  if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+    return FALSE;
+  return TRUE;
+}
+
+static void
+poll_for_media_cb (DBusMessage *reply,
+                   GError *error,
+                   DBusOp *data)
+{
+  GSimpleAsyncResult *simple;
+  if (error != NULL)
+    simple = g_simple_async_result_new_from_error (data->object,
+                                                   data->callback,
+                                                   data->user_data,
+                                                   error);
+  else
+    simple = g_simple_async_result_new (data->object,
+                                        data->callback,
+                                        data->user_data,
+                                        NULL);
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+
+  g_object_unref (data->object);
+  g_free (data);
+}
+
+static void
+g_proxy_drive_poll_for_media (GDrive              *drive,
+                              GCancellable        *cancellable,
+                              GAsyncReadyCallback  callback,
+                              gpointer             user_data)
+{
+  GProxyDrive *proxy_drive = G_PROXY_DRIVE (drive);
+  DBusConnection *connection;
+  const char *name;
+  DBusMessage *message;
+  DBusOp *data;
+
+  G_LOCK (proxy_drive);
+
+  data = g_new0 (DBusOp, 1);
+  data->object = g_object_ref (drive);
+  data->callback = callback;
+  data->user_data = user_data;
+  data->cancellable = cancellable;
+
+  connection = g_proxy_volume_monitor_get_dbus_connection (proxy_drive->volume_monitor);
+  name = g_proxy_volume_monitor_get_dbus_name (proxy_drive->volume_monitor);
+
+  message = dbus_message_new_method_call (name,
+                                          "/",
+                                          "org.gtk.Private.RemoteVolumeMonitor",
+                                          "DrivePollForMedia");
+  dbus_message_append_args (message,
+                            DBUS_TYPE_STRING,
+                            &(proxy_drive->id),
+                            DBUS_TYPE_INVALID);
+  G_UNLOCK (proxy_drive);
+
+  _g_dbus_connection_call_async (connection,
+                                 message,
+                                 -1,
+                                 (GAsyncDBusCallback) poll_for_media_cb,
+                                 data);
+  dbus_connection_unref (connection);
+  dbus_message_unref (message);
+}
+
+static gboolean
+g_proxy_drive_poll_for_media_finish (GDrive        *drive,
+                                     GAsyncResult  *result,
+                                     GError       **error)
+{
+  if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+    return FALSE;
+  return TRUE;
+}
+
+
+static void
+g_proxy_drive_drive_iface_init (GDriveIface *iface)
+{
+  iface->get_name = g_proxy_drive_get_name;
+  iface->get_icon = g_proxy_drive_get_icon;
+  iface->has_volumes = g_proxy_drive_has_volumes;
+  iface->get_volumes = g_proxy_drive_get_volumes;
+  iface->is_media_removable = g_proxy_drive_is_media_removable;
+  iface->has_media = g_proxy_drive_has_media;
+  iface->is_media_check_automatic = g_proxy_drive_is_media_check_automatic;
+  iface->can_eject = g_proxy_drive_can_eject;
+  iface->can_poll_for_media = g_proxy_drive_can_poll_for_media;
+  iface->eject = g_proxy_drive_eject;
+  iface->eject_finish = g_proxy_drive_eject_finish;
+  iface->poll_for_media = g_proxy_drive_poll_for_media;
+  iface->poll_for_media_finish = g_proxy_drive_poll_for_media_finish;
+  iface->get_identifier = g_proxy_drive_get_identifier;
+  iface->enumerate_identifiers = g_proxy_drive_enumerate_identifiers;
+}
+
+void
+g_proxy_drive_register (GIOModule *module)
+{
+  g_proxy_drive_register_type (G_TYPE_MODULE (module));
+}

Added: trunk/monitor/proxy/gproxydrive.h
==============================================================================
--- (empty file)
+++ trunk/monitor/proxy/gproxydrive.h	Tue Jul  8 16:36:45 2008
@@ -0,0 +1,55 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2006-2008 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_PROXY_DRIVE_H__
+#define __G_PROXY_DRIVE_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "gproxyvolumemonitor.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_PROXY_DRIVE        (g_proxy_drive_get_type ())
+#define G_PROXY_DRIVE(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_PROXY_DRIVE, GProxyDrive))
+#define G_PROXY_DRIVE_CLASS(k)    (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_PROXY_DRIVE, GProxyDriveClass))
+#define G_IS_PROXY_DRIVE(o)       (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_PROXY_DRIVE))
+#define G_IS_PROXY_DRIVE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_PROXY_DRIVE))
+
+typedef struct _GProxyDriveClass GProxyDriveClass;
+
+struct _GProxyDriveClass {
+   GObjectClass parent_class;
+};
+
+GType         g_proxy_drive_get_type     (void) G_GNUC_CONST;
+void          g_proxy_drive_register     (GIOModule           *module);
+GProxyDrive  *g_proxy_drive_new          (GProxyVolumeMonitor *volume_monitor);
+void          g_proxy_drive_update       (GProxyDrive         *drive,
+                                          DBusMessageIter     *iter);
+const char   *g_proxy_drive_get_id       (GProxyDrive         *drive);
+
+G_END_DECLS
+
+#endif /* __G_PROXY_DRIVE_H__ */

Added: trunk/monitor/proxy/gproxymount.c
==============================================================================
--- (empty file)
+++ trunk/monitor/proxy/gproxymount.c	Tue Jul  8 16:36:45 2008
@@ -0,0 +1,502 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2006-2008 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 <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <gio/gio.h>
+
+#include <gdbusutils.h>
+
+#include "gproxyvolumemonitor.h"
+#include "gproxymount.h"
+#include "gproxyvolume.h"
+
+/* Protects all fields of GProxyMount that can change */
+G_LOCK_DEFINE_STATIC(proxy_mount);
+
+struct _GProxyMount {
+  GObject parent;
+
+  GProxyVolumeMonitor *volume_monitor;
+
+  char *id;
+  char *name;
+  char *uuid;
+  char *volume_id;
+  gboolean can_unmount;
+  char **x_content_types;
+  GFile *root;
+  GIcon *icon;
+};
+
+static void g_proxy_mount_mount_iface_init (GMountIface *iface);
+
+#define _G_IMPLEMENT_INTERFACE_DYNAMIC(TYPE_IFACE, iface_init)       { \
+  const GInterfaceInfo g_implement_interface_info = { \
+    (GInterfaceInitFunc) iface_init, NULL, NULL \
+  }; \
+  g_type_module_add_interface (type_module, g_define_type_id, TYPE_IFACE, &g_implement_interface_info); \
+}
+G_DEFINE_DYNAMIC_TYPE_EXTENDED (GProxyMount, g_proxy_mount, G_TYPE_OBJECT, 0,
+                                _G_IMPLEMENT_INTERFACE_DYNAMIC (G_TYPE_MOUNT,
+                                                                g_proxy_mount_mount_iface_init))
+
+static void
+g_proxy_mount_finalize (GObject *object)
+{
+  GProxyMount *mount;
+
+  mount = G_PROXY_MOUNT (object);
+
+  g_free (mount->id);
+  g_free (mount->name);
+  g_free (mount->uuid);
+  g_free (mount->volume_id);
+  g_strfreev (mount->x_content_types);
+  if (mount->icon != NULL)
+    g_object_unref (mount->icon);
+  if (mount->root != NULL)
+    g_object_unref (mount->root);
+
+  if (mount->volume_monitor != NULL)
+    g_object_unref (mount->volume_monitor);
+
+  if (G_OBJECT_CLASS (g_proxy_mount_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_proxy_mount_parent_class)->finalize) (object);
+}
+
+static void
+g_proxy_mount_class_init (GProxyMountClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->finalize = g_proxy_mount_finalize;
+}
+
+static void
+g_proxy_mount_class_finalize (GProxyMountClass *klass)
+{
+}
+
+static void
+g_proxy_mount_init (GProxyMount *proxy_mount)
+{
+}
+
+GProxyMount *
+g_proxy_mount_new (GProxyVolumeMonitor *volume_monitor)
+{
+  GProxyMount *mount;
+  mount = g_object_new (G_TYPE_PROXY_MOUNT, NULL);
+  mount->volume_monitor = g_object_ref (volume_monitor);
+  return mount;
+}
+
+/* string               id
+ * string               name
+ * string               gicon_data
+ * string               uuid
+ * string               root_uri
+ * boolean              can-unmount
+ * string               volume-id
+ * array:string         x-content-types
+ */
+
+void
+g_proxy_mount_update (GProxyMount         *mount,
+                      DBusMessageIter     *iter)
+{
+  DBusMessageIter iter_struct;
+  DBusMessageIter iter_x_content_types;
+  const char *id;
+  const char *name;
+  const char *gicon_data;
+  const char *uuid;
+  const char *root_uri;
+  dbus_bool_t can_unmount;
+  const char *volume_id;
+  GPtrArray *x_content_types;
+
+  dbus_message_iter_recurse (iter, &iter_struct);
+  dbus_message_iter_get_basic (&iter_struct, &id);
+  dbus_message_iter_next (&iter_struct);
+  dbus_message_iter_get_basic (&iter_struct, &name);
+  dbus_message_iter_next (&iter_struct);
+  dbus_message_iter_get_basic (&iter_struct, &gicon_data);
+  dbus_message_iter_next (&iter_struct);
+  dbus_message_iter_get_basic (&iter_struct, &uuid);
+  dbus_message_iter_next (&iter_struct);
+  dbus_message_iter_get_basic (&iter_struct, &root_uri);
+  dbus_message_iter_next (&iter_struct);
+  dbus_message_iter_get_basic (&iter_struct, &can_unmount);
+  dbus_message_iter_next (&iter_struct);
+  dbus_message_iter_get_basic (&iter_struct, &volume_id);
+  dbus_message_iter_next (&iter_struct);
+
+  x_content_types = g_ptr_array_new ();
+  dbus_message_iter_recurse (&iter_struct, &iter_x_content_types);
+  while (dbus_message_iter_get_arg_type (&iter_x_content_types) != DBUS_TYPE_INVALID)
+    {
+      const char *x_content_type;
+      dbus_message_iter_get_basic (&iter_x_content_types, &x_content_type);
+      dbus_message_iter_next (&iter_x_content_types);
+      g_ptr_array_add (x_content_types, (gpointer) x_content_type);
+    }
+  g_ptr_array_add (x_content_types, NULL);
+  dbus_message_iter_next (&iter_struct);
+
+  if (mount->id != NULL && strcmp (mount->id, id) != 0)
+    {
+      g_warning ("id mismatch during update of mount");
+      goto out;
+    }
+
+  if (strlen (name) == 0)
+    name = NULL;
+  if (strlen (uuid) == 0)
+    uuid = NULL;
+
+  /* out with the old */
+  g_free (mount->id);
+  g_free (mount->name);
+  g_free (mount->uuid);
+  g_free (mount->volume_id);
+  if (mount->icon != NULL)
+    g_object_unref (mount->icon);
+  g_strfreev (mount->x_content_types);
+  if (mount->root != NULL)
+    g_object_unref (mount->root);
+
+  /* in with the new */
+  mount->id = g_strdup (id);
+  mount->name = g_strdup (name);
+  mount->icon = _g_icon_new_from_serialized_data (gicon_data);
+  mount->uuid = g_strdup (uuid);
+  mount->root = g_file_new_for_uri (root_uri);
+  mount->can_unmount = can_unmount;
+  mount->volume_id = g_strdup (volume_id);
+  mount->x_content_types = g_strdupv ((char **) x_content_types->pdata);
+
+ out:
+  g_ptr_array_free (x_content_types, TRUE);
+}
+
+const char *
+g_proxy_mount_get_id (GProxyMount *mount)
+{
+  return mount->id;
+}
+
+static GFile *
+g_proxy_mount_get_root (GMount *mount)
+{
+  GProxyMount *proxy_mount = G_PROXY_MOUNT (mount);
+  GFile *root;
+
+  G_LOCK (proxy_mount);
+  root = proxy_mount->root != NULL ? g_object_ref (proxy_mount->root) : NULL;
+  G_UNLOCK (proxy_mount);
+  return root;
+}
+
+static GIcon *
+g_proxy_mount_get_icon (GMount *mount)
+{
+  GProxyMount *proxy_mount = G_PROXY_MOUNT (mount);
+  GIcon *icon;
+
+  G_LOCK (proxy_mount);
+  icon = proxy_mount->icon != NULL ? g_object_ref (proxy_mount->icon) : NULL;
+  G_UNLOCK (proxy_mount);
+  return icon;
+}
+
+static char *
+g_proxy_mount_get_uuid (GMount *mount)
+{
+  GProxyMount *proxy_mount = G_PROXY_MOUNT (mount);
+  char *uuid;
+
+  G_LOCK (proxy_mount);
+  uuid = g_strdup (proxy_mount->uuid);
+  G_UNLOCK (proxy_mount);
+  return uuid;
+}
+
+static char *
+g_proxy_mount_get_name (GMount *mount)
+{
+  GProxyMount *proxy_mount = G_PROXY_MOUNT (mount);
+  char *name;
+
+  G_LOCK (proxy_mount);
+  name = g_strdup (proxy_mount->name);
+  G_UNLOCK (proxy_mount);
+
+  return name;
+}
+
+static GDrive *
+g_proxy_mount_get_drive (GMount *mount)
+{
+  GProxyMount *proxy_mount = G_PROXY_MOUNT (mount);
+  GProxyVolume *volume;
+  GDrive *drive;
+
+  G_LOCK (proxy_mount);
+  volume = NULL;
+  if (proxy_mount->volume_id != NULL && strlen (proxy_mount->volume_id) > 0)
+    volume = g_proxy_volume_monitor_get_volume_for_id (proxy_mount->volume_monitor,
+                                                       proxy_mount->volume_id);
+  G_UNLOCK (proxy_mount);
+
+  drive = NULL;
+  if (volume != NULL)
+    {
+      drive = g_volume_get_drive (G_VOLUME (volume));
+      g_object_unref (volume);
+    }
+
+  return drive;
+}
+
+static GVolume *
+g_proxy_mount_get_volume (GMount *mount)
+{
+  GProxyMount *proxy_mount = G_PROXY_MOUNT (mount);
+  GProxyVolume *volume;
+
+  G_LOCK (proxy_mount);
+  volume = NULL;
+  if (proxy_mount->volume_id != NULL && strlen (proxy_mount->volume_id) > 0)
+    volume = g_proxy_volume_monitor_get_volume_for_id (proxy_mount->volume_monitor,
+                                                       proxy_mount->volume_id);
+  G_UNLOCK (proxy_mount);
+
+  return volume != NULL ? G_VOLUME (volume) : NULL;
+}
+
+static gboolean
+g_proxy_mount_can_unmount (GMount *mount)
+{
+  GProxyMount *proxy_mount = G_PROXY_MOUNT (mount);
+  gboolean res;
+
+  G_LOCK (proxy_mount);
+  res = proxy_mount->can_unmount;
+  G_UNLOCK (proxy_mount);
+
+  return res;
+}
+
+static gboolean
+g_proxy_mount_can_eject (GMount *mount)
+{
+  GDrive *drive;
+  gboolean can_eject;
+
+  can_eject = FALSE;
+  drive = g_proxy_mount_get_drive (mount);
+  if (drive != NULL)
+    {
+      can_eject = g_drive_can_eject (drive);
+      g_object_unref (drive);
+    }
+
+  return can_eject;
+}
+
+typedef struct {
+  GObject *object;
+  GAsyncReadyCallback callback;
+  gpointer user_data;
+} EjectWrapperOp;
+
+static void
+eject_wrapper_callback (GObject *source_object,
+                        GAsyncResult *res,
+                        gpointer user_data)
+{
+  EjectWrapperOp *data  = user_data;
+  data->callback (data->object, res, data->user_data);
+  g_free (data);
+}
+
+static void
+g_proxy_mount_eject (GMount              *mount,
+                     GMountUnmountFlags   flags,
+                     GCancellable        *cancellable,
+                     GAsyncReadyCallback  callback,
+                     gpointer             user_data)
+{
+  GDrive *drive;
+
+  drive = g_proxy_mount_get_drive (mount);
+
+  if (drive != NULL)
+    {
+      EjectWrapperOp *data;
+      data = g_new0 (EjectWrapperOp, 1);
+      data->object = G_OBJECT (mount);
+      data->callback = callback;
+      data->user_data = user_data;
+      g_drive_eject (drive, flags, cancellable, eject_wrapper_callback, data);
+      g_object_unref (drive);
+    }
+}
+
+static gboolean
+g_proxy_mount_eject_finish (GMount        *mount,
+                            GAsyncResult  *result,
+                            GError       **error)
+{
+  GDrive *drive;
+  gboolean res;
+
+  res = TRUE;
+
+  drive = g_proxy_mount_get_drive (mount);
+
+  if (drive != NULL)
+    {
+      res = g_drive_eject_finish (drive, result, error);
+      g_object_unref (drive);
+    }
+  return res;
+}
+
+typedef struct {
+  GObject *object;
+  GAsyncReadyCallback callback;
+  gpointer user_data;
+  GCancellable *cancellable;
+} DBusOp;
+
+static void
+unmount_cb (DBusMessage *reply,
+            GError *error,
+            DBusOp *data)
+{
+  GSimpleAsyncResult *simple;
+  if (error != NULL)
+    simple = g_simple_async_result_new_from_error (data->object,
+                                                   data->callback,
+                                                   data->user_data,
+                                                   error);
+  else
+    simple = g_simple_async_result_new (data->object,
+                                        data->callback,
+                                        data->user_data,
+                                        NULL);
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+
+  g_object_unref (data->object);
+  g_free (data);
+}
+
+static void
+g_proxy_mount_unmount (GMount              *mount,
+                       GMountUnmountFlags   flags,
+                       GCancellable        *cancellable,
+                       GAsyncReadyCallback  callback,
+                       gpointer             user_data)
+{
+  GProxyMount *proxy_mount = G_PROXY_MOUNT (mount);
+  DBusConnection *connection;
+  const char *name;
+  DBusMessage *message;
+  DBusOp *data;
+  dbus_uint32_t _flags = flags;
+
+  G_LOCK (proxy_mount);
+
+  data = g_new0 (DBusOp, 1);
+  data->object = g_object_ref (mount);
+  data->callback = callback;
+  data->user_data = user_data;
+  data->cancellable = cancellable;
+
+  connection = g_proxy_volume_monitor_get_dbus_connection (proxy_mount->volume_monitor);
+  name = g_proxy_volume_monitor_get_dbus_name (proxy_mount->volume_monitor);
+
+  message = dbus_message_new_method_call (name,
+                                          "/",
+                                          "org.gtk.Private.RemoteVolumeMonitor",
+                                          "MountUnmount");
+  dbus_message_append_args (message,
+                            DBUS_TYPE_STRING,
+                            &(proxy_mount->id),
+                            DBUS_TYPE_UINT32,
+                            &_flags,
+                            DBUS_TYPE_INVALID);
+  G_UNLOCK (proxy_mount);
+
+  _g_dbus_connection_call_async (connection,
+                                 message,
+                                 -1,
+                                 (GAsyncDBusCallback) unmount_cb,
+                                 data);
+
+  dbus_message_unref (message);
+  dbus_connection_unref (connection);
+}
+
+static gboolean
+g_proxy_mount_unmount_finish (GMount        *mount,
+                              GAsyncResult  *result,
+                              GError       **error)
+{
+  if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+    return FALSE;
+  return TRUE;
+}
+
+static void
+g_proxy_mount_mount_iface_init (GMountIface *iface)
+{
+  iface->get_root = g_proxy_mount_get_root;
+  iface->get_name = g_proxy_mount_get_name;
+  iface->get_icon = g_proxy_mount_get_icon;
+  iface->get_uuid = g_proxy_mount_get_uuid;
+  iface->get_drive = g_proxy_mount_get_drive;
+  iface->get_volume = g_proxy_mount_get_volume;
+  iface->can_unmount = g_proxy_mount_can_unmount;
+  iface->can_eject = g_proxy_mount_can_eject;
+  iface->unmount = g_proxy_mount_unmount;
+  iface->unmount_finish = g_proxy_mount_unmount_finish;
+  iface->eject = g_proxy_mount_eject;
+  iface->eject_finish = g_proxy_mount_eject_finish;
+}
+
+void
+g_proxy_mount_register (GIOModule *module)
+{
+  g_proxy_mount_register_type (G_TYPE_MODULE (module));
+}

Added: trunk/monitor/proxy/gproxymount.h
==============================================================================
--- (empty file)
+++ trunk/monitor/proxy/gproxymount.h	Tue Jul  8 16:36:45 2008
@@ -0,0 +1,56 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2006-2008 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_PROXY_MOUNT_H__
+#define __G_PROXY_MOUNT_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "gproxyvolumemonitor.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_PROXY_MOUNT        (g_proxy_mount_get_type ())
+#define G_PROXY_MOUNT(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_PROXY_MOUNT, GProxyMount))
+#define G_PROXY_MOUNT_CLASS(k)    (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_PROXY_MOUNT, GProxyMountClass))
+#define G_IS_PROXY_MOUNT(o)       (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_PROXY_MOUNT))
+#define G_IS_PROXY_MOUNT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_PROXY_MOUNT))
+
+typedef struct _GProxyMountClass GProxyMountClass;
+
+struct _GProxyMountClass {
+   GObjectClass parent_class;
+};
+
+GType         g_proxy_mount_get_type (void) G_GNUC_CONST;
+void          g_proxy_mount_register (GIOModule           *module);
+GProxyMount  *g_proxy_mount_new      (GProxyVolumeMonitor *volume_monitor);
+void          g_proxy_mount_update   (GProxyMount         *mount,
+                                      DBusMessageIter     *iter);
+const char   *g_proxy_mount_get_id   (GProxyMount         *mount);
+
+
+G_END_DECLS
+
+#endif /* __G_PROXY_MOUNT_H__ */

Added: trunk/monitor/proxy/gproxyvolume.c
==============================================================================
--- (empty file)
+++ trunk/monitor/proxy/gproxyvolume.c	Tue Jul  8 16:36:45 2008
@@ -0,0 +1,688 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2006-2008 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 <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <gio/gio.h>
+
+#include <gdbusutils.h>
+
+#include "gproxydrive.h"
+#include "gproxyvolume.h"
+#include "gproxymount.h"
+
+/* Protects all fields of GProxyVolume that can change */
+G_LOCK_DEFINE_STATIC(proxy_volume);
+
+struct _GProxyVolume {
+  GObject parent;
+
+  GProxyVolumeMonitor *volume_monitor;
+
+  char *id;
+  char *name;
+  char *uuid;
+  char *activation_uri;
+  GIcon *icon;
+  char *drive_id;
+  char *mount_id;
+  GHashTable *identifiers;
+
+  GMount *foreign_mount;
+
+  gboolean can_mount;
+  gboolean should_automount;
+};
+
+static void g_proxy_volume_volume_iface_init (GVolumeIface *iface);
+
+#define _G_IMPLEMENT_INTERFACE_DYNAMIC(TYPE_IFACE, iface_init)       { \
+  const GInterfaceInfo g_implement_interface_info = { \
+    (GInterfaceInitFunc) iface_init, NULL, NULL \
+  }; \
+  g_type_module_add_interface (type_module, g_define_type_id, TYPE_IFACE, &g_implement_interface_info); \
+}
+
+G_DEFINE_DYNAMIC_TYPE_EXTENDED (GProxyVolume, g_proxy_volume, G_TYPE_OBJECT, 0,
+                                _G_IMPLEMENT_INTERFACE_DYNAMIC (G_TYPE_VOLUME,
+                                                                g_proxy_volume_volume_iface_init))
+
+static void
+g_proxy_volume_finalize (GObject *object)
+{
+  GProxyVolume *volume;
+
+  volume = G_PROXY_VOLUME (object);
+
+  g_free (volume->id);
+  g_free (volume->name);
+  g_free (volume->uuid);
+  g_free (volume->activation_uri);
+  if (volume->icon != NULL)
+    g_object_unref (volume->icon);
+  g_free (volume->drive_id);
+  g_free (volume->mount_id);
+  if (volume->identifiers != NULL)
+    g_hash_table_unref (volume->identifiers);
+
+  if (volume->foreign_mount != NULL)
+    g_object_unref (volume->foreign_mount);
+
+  if (volume->volume_monitor != NULL)
+    g_object_unref (volume->volume_monitor);
+
+  if (G_OBJECT_CLASS (g_proxy_volume_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_proxy_volume_parent_class)->finalize) (object);
+}
+
+static void
+g_proxy_volume_class_init (GProxyVolumeClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->finalize = g_proxy_volume_finalize;
+}
+
+static void
+g_proxy_volume_class_finalize (GProxyVolumeClass *klass)
+{
+}
+
+static void
+g_proxy_volume_init (GProxyVolume *proxy_volume)
+{
+}
+
+GProxyVolume *
+g_proxy_volume_new (GProxyVolumeMonitor *volume_monitor)
+{
+  GProxyVolume *volume;
+  volume = g_object_new (G_TYPE_PROXY_VOLUME, NULL);
+  volume->volume_monitor = g_object_ref (volume_monitor);
+  return volume;
+}
+
+static gboolean
+changed_in_idle (gpointer data)
+{
+  GProxyVolume *volume = data;
+
+  g_signal_emit_by_name (volume, "changed");
+  if (volume->volume_monitor != NULL)
+    g_signal_emit_by_name (volume->volume_monitor, "volume-changed", volume);
+  g_object_unref (volume);
+
+  return FALSE;
+}
+
+static void
+foreign_mount_unmounted (GMount *mount, gpointer user_data)
+{
+  GProxyVolume *volume = G_PROXY_VOLUME (user_data);
+  gboolean check;
+
+  G_LOCK (proxy_volume);
+  check = (volume->foreign_mount == mount);
+  G_UNLOCK (proxy_volume);
+  if (check)
+    g_proxy_volume_adopt_foreign_mount (volume, NULL);
+}
+
+void
+g_proxy_volume_adopt_foreign_mount (GProxyVolume *volume,
+                                    GMount       *foreign_mount)
+{
+  G_LOCK (proxy_volume);
+  if (volume->foreign_mount != NULL)
+    g_object_unref (volume->foreign_mount);
+
+  if (foreign_mount != NULL)
+    {
+      volume->foreign_mount =  g_object_ref (foreign_mount);
+      g_signal_connect_object (foreign_mount, "unmounted", (GCallback) foreign_mount_unmounted, volume, 0);
+    }
+  else
+    volume->foreign_mount =  NULL;
+
+  g_idle_add (changed_in_idle, g_object_ref (volume));
+  G_UNLOCK (proxy_volume);
+}
+
+/* string               id
+ * string               name
+ * string               gicon_data
+ * string               uuid
+ * string               activation_uri
+ * boolean              can-mount
+ * boolean              should-automount
+ * string               drive-id
+ * string               mount-id
+ * dict:string->string  identifiers
+ */
+
+void g_proxy_volume_update (GProxyVolume    *volume,
+                            DBusMessageIter *iter)
+{
+  DBusMessageIter iter_struct;
+  const char *id;
+  const char *name;
+  const char *gicon_data;
+  const char *uuid;
+  const char *activation_uri;
+  const char *drive_id;
+  const char *mount_id;
+  dbus_bool_t can_mount;
+  dbus_bool_t should_automount;
+  GHashTable *identifiers;
+
+  dbus_message_iter_recurse (iter, &iter_struct);
+  dbus_message_iter_get_basic (&iter_struct, &id);
+  dbus_message_iter_next (&iter_struct);
+  dbus_message_iter_get_basic (&iter_struct, &name);
+  dbus_message_iter_next (&iter_struct);
+  dbus_message_iter_get_basic (&iter_struct, &gicon_data);
+  dbus_message_iter_next (&iter_struct);
+  dbus_message_iter_get_basic (&iter_struct, &uuid);
+  dbus_message_iter_next (&iter_struct);
+  dbus_message_iter_get_basic (&iter_struct, &activation_uri);
+  dbus_message_iter_next (&iter_struct);
+  dbus_message_iter_get_basic (&iter_struct, &can_mount);
+  dbus_message_iter_next (&iter_struct);
+  dbus_message_iter_get_basic (&iter_struct, &should_automount);
+  dbus_message_iter_next (&iter_struct);
+  dbus_message_iter_get_basic (&iter_struct, &drive_id);
+  dbus_message_iter_next (&iter_struct);
+  dbus_message_iter_get_basic (&iter_struct, &mount_id);
+  dbus_message_iter_next (&iter_struct);
+
+  identifiers = _get_identifiers (&iter_struct);
+  dbus_message_iter_next (&iter_struct);
+
+  if (volume->id != NULL && strcmp (volume->id, id) != 0)
+    {
+      g_warning ("id mismatch during update of volume");
+      goto out;
+    }
+
+  if (strlen (name) == 0)
+    name = NULL;
+  if (strlen (uuid) == 0)
+    uuid = NULL;
+  if (strlen (activation_uri) == 0)
+    activation_uri = NULL;
+
+  /* out with the old */
+  g_free (volume->id);
+  g_free (volume->name);
+  g_free (volume->uuid);
+  g_free (volume->activation_uri);
+  if (volume->icon != NULL)
+    g_object_unref (volume->icon);
+  g_free (volume->drive_id);
+  g_free (volume->mount_id);
+  if (volume->identifiers != NULL)
+    g_hash_table_unref (volume->identifiers);
+
+  /* in with the new */
+  volume->id = g_strdup (id);
+  volume->name = g_strdup (name);
+  volume->uuid = g_strdup (uuid);
+  volume->activation_uri = g_strdup (activation_uri);
+  volume->icon = _g_icon_new_from_serialized_data (gicon_data);
+  volume->drive_id = g_strdup (drive_id);
+  volume->mount_id = g_strdup (mount_id);
+  volume->can_mount = can_mount;
+  volume->should_automount = should_automount;
+  volume->identifiers = identifiers != NULL ? g_hash_table_ref (identifiers) : NULL;
+
+ out:
+  g_hash_table_unref (identifiers);
+}
+
+const char *
+g_proxy_volume_get_id (GProxyVolume *volume)
+{
+  return volume->id;
+}
+
+static GIcon *
+g_proxy_volume_get_icon (GVolume *volume)
+{
+  GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume);
+  GIcon *icon;
+
+  G_LOCK (proxy_volume);
+  icon = proxy_volume->icon != NULL ? g_object_ref (proxy_volume->icon) : NULL;
+  G_UNLOCK (proxy_volume);
+  return icon;
+}
+
+static char *
+g_proxy_volume_get_name (GVolume *volume)
+{
+  GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume);
+  char *name;
+
+  G_LOCK (proxy_volume);
+  name = g_strdup (proxy_volume->name);
+  G_UNLOCK (proxy_volume);
+  return name;
+}
+
+static char *
+g_proxy_volume_get_uuid (GVolume *volume)
+{
+  GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume);
+  char *uuid;
+
+  G_LOCK (proxy_volume);
+  uuid = g_strdup (proxy_volume->uuid);
+  G_UNLOCK (proxy_volume);
+  return uuid;
+}
+
+static gboolean
+g_proxy_volume_can_mount (GVolume *volume)
+{
+  GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume);
+  gboolean res;
+
+  G_LOCK (proxy_volume);
+  res = proxy_volume->can_mount;
+  G_UNLOCK (proxy_volume);
+  return res;
+}
+
+static gboolean
+g_proxy_volume_can_eject (GVolume *volume)
+{
+  GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume);
+  GProxyDrive *drive;
+  gboolean res;
+
+  G_LOCK (proxy_volume);
+  res = FALSE;
+  if (proxy_volume->drive_id != NULL && strlen (proxy_volume->drive_id) > 0)
+    {
+      drive = g_proxy_volume_monitor_get_drive_for_id (proxy_volume->volume_monitor,
+                                                       proxy_volume->drive_id);
+      if (drive != NULL)
+        {
+          res = g_drive_can_eject (G_DRIVE (drive));
+          g_object_unref (drive);
+        }
+    }
+  G_UNLOCK (proxy_volume);
+
+  return res;
+}
+
+static gboolean
+g_proxy_volume_should_automount (GVolume *volume)
+{
+  GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume);
+  gboolean res;
+
+  G_LOCK (proxy_volume);
+  res = proxy_volume->should_automount;
+  G_UNLOCK (proxy_volume);
+
+  return res;
+}
+
+static GDrive *
+g_proxy_volume_get_drive (GVolume *volume)
+{
+  GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume);
+  GProxyDrive *drive;
+
+  G_LOCK (proxy_volume);
+  drive = NULL;
+  if (proxy_volume->drive_id != NULL && strlen (proxy_volume->drive_id) > 0)
+    drive = g_proxy_volume_monitor_get_drive_for_id (proxy_volume->volume_monitor,
+                                                     proxy_volume->drive_id);
+  G_UNLOCK (proxy_volume);
+
+  return drive != NULL ? G_DRIVE (drive) : NULL;
+}
+
+static GMount *
+g_proxy_volume_get_mount (GVolume *volume)
+{
+  GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume);
+  GMount *mount;
+
+  G_LOCK (proxy_volume);
+  if (proxy_volume->foreign_mount != NULL)
+    {
+      mount = g_object_ref (proxy_volume->foreign_mount);
+    }
+  else
+    {
+      mount = NULL;
+      if (proxy_volume->mount_id != NULL && strlen (proxy_volume->mount_id) > 0)
+        {
+          GProxyMount *proxy_mount;
+          proxy_mount = g_proxy_volume_monitor_get_mount_for_id (proxy_volume->volume_monitor,
+                                                                 proxy_volume->mount_id);
+          if (proxy_mount != NULL)
+            mount = G_MOUNT (proxy_mount);
+        }
+    }
+  G_UNLOCK (proxy_volume);
+
+  return mount;
+}
+
+typedef struct {
+  GObject *object;
+  GAsyncReadyCallback callback;
+  gpointer user_data;
+} EjectWrapperOp;
+
+static void
+eject_wrapper_callback (GObject *source_object,
+                        GAsyncResult *res,
+                        gpointer user_data)
+{
+  EjectWrapperOp *data  = user_data;
+  data->callback (data->object, res, data->user_data);
+  g_free (data);
+}
+
+static void
+g_proxy_volume_eject (GVolume              *volume,
+                    GMountUnmountFlags   flags,
+                    GCancellable        *cancellable,
+                    GAsyncReadyCallback  callback,
+                    gpointer             user_data)
+{
+  GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume);
+  GProxyDrive *drive;
+
+  drive = NULL;
+  G_LOCK (proxy_volume);
+  if (proxy_volume->drive_id != NULL && strlen (proxy_volume->drive_id) > 0)
+    {
+      drive = g_proxy_volume_monitor_get_drive_for_id (proxy_volume->volume_monitor,
+                                                       proxy_volume->drive_id);
+    }
+  G_UNLOCK (proxy_volume);
+
+  if (drive != NULL)
+    {
+      EjectWrapperOp *data;
+      data = g_new0 (EjectWrapperOp, 1);
+      data->object = g_object_ref (volume);
+      data->callback = callback;
+      data->user_data = user_data;
+      g_drive_eject (G_DRIVE (drive), flags, cancellable, eject_wrapper_callback, data);
+      g_object_unref (drive);
+    }
+}
+
+static gboolean
+g_proxy_volume_eject_finish (GVolume        *volume,
+                          GAsyncResult  *result,
+                          GError       **error)
+{
+  GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume);
+  GProxyDrive *drive;
+  gboolean res;
+
+  G_LOCK (proxy_volume);
+  res = TRUE;
+  drive = NULL;
+  if (proxy_volume->drive_id != NULL && strlen (proxy_volume->drive_id) > 0)
+    drive = g_proxy_volume_monitor_get_drive_for_id (proxy_volume->volume_monitor,
+                                                     proxy_volume->drive_id);
+  G_UNLOCK (proxy_volume);
+
+  if (drive != NULL)
+    {
+      res = g_drive_eject_finish (G_DRIVE (drive), result, error);
+      g_object_unref (drive);
+    }
+
+  return res;
+}
+
+static char *
+g_proxy_volume_get_identifier (GVolume              *volume,
+                               const char          *kind)
+{
+  GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume);
+  char *res;
+
+  G_LOCK (proxy_volume);
+  if (proxy_volume->identifiers != NULL)
+    res = g_strdup (g_hash_table_lookup (proxy_volume->identifiers, kind));
+  else
+    res = NULL;
+  G_UNLOCK (proxy_volume);
+
+  return res;
+}
+
+static void
+add_identifier_key (const char *key, const char *value, GPtrArray *res)
+{
+  g_ptr_array_add (res, g_strdup (key));
+}
+
+static char **
+g_proxy_volume_enumerate_identifiers (GVolume *volume)
+{
+  GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume);
+  GPtrArray *res;
+
+  res = g_ptr_array_new ();
+
+  G_LOCK (proxy_volume);
+  if (proxy_volume->identifiers != NULL)
+    g_hash_table_foreach (proxy_volume->identifiers, (GHFunc) add_identifier_key, res);
+  G_UNLOCK (proxy_volume);
+
+  /* Null-terminate */
+  g_ptr_array_add (res, NULL);
+
+  return (char **) g_ptr_array_free (res, FALSE);
+}
+
+typedef struct {
+  GObject *object;
+  GAsyncReadyCallback callback;
+  gpointer user_data;
+  GCancellable *cancellable;
+} DBusOp;
+
+static void
+mount_cb (DBusMessage *reply,
+          GError *error,
+          DBusOp *data)
+{
+  GSimpleAsyncResult *simple;
+  if (error != NULL)
+    simple = g_simple_async_result_new_from_error (data->object,
+                                                   data->callback,
+                                                   data->user_data,
+                                                   error);
+  else
+    simple = g_simple_async_result_new (data->object,
+                                        data->callback,
+                                        data->user_data,
+                                        NULL);
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+
+  g_object_unref (data->object);
+  g_free (data);
+}
+
+typedef struct
+{
+  GProxyVolume *enclosing_volume;
+  GAsyncReadyCallback  callback;
+  gpointer user_data;
+} ForeignMountOp;
+
+static void
+mount_foreign_callback (GObject *source_object,
+                        GAsyncResult *res,
+                        gpointer user_data)
+{
+  ForeignMountOp *data = user_data;
+  data->callback (G_OBJECT (data->enclosing_volume), res, data->user_data);
+  g_object_unref (data->enclosing_volume);
+  g_free (data);
+}
+
+static void
+g_proxy_volume_mount (GVolume             *volume,
+                      GMountMountFlags     flags,
+                      GMountOperation     *mount_operation,
+                      GCancellable        *cancellable,
+                      GAsyncReadyCallback  callback,
+                      gpointer             user_data)
+{
+  GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume);
+
+  G_LOCK (proxy_volume);
+  if (proxy_volume->activation_uri != NULL)
+    {
+      ForeignMountOp *data;
+      GFile *root;
+
+      data = g_new0 (ForeignMountOp, 1);
+      data->enclosing_volume = g_object_ref (volume);
+      data->callback = callback;
+      data->user_data = user_data;
+
+      root = g_file_new_for_uri (proxy_volume->activation_uri);
+
+      G_UNLOCK (proxy_volume);
+
+      g_file_mount_enclosing_volume (root,
+                                     flags,
+                                     mount_operation,
+                                     cancellable,
+                                     mount_foreign_callback,
+                                     data);
+
+      g_object_unref (root);
+    }
+  else
+    {
+      DBusOp *data;
+      DBusConnection *connection;
+      const char *name;
+      DBusMessage *message;
+      dbus_uint32_t _flags = flags;
+      dbus_bool_t use_mount_operation = mount_operation != NULL;
+
+      /* TODO: support mount_operation */
+
+      data = g_new0 (DBusOp, 1);
+      data->object = g_object_ref (volume);
+      data->callback = callback;
+      data->user_data = user_data;
+      data->cancellable = cancellable;
+
+      connection = g_proxy_volume_monitor_get_dbus_connection (proxy_volume->volume_monitor);
+      name = g_proxy_volume_monitor_get_dbus_name (proxy_volume->volume_monitor);
+
+      message = dbus_message_new_method_call (name,
+                                              "/",
+                                              "org.gtk.Private.RemoteVolumeMonitor",
+                                              "VolumeMount");
+      dbus_message_append_args (message,
+                                DBUS_TYPE_STRING,
+                                &(proxy_volume->id),
+                                DBUS_TYPE_UINT32,
+                                &_flags,
+                                DBUS_TYPE_BOOLEAN,
+                                &use_mount_operation,
+                                DBUS_TYPE_INVALID);
+      G_UNLOCK (proxy_volume);
+
+      _g_dbus_connection_call_async (connection,
+                                     message,
+                                     -1,
+                                     (GAsyncDBusCallback) mount_cb,
+                                     data);
+      dbus_message_unref (message);
+      dbus_connection_unref (connection);
+    }
+}
+
+static gboolean
+g_proxy_volume_mount_finish (GVolume        *volume,
+                             GAsyncResult  *result,
+                             GError       **error)
+{
+  if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+    return FALSE;
+  return TRUE;
+}
+
+static GFile *
+g_proxy_volume_get_activation_root (GVolume *volume)
+{
+  GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume);
+  if (proxy_volume->activation_uri == NULL)
+    return NULL;
+  else
+    return g_file_new_for_uri (proxy_volume->activation_uri);
+}
+
+static void
+g_proxy_volume_volume_iface_init (GVolumeIface *iface)
+{
+  iface->get_name = g_proxy_volume_get_name;
+  iface->get_icon = g_proxy_volume_get_icon;
+  iface->get_uuid = g_proxy_volume_get_uuid;
+  iface->get_drive = g_proxy_volume_get_drive;
+  iface->get_mount = g_proxy_volume_get_mount;
+  iface->can_mount = g_proxy_volume_can_mount;
+  iface->can_eject = g_proxy_volume_can_eject;
+  iface->should_automount = g_proxy_volume_should_automount;
+  iface->mount_fn = g_proxy_volume_mount;
+  iface->mount_finish = g_proxy_volume_mount_finish;
+  iface->eject = g_proxy_volume_eject;
+  iface->eject_finish = g_proxy_volume_eject_finish;
+  iface->get_identifier = g_proxy_volume_get_identifier;
+  iface->enumerate_identifiers = g_proxy_volume_enumerate_identifiers;
+  iface->get_activation_root = g_proxy_volume_get_activation_root;
+}
+
+void
+g_proxy_volume_register (GIOModule *module)
+{
+  g_proxy_volume_register_type (G_TYPE_MODULE (module));
+}

Added: trunk/monitor/proxy/gproxyvolume.h
==============================================================================
--- (empty file)
+++ trunk/monitor/proxy/gproxyvolume.h	Tue Jul  8 16:36:45 2008
@@ -0,0 +1,58 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2006-2008 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_PROXY_VOLUME_H__
+#define __G_PROXY_VOLUME_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "gproxyvolumemonitor.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_PROXY_VOLUME        (g_proxy_volume_get_type ())
+#define G_PROXY_VOLUME(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_PROXY_VOLUME, GProxyVolume))
+#define G_PROXY_VOLUME_CLASS(k)    (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_PROXY_VOLUME, GProxyVolumeClass))
+#define G_IS_PROXY_VOLUME(o)       (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_PROXY_VOLUME))
+#define G_IS_PROXY_VOLUME_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_PROXY_VOLUME))
+
+typedef struct _GProxyVolumeClass GProxyVolumeClass;
+
+struct _GProxyVolumeClass {
+   GObjectClass parent_class;
+};
+
+GType         g_proxy_volume_get_type            (void) G_GNUC_CONST;
+GProxyVolume *g_proxy_volume_new                 (GProxyVolumeMonitor *volume_monitor);
+void          g_proxy_volume_update              (GProxyVolume        *volume,
+                                                  DBusMessageIter     *iter);
+const char   *g_proxy_volume_get_id              (GProxyVolume        *volume);
+void          g_proxy_volume_adopt_foreign_mount (GProxyVolume        *volume,
+                                                  GMount              *foreign_mount);
+void          g_proxy_volume_register            (GIOModule           *module);
+
+
+G_END_DECLS
+
+#endif /* __G_PROXY_VOLUME_H__ */

Added: trunk/monitor/proxy/gproxyvolumemonitor.c
==============================================================================
--- (empty file)
+++ trunk/monitor/proxy/gproxyvolumemonitor.c	Tue Jul  8 16:36:45 2008
@@ -0,0 +1,1178 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2006-2008 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>
+ */
+
+/*
+ * TODO: since we already call IsSupported() at module load time (in
+ * order to register the appropriate types) we really just should
+ * construct all the volume monitors. This is a good idea because
+ *
+ *  - instead of calling IsSupported() we just call List()
+ *    - e.g. exactly the same IPC overhead
+ *    - neglible memory + cpu overhead
+ *      - will need to construct them at some point
+ *    - we can actually implement get_mount_for_mount_path()
+ *      correctly
+ *
+ *  - implement support for GMountOperation
+ *    - not implemented in the HAL volume monitor and that's all
+ *      that is using it right now. Will implement at some point
+ *      when it's needed or someone has spare cycles.
+ */
+
+#include <config.h>
+
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <gio/gio.h>
+#include <gdbusutils.h>
+
+#include "gproxyvolumemonitor.h"
+#include "gproxymount.h"
+#include "gproxyvolume.h"
+#include "gproxydrive.h"
+
+G_LOCK_DEFINE_STATIC(proxy_vm);
+
+static DBusConnection *the_session_bus = NULL;
+static GHashTable *the_volume_monitors = NULL;
+
+struct _GProxyVolumeMonitor {
+  GNativeVolumeMonitor parent;
+  DBusConnection *session_bus;
+
+  GHashTable *drives;
+  GHashTable *volumes;
+  GHashTable *mounts;
+};
+
+G_DEFINE_DYNAMIC_TYPE_EXTENDED (GProxyVolumeMonitor,
+                                g_proxy_volume_monitor,
+                                G_TYPE_NATIVE_VOLUME_MONITOR,
+                                G_TYPE_FLAG_ABSTRACT,
+                                {})
+
+static void seed_monitor (GProxyVolumeMonitor  *monitor);
+
+static DBusHandlerResult filter_function (DBusConnection *connection, DBusMessage *message, void *user_data);
+
+static void signal_emit_in_idle (gpointer object, const char *signal_name, gpointer other_object);
+
+static char *
+get_match_rule (GProxyVolumeMonitor *monitor)
+{
+  return g_strdup_printf ("type='signal',"
+                          "interface='org.gtk.Private.RemoteVolumeMonitor',"
+                          "sender='%s'",
+                          g_proxy_volume_monitor_get_dbus_name (monitor));
+}
+
+static void
+g_proxy_volume_monitor_finalize (GObject *object)
+{
+  GProxyVolumeMonitor *monitor;
+  DBusError dbus_error;
+  char *match_rule;
+  GObjectClass *parent_class;
+
+  parent_class = G_OBJECT_CLASS (G_OBJECT_GET_CLASS (object));
+
+  monitor = G_PROXY_VOLUME_MONITOR (object);
+
+  g_hash_table_unref (monitor->drives);
+  g_hash_table_unref (monitor->volumes);
+  g_hash_table_unref (monitor->mounts);
+
+  dbus_connection_remove_filter (monitor->session_bus, filter_function, monitor);
+  match_rule = get_match_rule (monitor);
+  dbus_bus_remove_match (monitor->session_bus,
+                         match_rule,
+                         &dbus_error);
+  if (dbus_error_is_set (&dbus_error)) {
+    /* not really a whole lot to do on failure than whine since
+     * GObject finalization can't fail...
+     */
+    g_warning ("cannot remove match rule '%s': %s: %s", match_rule, dbus_error.name, dbus_error.message);
+    dbus_error_free (&dbus_error);
+  }
+  g_free (match_rule);
+  dbus_connection_unref (monitor->session_bus);
+
+  if (parent_class->finalize)
+    parent_class->finalize (object);
+}
+
+static GList *
+get_mounts (GVolumeMonitor *volume_monitor)
+{
+  GProxyVolumeMonitor *monitor;
+  GList *l;
+  GHashTableIter hash_iter;
+  GProxyMount *mount;
+
+  monitor = G_PROXY_VOLUME_MONITOR (volume_monitor);
+  l = NULL;
+
+  G_LOCK (proxy_vm);
+
+  g_hash_table_iter_init (&hash_iter, monitor->mounts);
+  while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &mount))
+    l = g_list_append (l, g_object_ref (mount));
+
+  G_UNLOCK (proxy_vm);
+
+  return l;
+}
+
+static GList *
+get_volumes (GVolumeMonitor *volume_monitor)
+{
+  GProxyVolumeMonitor *monitor;
+  GList *l;
+  GHashTableIter hash_iter;
+  GProxyVolume *volume;
+
+  monitor = G_PROXY_VOLUME_MONITOR (volume_monitor);
+  l = NULL;
+
+  G_LOCK (proxy_vm);
+
+  g_hash_table_iter_init (&hash_iter, monitor->volumes);
+  while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &volume))
+    l = g_list_append (l, g_object_ref (volume));
+
+  G_UNLOCK (proxy_vm);
+
+  return l;
+}
+
+static GList *
+get_connected_drives (GVolumeMonitor *volume_monitor)
+{
+  GProxyVolumeMonitor *monitor;
+  GList *l;
+  GHashTableIter hash_iter;
+  GProxyDrive *drive;
+
+  monitor = G_PROXY_VOLUME_MONITOR (volume_monitor);
+  l = NULL;
+
+  G_LOCK (proxy_vm);
+
+  g_hash_table_iter_init (&hash_iter, monitor->drives);
+  while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &drive))
+    l = g_list_append (l, g_object_ref (drive));
+
+  G_UNLOCK (proxy_vm);
+
+  return l;
+}
+
+static GVolume *
+get_volume_for_uuid (GVolumeMonitor *volume_monitor, const char *uuid)
+{
+  GProxyVolumeMonitor *monitor;
+  GHashTableIter hash_iter;
+  GVolume *found_volume;
+  GVolume *volume;
+
+  monitor = G_PROXY_VOLUME_MONITOR (volume_monitor);
+
+  G_LOCK (proxy_vm);
+
+  found_volume = NULL;
+  g_hash_table_iter_init (&hash_iter, monitor->volumes);
+  while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &volume) &&
+         found_volume != NULL)
+    {
+      char *_uuid;
+      _uuid = g_volume_get_uuid (volume);
+      if (_uuid != NULL)
+        {
+          if (strcmp (uuid, _uuid) == 0)
+            found_volume = g_object_ref (volume);
+          g_free (_uuid);
+        }
+    }
+
+  G_UNLOCK (proxy_vm);
+
+  return found_volume;
+}
+
+static GMount *
+get_mount_for_uuid (GVolumeMonitor *volume_monitor, const char *uuid)
+{
+  GProxyVolumeMonitor *monitor;
+  GHashTableIter hash_iter;
+  GMount *found_mount;
+  GMount *mount;
+
+  monitor = G_PROXY_VOLUME_MONITOR (volume_monitor);
+
+  G_LOCK (proxy_vm);
+
+  found_mount = NULL;
+  g_hash_table_iter_init (&hash_iter, monitor->mounts);
+  while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &mount) &&
+         found_mount != NULL)
+    {
+      char *_uuid;
+      _uuid = g_mount_get_uuid (mount);
+      if (_uuid != NULL)
+        {
+          if (strcmp (uuid, _uuid) == 0)
+            found_mount = g_object_ref (mount);
+          g_free (_uuid);
+        }
+    }
+
+  G_UNLOCK (proxy_vm);
+
+  return found_mount;
+}
+
+static GMount *
+get_mount_for_mount_path (const char *mount_path,
+                          GCancellable *cancellable)
+{
+  GType type;
+  GMount *mount;
+  GNativeVolumeMonitorClass *klass;
+
+  /* This static method on GNativeVolumeMonitor is a *complete* pain
+   * in the ass to deal with; we need to rework gio so it's deprecated
+   * and thus never will get called.
+   *
+   * For now we just implement as badly as the GUnixVolumeMonitor
+   * shipped with gio; e.g. we just return *some* object that
+   * implements GMount and we don't worry about setting the
+   * corresponding volume or ensuring it's the same reference as what
+   * one would normally get.
+   */
+
+  mount = NULL;
+  klass = NULL;
+
+  /* You can't hide types from me
+   *
+   * (read: _g_unix_volume_monitor_get_type() isn't exported)
+   */
+  type = g_type_from_name ("GUnixVolumeMonitor");
+  if (type == 0)
+    goto out;
+
+  klass = G_NATIVE_VOLUME_MONITOR_CLASS (g_type_class_ref (type));
+  if (klass == NULL)
+    goto out;
+
+  if (klass->get_mount_for_mount_path != NULL)
+    mount = klass->get_mount_for_mount_path (mount_path, cancellable);
+
+ out:
+  if (klass != NULL)
+    g_type_class_unref (klass);
+  return mount;
+}
+
+static void
+volume_monitor_went_away (gpointer data,
+                          GObject *where_the_object_was)
+{
+  GType type = (GType) data;
+  G_LOCK (proxy_vm);
+  g_hash_table_remove (the_volume_monitors, (gpointer) type);
+  G_UNLOCK (proxy_vm);
+}
+
+static GObject *
+g_proxy_volume_monitor_constructor (GType                  type,
+                                    guint                  n_construct_properties,
+                                    GObjectConstructParam *construct_properties)
+{
+  GObject *object;
+  GProxyVolumeMonitor *monitor;
+  GProxyVolumeMonitorClass *klass;
+  GObjectClass *parent_class;
+  DBusError dbus_error;
+  char *match_rule;
+
+  G_LOCK (proxy_vm);
+
+  klass = G_PROXY_VOLUME_MONITOR_CLASS (g_type_class_peek (type));
+  object = g_hash_table_lookup (the_volume_monitors, (gpointer) type);
+  if (object != NULL)
+    {
+      g_object_ref (object);
+      goto out;
+    }
+
+  /* Invoke parent constructor. */
+  klass = G_PROXY_VOLUME_MONITOR_CLASS (g_type_class_peek (G_TYPE_PROXY_VOLUME_MONITOR));
+  parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
+  object = parent_class->constructor (type,
+                                      n_construct_properties,
+                                      construct_properties);
+
+  monitor = G_PROXY_VOLUME_MONITOR (object);
+
+  dbus_error_init (&dbus_error);
+  monitor->session_bus = dbus_connection_ref (the_session_bus);
+  monitor->drives = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+  monitor->volumes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+  monitor->mounts = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+
+  dbus_connection_add_filter (monitor->session_bus, filter_function, monitor, NULL);
+  match_rule = get_match_rule (monitor);
+  dbus_bus_add_match (monitor->session_bus,
+                      match_rule,
+                      &dbus_error);
+  if (dbus_error_is_set (&dbus_error)) {
+    /* not really a whole lot to do on failure than whine since
+     * GObject construction can't fail...
+     */
+    g_warning ("cannot add match rule '%s': %s: %s", match_rule, dbus_error.name, dbus_error.message);
+    dbus_error_free (&dbus_error);
+  }
+  g_free (match_rule);
+
+  seed_monitor (monitor);
+
+  g_hash_table_insert (the_volume_monitors, (gpointer) type, object);
+  g_object_weak_ref (G_OBJECT (object), volume_monitor_went_away, (gpointer) type);
+
+ out:
+  G_UNLOCK (proxy_vm);
+  return object;
+}
+
+typedef struct {
+  const char *signal_name;
+  GObject *object;
+  GObject *other_object;
+} SignalEmitIdleData;
+
+static gboolean
+signal_emit_in_idle_do (SignalEmitIdleData *data)
+{
+  if (data->other_object != NULL)
+    {
+      g_signal_emit_by_name (data->object, data->signal_name, data->other_object);
+      g_object_unref (data->other_object);
+    }
+  else
+    {
+      g_signal_emit_by_name (data->object, data->signal_name);
+    }
+  g_object_unref (data->object);
+  g_free (data);
+
+  return FALSE;
+}
+
+static void
+signal_emit_in_idle (gpointer object, const char *signal_name, gpointer other_object)
+{
+  SignalEmitIdleData *data;
+
+  data = g_new0 (SignalEmitIdleData, 1);
+  data->signal_name = signal_name;
+  data->object = g_object_ref (G_OBJECT (object));
+  data->other_object = other_object != NULL ? g_object_ref (G_OBJECT (other_object)) : NULL;
+  g_idle_add ((GSourceFunc) signal_emit_in_idle_do, data);
+}
+
+
+
+static DBusHandlerResult
+filter_function (DBusConnection *connection, DBusMessage *message, void *user_data)
+{
+  GProxyVolumeMonitor *monitor = G_PROXY_VOLUME_MONITOR (user_data);
+  DBusMessageIter iter;
+  const char *id;
+  const char *member;
+  GProxyDrive *drive;
+  GProxyVolume *volume;
+  GProxyMount *mount;
+
+  G_LOCK (proxy_vm);
+
+  member = dbus_message_get_member (message);
+
+  if (dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "DriveChanged") ||
+      dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "DriveConnected") ||
+      dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "DriveDisconnected") ||
+      dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "DriveEjectButton")) {
+
+    dbus_message_iter_init (message, &iter);
+    dbus_message_iter_get_basic (&iter, &id);
+    dbus_message_iter_next (&iter);
+
+    if (strcmp (member, "DriveChanged") == 0)
+      {
+        drive = g_hash_table_lookup (monitor->drives, id);
+        if (drive != NULL)
+          {
+            g_proxy_drive_update (drive, &iter);
+            signal_emit_in_idle (drive, "changed", NULL);
+            signal_emit_in_idle (monitor, "drive-changed", drive);
+          }
+      }
+    else if (strcmp (member, "DriveConnected") == 0)
+      {
+        drive = g_hash_table_lookup (monitor->drives, id);
+        if (drive == NULL)
+          {
+            drive = g_proxy_drive_new (monitor);
+            g_proxy_drive_update (drive, &iter);
+            g_hash_table_insert (monitor->drives, g_strdup (g_proxy_drive_get_id (drive)), drive);
+            signal_emit_in_idle (monitor, "drive-connected", drive);
+          }
+      }
+    else if (strcmp (member, "DriveDisconnected") == 0)
+      {
+        drive = g_hash_table_lookup (monitor->drives, id);
+        if (drive != NULL)
+          {
+            g_object_ref (drive);
+            g_hash_table_remove (monitor->drives, id);
+            signal_emit_in_idle (drive, "disconnected", NULL);
+            signal_emit_in_idle (monitor, "drive-disconnected", drive);
+            g_object_unref (drive);
+          }
+      }
+    else if (strcmp (member, "DriveEjectButton") == 0)
+      {
+        drive = g_hash_table_lookup (monitor->drives, id);
+        if (drive != NULL)
+          {
+            signal_emit_in_idle (drive, "eject-button", NULL);
+            signal_emit_in_idle (monitor, "drive-eject-button", drive);
+          }
+      }
+
+  } else if (dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "VolumeChanged") ||
+             dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "VolumeAdded") ||
+             dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "VolumeRemoved")) {
+
+    dbus_message_iter_init (message, &iter);
+    dbus_message_iter_get_basic (&iter, &id);
+    dbus_message_iter_next (&iter);
+
+    if (strcmp (member, "VolumeChanged") == 0)
+      {
+        volume = g_hash_table_lookup (monitor->volumes, id);
+        if (volume != NULL)
+          {
+            g_proxy_volume_update (volume, &iter);
+            signal_emit_in_idle (volume, "changed", NULL);
+            signal_emit_in_idle (monitor, "volume-changed", volume);
+          }
+      }
+    else if (strcmp (member, "VolumeAdded") == 0)
+      {
+        volume = g_hash_table_lookup (monitor->volumes, id);
+        if (volume == NULL)
+          {
+            volume = g_proxy_volume_new (monitor);
+            g_proxy_volume_update (volume, &iter);
+            g_hash_table_insert (monitor->volumes, g_strdup (g_proxy_volume_get_id (volume)), volume);
+            signal_emit_in_idle (monitor, "volume-added", volume);
+          }
+      }
+    else if (strcmp (member, "VolumeRemoved") == 0)
+      {
+        volume = g_hash_table_lookup (monitor->volumes, id);
+        if (volume != NULL)
+          {
+            g_object_ref (volume);
+            g_hash_table_remove (monitor->volumes, id);
+            signal_emit_in_idle (volume, "removed", NULL);
+            signal_emit_in_idle (monitor, "volume-removed", volume);
+            g_object_unref (volume);
+          }
+      }
+
+  } else if (dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "MountChanged") ||
+             dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "MountAdded") ||
+             dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "MountPreUnmount") ||
+             dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "MountRemoved")) {
+
+    dbus_message_iter_init (message, &iter);
+    dbus_message_iter_get_basic (&iter, &id);
+    dbus_message_iter_next (&iter);
+
+    if (strcmp (member, "MountChanged") == 0)
+      {
+        mount = g_hash_table_lookup (monitor->mounts, id);
+        if (mount != NULL)
+          {
+            g_proxy_mount_update (mount, &iter);
+            signal_emit_in_idle (mount, "changed", NULL);
+            signal_emit_in_idle (monitor, "mount-changed", mount);
+          }
+      }
+    else if (strcmp (member, "MountAdded") == 0)
+      {
+        mount = g_hash_table_lookup (monitor->mounts, id);
+        if (mount == NULL)
+          {
+            mount = g_proxy_mount_new (monitor);
+            g_proxy_mount_update (mount, &iter);
+            g_hash_table_insert (monitor->mounts, g_strdup (g_proxy_mount_get_id (mount)), mount);
+            signal_emit_in_idle (monitor, "mount-added", mount);
+          }
+      }
+    else if (strcmp (member, "MountPreUnmount") == 0)
+      {
+        mount = g_hash_table_lookup (monitor->mounts, id);
+        if (mount != NULL)
+          {
+            signal_emit_in_idle (mount, "pre-unmount", NULL);
+            signal_emit_in_idle (monitor, "mount-pre-unmount", mount);
+          }
+      }
+    else if (strcmp (member, "MountRemoved") == 0)
+      {
+        mount = g_hash_table_lookup (monitor->mounts, id);
+        if (mount != NULL)
+          {
+            g_object_ref (mount);
+            g_hash_table_remove (monitor->mounts, id);
+            signal_emit_in_idle (mount, "unmounted", NULL);
+            signal_emit_in_idle (monitor, "mount-removed", mount);
+            g_object_unref (mount);
+          }
+      }
+
+  }
+
+  G_UNLOCK (proxy_vm);
+
+  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static void
+g_proxy_volume_monitor_init (GProxyVolumeMonitor *monitor)
+{
+}
+
+static void
+g_proxy_volume_monitor_class_finalize (GProxyVolumeMonitorClass *klass)
+{
+  g_free (klass->dbus_name);
+}
+
+static void
+g_proxy_volume_monitor_class_intern_init_pre (GProxyVolumeMonitorClass *klass, gconstpointer class_data)
+{
+  klass->dbus_name = g_strdup ((char *) class_data);
+  g_proxy_volume_monitor_class_intern_init (klass);
+}
+
+static gboolean
+is_supported (void)
+{
+  if (the_session_bus != NULL)
+    return TRUE;
+  return FALSE;
+}
+
+static GVolume *
+adopt_orphan_mount (GMount *mount, GVolumeMonitor *monitor)
+{
+  GProxyVolumeMonitor *proxy_monitor = G_PROXY_VOLUME_MONITOR (monitor);
+  GFile *mount_root;
+  GProxyVolume *proxy_volume;
+  GVolume *ret;
+  GHashTableIter hash_iter;
+
+  ret = NULL;
+
+  G_LOCK (proxy_vm);
+
+  mount_root = g_mount_get_root (mount);
+
+  /* TODO: consider what happens if two volumes wants to adopt the same mount?
+   *
+   * e.g. imagine two GVolume objects with activation_roots
+   *
+   *      ssh://server/dir1
+   *      ssh://server/dir2
+   */
+
+  g_hash_table_iter_init (&hash_iter, proxy_monitor->volumes);
+  while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &proxy_volume))
+    {
+      GFile *activation_root;
+
+      activation_root = g_volume_get_activation_root (G_VOLUME (proxy_volume));
+      if (activation_root != NULL)
+        {
+          if (g_file_has_prefix (activation_root, mount_root) ||
+              g_file_equal (activation_root, mount_root))
+            {
+              g_proxy_volume_adopt_foreign_mount (proxy_volume, mount);
+              ret = g_object_ref (proxy_volume);
+              g_object_unref (activation_root);
+              goto found;
+            }
+          g_object_unref (activation_root);
+        }
+    }
+
+ found:
+  g_object_unref (mount_root);
+
+  G_UNLOCK (proxy_vm);
+  return ret;
+}
+
+static void
+g_proxy_volume_monitor_class_init (GProxyVolumeMonitorClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GVolumeMonitorClass *monitor_class = G_VOLUME_MONITOR_CLASS (klass);
+  GNativeVolumeMonitorClass *native_class = G_NATIVE_VOLUME_MONITOR_CLASS (klass);
+
+  gobject_class->constructor = g_proxy_volume_monitor_constructor;
+  gobject_class->finalize = g_proxy_volume_monitor_finalize;
+
+  monitor_class->get_mounts = get_mounts;
+  monitor_class->get_volumes = get_volumes;
+  monitor_class->get_connected_drives = get_connected_drives;
+  monitor_class->get_volume_for_uuid = get_volume_for_uuid;
+  monitor_class->get_mount_for_uuid = get_mount_for_uuid;
+  monitor_class->adopt_orphan_mount = adopt_orphan_mount;
+  monitor_class->is_supported = is_supported;
+
+  native_class->get_mount_for_mount_path = get_mount_for_mount_path;
+}
+
+/* Must be called with no locks held */
+static void
+seed_monitor (GProxyVolumeMonitor *monitor)
+{
+  DBusMessage *message;
+  DBusMessage *reply;
+  DBusError dbus_error;
+  DBusMessageIter iter_reply;
+  DBusMessageIter iter_array;
+
+  message = dbus_message_new_method_call (g_proxy_volume_monitor_get_dbus_name (monitor),
+                                          "/",
+                                          "org.gtk.Private.RemoteVolumeMonitor",
+                                          "List");
+  if (message == NULL)
+    {
+      g_warning ("Cannot allocate memory for DBusMessage");
+      goto fail;
+    }
+  dbus_error_init (&dbus_error);
+  reply = dbus_connection_send_with_reply_and_block (monitor->session_bus,
+                                                     message,
+                                                     -1,
+                                                     &dbus_error);
+  dbus_message_unref (message);
+  if (dbus_error_is_set (&dbus_error))
+    {
+      g_warning ("invoking List() failed for type %s: %s: %s",
+                 G_OBJECT_TYPE_NAME (monitor),
+                 dbus_error.name,
+                 dbus_error.message);
+      dbus_error_free (&dbus_error);
+      goto fail;
+    }
+
+  dbus_message_iter_init (reply, &iter_reply);
+
+  /* TODO: verify signature */
+
+  /* drives */
+  dbus_message_iter_recurse (&iter_reply, &iter_array);
+  while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
+    {
+      GProxyDrive *drive;
+      const char *id;
+      drive = g_proxy_drive_new (monitor);
+      g_proxy_drive_update (drive, &iter_array);
+      id = g_proxy_drive_get_id (drive);
+      g_hash_table_insert (monitor->drives, g_strdup (id), drive);
+      dbus_message_iter_next (&iter_array);
+    }
+  dbus_message_iter_next (&iter_reply);
+
+  /* volumes */
+  dbus_message_iter_recurse (&iter_reply, &iter_array);
+  while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
+    {
+      GProxyVolume *volume;
+      const char *id;
+      volume = g_proxy_volume_new (monitor);
+      g_proxy_volume_update (volume, &iter_array);
+      id = g_proxy_volume_get_id (volume);
+      g_hash_table_insert (monitor->volumes, g_strdup (id), volume);
+      dbus_message_iter_next (&iter_array);
+    }
+  dbus_message_iter_next (&iter_reply);
+
+  /* mounts */
+  dbus_message_iter_recurse (&iter_reply, &iter_array);
+  while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
+    {
+      GProxyMount *mount;
+      const char *id;
+      mount = g_proxy_mount_new (monitor);
+      g_proxy_mount_update (mount, &iter_array);
+      id = g_proxy_mount_get_id (mount);
+      g_hash_table_insert (monitor->mounts, g_strdup (id), mount);
+      dbus_message_iter_next (&iter_array);
+    }
+  dbus_message_iter_next (&iter_reply);
+
+  dbus_message_unref (reply);
+
+ fail:
+  ;
+}
+
+GProxyDrive *
+g_proxy_volume_monitor_get_drive_for_id  (GProxyVolumeMonitor *volume_monitor,
+                                          const char          *id)
+{
+  GProxyDrive *drive;
+
+  G_LOCK (proxy_vm);
+  drive = g_hash_table_lookup (volume_monitor->drives, id);
+  if (drive != NULL)
+    g_object_ref (drive);
+  G_UNLOCK (proxy_vm);
+
+  return drive;
+}
+
+GProxyVolume *
+g_proxy_volume_monitor_get_volume_for_id (GProxyVolumeMonitor *volume_monitor,
+                                          const char          *id)
+{
+  GProxyVolume *volume;
+
+  G_LOCK (proxy_vm);
+  volume = g_hash_table_lookup (volume_monitor->volumes, id);
+  if (volume != NULL)
+    g_object_ref (volume);
+  G_UNLOCK (proxy_vm);
+
+  return volume;
+}
+
+GProxyMount *
+g_proxy_volume_monitor_get_mount_for_id  (GProxyVolumeMonitor *volume_monitor,
+                                          const char          *id)
+{
+  GProxyMount *mount;
+
+  G_LOCK (proxy_vm);
+  mount = g_hash_table_lookup (volume_monitor->mounts, id);
+  if (mount != NULL)
+    g_object_ref (mount);
+  G_UNLOCK (proxy_vm);
+
+  return mount;
+}
+
+
+GHashTable *
+_get_identifiers (DBusMessageIter *iter)
+{
+  GHashTable *hash_table;
+  DBusMessageIter iter_array;
+
+  hash_table = g_hash_table_new_full (g_str_hash,
+                                      g_str_equal,
+                                      g_free,
+                                      g_free);
+
+  dbus_message_iter_recurse (iter, &iter_array);
+  while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
+    {
+      DBusMessageIter iter_dict_entry;
+      const char *key;
+      const char *value;
+
+      dbus_message_iter_recurse (&iter_array, &iter_dict_entry);
+      dbus_message_iter_get_basic (&iter_dict_entry, &key);
+      dbus_message_iter_next (&iter_dict_entry);
+      dbus_message_iter_get_basic (&iter_dict_entry, &value);
+
+      g_hash_table_insert (hash_table, g_strdup (key), g_strdup (value));
+
+      dbus_message_iter_next (&iter_array);
+    }
+
+  return hash_table;
+}
+
+GIcon *
+_g_icon_new_from_serialized_data (const char *gicon_data)
+{
+  char **tokens;
+  GIcon *icon;
+  guint num_tokens;
+
+  g_return_val_if_fail (gicon_data != NULL, NULL);
+
+  icon = NULL;
+
+  tokens = g_strsplit (gicon_data, " ", 0);
+  num_tokens = g_strv_length (tokens);
+
+  if (num_tokens < 2)
+    {
+      g_warning ("malformed GIcon data \"%s\"", gicon_data);
+      goto out;
+    }
+
+  if (strcmp (tokens[0], "GFileIcon") == 0)
+    {
+      GFile *file;
+      char *unescaped_uri;
+
+      if (num_tokens != 2)
+        {
+          g_warning ("malformed GFileIcon gicon_data \"%s\"", gicon_data);
+          goto out;
+        }
+
+      unescaped_uri = g_uri_unescape_string (tokens[1], NULL);
+      file = g_file_new_for_uri (unescaped_uri);
+      icon = g_file_icon_new (file);
+      g_object_unref (file);
+      g_free (unescaped_uri);
+    }
+  else if (strcmp (tokens[0], "GThemedIcon") == 0)
+    {
+      int n;
+
+      icon = NULL;
+      for (n = 1; n < num_tokens; n++)
+        {
+          char *unescaped_name;
+
+          unescaped_name = g_uri_unescape_string (tokens[n], NULL);
+          if (icon == NULL)
+            icon = g_themed_icon_new (unescaped_name);
+          else
+            g_themed_icon_append_name (G_THEMED_ICON (icon), unescaped_name);
+          g_free (unescaped_name);
+        }
+    }
+  else
+    {
+      g_warning ("cannot parse gicon_data \"%s\"; please add support", gicon_data);
+    }
+
+ out:
+  g_strfreev (tokens);
+  return icon;
+}
+
+DBusConnection *
+g_proxy_volume_monitor_get_dbus_connection (GProxyVolumeMonitor *volume_monitor)
+{
+  return dbus_connection_ref (volume_monitor->session_bus);
+}
+
+const char *
+g_proxy_volume_monitor_get_dbus_name (GProxyVolumeMonitor *volume_monitor)
+{
+  GProxyVolumeMonitorClass *klass = G_PROXY_VOLUME_MONITOR_CLASS (G_OBJECT_GET_CLASS (volume_monitor));
+  return klass->dbus_name;
+}
+
+static void
+register_volume_monitor (GTypeModule *type_module,
+                         const char *type_name,
+                         const char *dbus_name,
+                         gboolean is_native,
+                         int priority)
+{
+  GType type;
+  const GTypeInfo type_info = {
+    sizeof (GProxyVolumeMonitorClass),
+    (GBaseInitFunc) NULL,
+    (GBaseFinalizeFunc) NULL,
+    (GClassInitFunc) g_proxy_volume_monitor_class_intern_init_pre,
+    (GClassFinalizeFunc) g_proxy_volume_monitor_class_finalize,
+    (gconstpointer) g_strdup (dbus_name),  /* class_data (leaked!) */
+    sizeof (GProxyVolumeMonitor),
+    0,      /* n_preallocs */
+    (GInstanceInitFunc) g_proxy_volume_monitor_init,
+    NULL    /* value_table */
+  };
+
+  type = g_type_module_register_type (type_module,
+                                      G_TYPE_PROXY_VOLUME_MONITOR,
+                                      type_name,
+                                      &type_info,
+                                      0 /* type_flags */);
+
+  g_io_extension_point_implement (is_native ? G_NATIVE_VOLUME_MONITOR_EXTENSION_POINT_NAME :
+                                              G_VOLUME_MONITOR_EXTENSION_POINT_NAME,
+                                  type,
+                                  type_name,
+                                  priority);
+}
+
+void
+g_proxy_volume_monitor_setup_session_bus_connection (void)
+{
+  DBusError dbus_error;
+
+  G_LOCK (proxy_vm);
+  if (the_session_bus != NULL)
+    {
+      g_warning ("session bus connection is already up!");
+      dbus_connection_ref (the_session_bus);
+      goto out;
+    }
+
+  /* This is so that system daemons can use gio
+   * without spawning private dbus instances.
+   * See bug 526454.
+   */
+  if (g_getenv ("DBUS_SESSION_BUS_ADDRESS") == NULL)
+    {
+      goto out;
+    }
+
+  dbus_error_init (&dbus_error);
+  the_session_bus = dbus_bus_get_private (DBUS_BUS_SESSION, &dbus_error);
+  if (dbus_error_is_set (&dbus_error)) {
+    g_warning ("cannot connect to the session bus: %s: %s", dbus_error.name, dbus_error.message);
+    dbus_error_free (&dbus_error);
+    goto out;
+  }
+
+  _g_dbus_connection_integrate_with_main (the_session_bus);
+
+  the_volume_monitors = g_hash_table_new_full (g_int_hash, g_int_equal, NULL, NULL);
+
+ out:
+  G_UNLOCK (proxy_vm);
+}
+
+void
+g_proxy_volume_monitor_teardown_session_bus_connection (void)
+{
+  G_LOCK (proxy_vm);
+  if (the_session_bus != NULL)
+    {
+      /* it would be nice to check that refcount==1 here */
+      _g_dbus_connection_remove_from_main (the_session_bus);
+      dbus_connection_close (the_session_bus);
+      the_session_bus = NULL;
+
+      g_hash_table_unref (the_volume_monitors);
+      the_volume_monitors = NULL;
+    }
+  G_UNLOCK (proxy_vm);
+}
+
+static gboolean
+is_remote_monitor_supported (const char *dbus_name)
+{
+  DBusMessage *message;
+  DBusMessage *reply;
+  DBusError dbus_error;
+  dbus_bool_t is_supported;
+
+  message = NULL;
+  reply = NULL;
+  is_supported = FALSE;
+
+  message = dbus_message_new_method_call (dbus_name,
+                                          "/",
+                                          "org.gtk.Private.RemoteVolumeMonitor",
+                                          "IsSupported");
+  if (message == NULL)
+    {
+      g_warning ("Cannot allocate memory for DBusMessage");
+      goto fail;
+    }
+  dbus_error_init (&dbus_error);
+  reply = dbus_connection_send_with_reply_and_block (the_session_bus,
+                                                     message,
+                                                     -1,
+                                                     &dbus_error);
+  if (dbus_error_is_set (&dbus_error))
+    {
+      g_warning ("invoking IsSupported() failed for remote volume monitor with dbus name %s: %s: %s",
+                 dbus_name,
+                 dbus_error.name,
+                 dbus_error.message);
+      dbus_error_free (&dbus_error);
+      goto fail;
+    }
+
+  if (!dbus_message_get_args (reply, &dbus_error,
+                              DBUS_TYPE_BOOLEAN, &is_supported,
+                              DBUS_TYPE_INVALID))
+    {
+      g_warning ("Error parsing args in reply for IsSupported(): %s: %s", dbus_error.name, dbus_error.message);
+      dbus_error_free (&dbus_error);
+      goto fail;
+    }
+
+  if (!is_supported)
+    g_warning ("remote volume monitor with dbus name %s is not supported", dbus_name);
+
+ fail:
+  if (message != NULL)
+    dbus_message_unref (message);
+  if (reply != NULL)
+    dbus_message_unref (reply);
+  return is_supported;
+}
+
+void
+g_proxy_volume_monitor_register (GIOModule *module)
+{
+  GDir *dir;
+  GError *error;
+
+  /* first register the abstract base type... */
+  g_proxy_volume_monitor_register_type (G_TYPE_MODULE (module));
+
+  /* ... then register instantiable types for each remote volume
+   * monitor - each remote volume monitor is defined in a key-value
+   * file in $(datadir)/gvfs/remote-volume-monitors that must have
+   * the suffix .monitor. Each file specifies
+   *
+   * - the name of the volume monitor
+   * - the name of the D-Bus service
+   * - whether the volume monitor is native
+   *   - and if so the priority
+   */
+
+  error = NULL;
+  dir = g_dir_open (REMOTE_VOLUME_MONITORS_DIR, 0, &error);
+  if (dir == NULL)
+    {
+      g_warning ("cannot open directory " REMOTE_VOLUME_MONITORS_DIR ": %s", error->message);
+      g_error_free (error);
+    }
+  else
+    {
+      const char *name;
+
+      while ((name = g_dir_read_name (dir)) != NULL)
+        {
+          GKeyFile *key_file;
+          char *type_name;
+          char *path;
+          char *dbus_name;
+          gboolean is_native;
+          int native_priority;
+
+          type_name = NULL;
+          key_file = NULL;
+          dbus_name = NULL;
+          path = NULL;
+
+          if (!g_str_has_suffix (name, ".monitor"))
+            goto cont;
+
+          path = g_build_filename (REMOTE_VOLUME_MONITORS_DIR, name, NULL);
+
+          key_file = g_key_file_new ();
+          error = NULL;
+          if (!g_key_file_load_from_file (key_file, path, G_KEY_FILE_NONE, &error))
+            {
+              g_warning ("error loading key-value file %s: %s", path, error->message);
+              g_error_free (error);
+              goto cont;
+            }
+
+          type_name = g_key_file_get_string (key_file, "RemoteVolumeMonitor", "Name", &error);
+          if (error != NULL)
+            {
+              g_warning ("error extracting Name key from %s: %s", path, error->message);
+              g_error_free (error);
+              goto cont;
+            }
+
+          dbus_name = g_key_file_get_string (key_file, "RemoteVolumeMonitor", "DBusName", &error);
+          if (error != NULL)
+            {
+              g_warning ("error extracting DBusName key from %s: %s", path, error->message);
+              g_error_free (error);
+              goto cont;
+            }
+
+          is_native = g_key_file_get_boolean (key_file, "RemoteVolumeMonitor", "IsNative", &error);
+          if (error != NULL)
+            {
+              g_warning ("error extracting IsNative key from %s: %s", path, error->message);
+              g_error_free (error);
+              goto cont;
+            }
+
+          if (is_native)
+            {
+              native_priority = g_key_file_get_integer (key_file, "RemoteVolumeMonitor", "NativePriority", &error);
+              if (error != NULL)
+                {
+                  g_warning ("error extracting NativePriority key from %s: %s", path, error->message);
+                  g_error_free (error);
+                  goto cont;
+                }
+            }
+          else
+            {
+              native_priority = 0;
+            }
+
+          if (is_remote_monitor_supported (dbus_name))
+            {
+              register_volume_monitor (G_TYPE_MODULE (module),
+                                       type_name,
+                                       dbus_name,
+                                       is_native,
+                                       native_priority);
+            }
+
+        cont:
+
+          g_free (type_name);
+          g_free (dbus_name);
+          g_free (path);
+          if (key_file != NULL)
+              g_key_file_free (key_file);
+        }
+      g_dir_close (dir);
+    }
+}

Added: trunk/monitor/proxy/gproxyvolumemonitor.h
==============================================================================
--- (empty file)
+++ trunk/monitor/proxy/gproxyvolumemonitor.h	Tue Jul  8 16:36:45 2008
@@ -0,0 +1,75 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2006-2008 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_PROXY_VOLUME_MONITOR_H__
+#define __G_PROXY_VOLUME_MONITOR_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+#include <gio/gunixmounts.h>
+#include <dbus/dbus.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_PROXY_VOLUME_MONITOR        (g_proxy_volume_monitor_get_type ())
+#define G_PROXY_VOLUME_MONITOR(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_PROXY_VOLUME_MONITOR, GProxyVolumeMonitor))
+#define G_PROXY_VOLUME_MONITOR_CLASS(k)    (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_PROXY_VOLUME_MONITOR, GProxyVolumeMonitorClass))
+#define G_IS_PROXY_VOLUME_MONITOR(o)       (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_PROXY_VOLUME_MONITOR))
+#define G_IS_PROXY_VOLUME_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_PROXY_VOLUME_MONITOR))
+
+typedef struct _GProxyVolumeMonitor GProxyVolumeMonitor;
+typedef struct _GProxyVolumeMonitorClass GProxyVolumeMonitorClass;
+
+/* Forward definitions */
+typedef struct _GProxyDrive GProxyDrive;
+typedef struct _GProxyVolume GProxyVolume;
+typedef struct _GProxyMount GProxyMount;
+
+struct _GProxyVolumeMonitorClass {
+  GNativeVolumeMonitorClass parent_class;
+  char *dbus_name;
+};
+
+GType g_proxy_volume_monitor_get_type (void) G_GNUC_CONST;
+
+void            g_proxy_volume_monitor_register          (GIOModule           *module);
+GProxyDrive    *g_proxy_volume_monitor_get_drive_for_id  (GProxyVolumeMonitor *volume_monitor,
+                                                          const char          *id);
+GProxyVolume   *g_proxy_volume_monitor_get_volume_for_id (GProxyVolumeMonitor *volume_monitor,
+                                                          const char          *id);
+GProxyMount    *g_proxy_volume_monitor_get_mount_for_id  (GProxyVolumeMonitor *volume_monitor,
+                                                          const char          *id);
+DBusConnection *g_proxy_volume_monitor_get_dbus_connection (GProxyVolumeMonitor *volume_monitor);
+const char     *g_proxy_volume_monitor_get_dbus_name       (GProxyVolumeMonitor *volume_monitor);
+
+void g_proxy_volume_monitor_setup_session_bus_connection (void);
+void g_proxy_volume_monitor_teardown_session_bus_connection (void);
+
+
+GHashTable *_get_identifiers (DBusMessageIter *iter);
+
+GIcon *_g_icon_new_from_serialized_data (const char *gicon_data);
+
+G_END_DECLS
+
+#endif /* __G_PROXY_VOLUME_MONITOR_H__ */

Added: trunk/monitor/proxy/gvfsproxyvolumemonitordaemon.c
==============================================================================
--- (empty file)
+++ trunk/monitor/proxy/gvfsproxyvolumemonitordaemon.c	Tue Jul  8 16:36:45 2008
@@ -0,0 +1,1091 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2006-2008 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 <string.h>
+#include <gio/gio.h>
+#include <dbus/dbus.h>
+#include <glib/gi18n.h>
+
+#include "gdbusutils.h"
+#include "gvfsproxyvolumemonitordaemon.h"
+
+static GVolumeMonitor *monitor = NULL;
+static DBusConnection *connection = NULL;
+static GType the_volume_monitor_type;
+
+static void monitor_try_create (void);
+
+static char *
+_g_icon_serialize (GIcon *icon)
+{
+  char *ret;
+
+  g_return_val_if_fail (icon != NULL, NULL);
+  g_return_val_if_fail (G_IS_ICON (icon), NULL);
+
+  if (G_IS_FILE_ICON (icon))
+    {
+      GFileIcon *file_icon = G_FILE_ICON (icon);
+      GFile *file;
+      char *uri;
+      char *escaped_uri;
+
+      file = g_file_icon_get_file (file_icon);
+      uri = g_file_get_uri (file);
+      escaped_uri = g_uri_escape_string (uri, NULL, TRUE);
+
+      ret = g_strdup_printf ("GFileIcon %s", escaped_uri);
+
+      g_object_unref (file);
+      g_free (uri);
+      g_free (escaped_uri);
+    }
+  else if (G_IS_THEMED_ICON (icon))
+    {
+      GThemedIcon *themed_icon = G_THEMED_ICON (icon);
+      char *escaped_name;
+      char **names;
+      GString *s;
+
+      g_object_get (themed_icon,
+                    "names", &names,
+                    NULL);
+
+      s = g_string_new ("GThemedIcon");
+
+      if (names != NULL)
+        {
+          int n;
+          for (n = 0; names[n] != NULL; n++)
+            {
+              escaped_name = g_uri_escape_string (names[n], NULL, TRUE);
+              g_string_append_c (s, ' ');
+              g_string_append (s, escaped_name);
+              g_free (escaped_name);
+            }
+        }
+
+      ret = g_string_free (s, FALSE);
+
+      g_strfreev (names);
+    }
+  else
+    {
+      ret = NULL;
+      g_warning ("unknown icon type; please add support");
+    }
+
+  return ret;
+}
+
+/* string               id
+ * string               name
+ * string               gicon_data
+ * boolean              can-eject
+ * boolean              can-poll-for-media
+ * boolean              has-media
+ * boolean              is-media-removable
+ * array:string         volume-ids
+ * dict:string->string  identifiers
+ */
+#define DRIVE_STRUCT_TYPE "(sssbbbbasa{ss})"
+
+static void
+append_drive (GDrive *drive, DBusMessageIter *iter_array)
+{
+  DBusMessageIter iter_struct;
+  DBusMessageIter iter_volume_array;
+  DBusMessageIter iter_identifiers;
+  char *id;
+  char *name;
+  GIcon *icon;
+  char *icon_data;
+  gboolean can_eject;
+  gboolean can_poll_for_media;
+  gboolean has_media;
+  gboolean is_media_removable;
+  GList *volumes, *l;
+  char **identifiers;
+  int n;
+
+  dbus_message_iter_open_container (iter_array, DBUS_TYPE_STRUCT, NULL, &iter_struct);
+
+  id = g_strdup_printf ("%p", drive);
+  name = g_drive_get_name (drive);
+  icon = g_drive_get_icon (drive);
+  icon_data = _g_icon_serialize (icon);
+  can_eject = g_drive_can_eject (drive);
+  can_poll_for_media = g_drive_can_poll_for_media (drive);
+  has_media = g_drive_has_media (drive);
+  is_media_removable = g_drive_is_media_removable (drive);
+  volumes = g_drive_get_volumes (drive);
+  identifiers = g_drive_enumerate_identifiers (drive);
+
+  if (name == NULL)
+    name = g_strdup ("");
+
+  dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &id);
+  dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &name);
+  dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &icon_data);
+  dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_BOOLEAN, &can_eject);
+  dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_BOOLEAN, &can_poll_for_media);
+  dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_BOOLEAN, &has_media);
+  dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_BOOLEAN, &is_media_removable);
+
+  dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "s", &iter_volume_array);
+  for (l = volumes; l != NULL; l = l->next)
+    {
+      GVolume *volume = G_VOLUME (l->data);
+      char *volume_id;
+      volume_id = g_strdup_printf ("%p", volume);
+      dbus_message_iter_append_basic (&iter_volume_array, DBUS_TYPE_STRING, &volume_id);
+      g_free (volume_id);
+    }
+  dbus_message_iter_close_container (&iter_struct, &iter_volume_array);
+
+  dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "{ss}", &iter_identifiers);
+  for (n = 0; identifiers[n] != NULL; n++)
+    {
+      DBusMessageIter iter_dict_entry;
+      char *id_value;
+      id_value = g_drive_get_identifier (drive, identifiers[n]);
+      dbus_message_iter_open_container (&iter_identifiers,
+                                        DBUS_TYPE_DICT_ENTRY,
+                                        NULL,
+                                        &iter_dict_entry);
+      dbus_message_iter_append_basic (&iter_dict_entry, DBUS_TYPE_STRING, &(identifiers[n]));
+      dbus_message_iter_append_basic (&iter_dict_entry, DBUS_TYPE_STRING, &id_value);
+      dbus_message_iter_close_container (&iter_identifiers, &iter_dict_entry);
+      g_free (id_value);
+    }
+  dbus_message_iter_close_container (&iter_struct, &iter_identifiers);
+
+  g_strfreev (identifiers);
+  g_list_foreach (volumes, (GFunc) g_object_unref, NULL);
+  g_list_free (volumes);
+  g_free (icon_data);
+  g_object_unref (icon);
+  g_free (name);
+  g_free (id);
+
+  dbus_message_iter_close_container (iter_array, &iter_struct);
+}
+
+/* string               id
+ * string               name
+ * string               gicon_data
+ * string               uuid
+ * string               activation_uri
+ * boolean              can-mount
+ * boolean              should-automount
+ * string               drive-id
+ * string               mount-id
+ * dict:string->string  identifiers
+ */
+#define VOLUME_STRUCT_TYPE "(sssssbbssa{ss})"
+
+static void
+append_volume (GVolume *volume, DBusMessageIter *iter_array)
+{
+  DBusMessageIter iter_struct;
+  DBusMessageIter iter_identifiers;
+  char *id;
+  char *name;
+  GIcon *icon;
+  char *icon_data;
+  char *uuid;
+  GFile *activation_root;
+  char *activation_uri;
+  gboolean can_mount;
+  gboolean should_automount;
+  GDrive *drive;
+  char *drive_id;
+  GMount *mount;
+  char *mount_id;
+  char **identifiers;
+  int n;
+
+  dbus_message_iter_open_container (iter_array, DBUS_TYPE_STRUCT, NULL, &iter_struct);
+
+  id = g_strdup_printf ("%p", volume);
+  name = g_volume_get_name (volume);
+  icon = g_volume_get_icon (volume);
+  icon_data = _g_icon_serialize (icon);
+  uuid = g_volume_get_uuid (volume);
+  activation_root = g_volume_get_activation_root (volume);
+  if (activation_root == NULL)
+    activation_uri = g_strdup ("");
+  else
+    activation_uri = g_file_get_uri (activation_root);
+  can_mount = g_volume_can_mount (volume);
+  should_automount = g_volume_should_automount (volume);
+  drive = g_volume_get_drive (volume);
+  if (drive == NULL)
+    drive_id = g_strdup ("");
+  else
+    drive_id = g_strdup_printf ("%p", drive);
+  mount = g_volume_get_mount (volume);
+  if (mount == NULL)
+    mount_id = g_strdup ("");
+  else
+    mount_id = g_strdup_printf ("%p", mount);
+  identifiers = g_volume_enumerate_identifiers (volume);
+
+  if (name == NULL)
+    name = g_strdup ("");
+  if (uuid == NULL)
+    uuid = g_strdup ("");
+
+  dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &id);
+  dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &name);
+  dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &icon_data);
+  dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &uuid);
+  dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &activation_uri);
+  dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_BOOLEAN, &can_mount);
+  dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_BOOLEAN, &should_automount);
+  dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &drive_id);
+  dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &mount_id);
+
+  dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "{ss}", &iter_identifiers);
+  for (n = 0; identifiers[n] != NULL; n++)
+    {
+      DBusMessageIter iter_dict_entry;
+      char *id_value;
+      id_value = g_volume_get_identifier (volume, identifiers[n]);
+      dbus_message_iter_open_container (&iter_identifiers,
+                                        DBUS_TYPE_DICT_ENTRY,
+                                        NULL,
+                                        &iter_dict_entry);
+      dbus_message_iter_append_basic (&iter_dict_entry, DBUS_TYPE_STRING, &(identifiers[n]));
+      dbus_message_iter_append_basic (&iter_dict_entry, DBUS_TYPE_STRING, &id_value);
+      dbus_message_iter_close_container (&iter_identifiers, &iter_dict_entry);
+      g_free (id_value);
+    }
+  dbus_message_iter_close_container (&iter_struct, &iter_identifiers);
+
+  g_strfreev (identifiers);
+  g_free (mount_id);
+  if (mount != NULL)
+    g_object_unref (mount);
+  g_free (drive_id);
+  if (drive != NULL)
+    g_object_unref (drive);
+  g_free (uuid);
+  if (activation_root != NULL)
+    g_object_unref (activation_root);
+  g_free (activation_uri);
+  g_free (icon_data);
+  g_object_unref (icon);
+  g_free (name);
+  g_free (id);
+
+  dbus_message_iter_close_container (iter_array, &iter_struct);
+}
+
+/* string               id
+ * string               name
+ * string               gicon_data
+ * string               uuid
+ * string               root_uri
+ * boolean              can-unmount
+ * string               volume-id
+ * array:string         x-content-types
+ */
+#define MOUNT_STRUCT_TYPE "(sssssbsas)"
+
+static void
+append_mount (GMount *mount, DBusMessageIter *iter_array)
+{
+  DBusMessageIter iter_struct;
+  DBusMessageIter iter_x_content_types_array;
+  char *id;
+  char *name;
+  GIcon *icon;
+  char *icon_data;
+  char *uuid;
+  GFile *root;
+  char *root_uri;
+  gboolean can_unmount;
+  GVolume *volume;
+  char *volume_id;
+
+  dbus_message_iter_open_container (iter_array, DBUS_TYPE_STRUCT, NULL, &iter_struct);
+
+  id = g_strdup_printf ("%p", mount);
+  name = g_mount_get_name (mount);
+  icon = g_mount_get_icon (mount);
+  icon_data = _g_icon_serialize (icon);
+  uuid = g_mount_get_uuid (mount);
+  root = g_mount_get_root (mount);
+  root_uri = g_file_get_uri (root);
+  can_unmount = g_mount_can_unmount (mount);
+  volume = g_mount_get_volume (mount);
+  if (volume == NULL)
+    volume_id = g_strdup ("");
+  else
+    volume_id = g_strdup_printf ("%p", volume);
+
+  if (name == NULL)
+    name = g_strdup ("");
+
+  dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &id);
+  dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &name);
+  dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &icon_data);
+  dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &uuid);
+  dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &root_uri);
+  dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_BOOLEAN, &can_unmount);
+  dbus_message_iter_append_basic (&iter_struct, DBUS_TYPE_STRING, &volume_id);
+
+  dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "s", &iter_x_content_types_array);
+  /* TODO: append x-content types */
+  dbus_message_iter_close_container (&iter_struct, &iter_x_content_types_array);
+
+  g_free (volume_id);
+  if (volume != NULL)
+    g_object_unref (volume);
+  g_free (root_uri);
+  g_object_unref (root);
+  g_free (uuid);
+  g_free (icon_data);
+  g_object_unref (icon);
+  g_free (name);
+  g_free (id);
+
+  dbus_message_iter_close_container (iter_array, &iter_struct);
+}
+
+static DBusHandlerResult
+handle_list (DBusConnection *connection, DBusMessage *message)
+{
+  GList *drives;
+  GList *volumes;
+  GList *mounts;
+  DBusMessageIter iter;
+  DBusMessageIter iter_array;
+  DBusMessage *reply;
+
+  drives = g_volume_monitor_get_connected_drives (monitor);
+  volumes = g_volume_monitor_get_volumes (monitor);
+  mounts = g_volume_monitor_get_mounts (monitor);
+
+  reply = dbus_message_new_method_return (message);
+  dbus_message_iter_init_append (reply, &iter);
+
+  dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, DRIVE_STRUCT_TYPE, &iter_array);
+  g_list_foreach (drives, (GFunc) append_drive, &iter_array);
+  dbus_message_iter_close_container (&iter, &iter_array);
+
+  dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, VOLUME_STRUCT_TYPE, &iter_array);
+  g_list_foreach (volumes, (GFunc) append_volume, &iter_array);
+  dbus_message_iter_close_container (&iter, &iter_array);
+
+  dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, MOUNT_STRUCT_TYPE, &iter_array);
+  g_list_foreach (mounts, (GFunc) append_mount, &iter_array);
+  dbus_message_iter_close_container (&iter, &iter_array);
+
+  g_list_foreach (drives, (GFunc) g_object_unref, NULL);
+  g_list_free (drives);
+  g_list_foreach (volumes, (GFunc) g_object_unref, NULL);
+  g_list_free (volumes);
+  g_list_foreach (mounts, (GFunc) g_object_unref, NULL);
+  g_list_free (mounts);
+
+  dbus_connection_send (connection, reply, NULL);
+  dbus_message_unref (reply);
+
+  return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+mount_unmount_cb (GMount *mount, GAsyncResult *result, DBusMessage *message)
+{
+  GError *error;
+  DBusMessage *reply;
+
+  error = NULL;
+  if (!g_mount_unmount_finish (mount, result, &error))
+    {
+      reply = _dbus_message_new_from_gerror (message, error);
+      g_error_free (error);
+    }
+  else
+    {
+      reply = dbus_message_new_method_return (message);
+    }
+
+  dbus_connection_send (connection, reply, NULL);
+  dbus_message_unref (message);
+  dbus_message_unref (reply);
+}
+
+static DBusHandlerResult
+handle_mount_unmount (DBusConnection *connection, DBusMessage *message)
+{
+  const char *id;
+  dbus_uint32_t unmount_flags;
+  DBusError dbus_error;
+  GList *mounts, *l;
+  GMount *mount;
+  DBusHandlerResult ret;
+
+  mounts = NULL;
+  unmount_flags = 0;
+  ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+  dbus_error_init (&dbus_error);
+  if (!dbus_message_get_args (message, &dbus_error,
+                              DBUS_TYPE_STRING, &id,
+                              DBUS_TYPE_UINT32 &unmount_flags,
+                              DBUS_TYPE_INVALID))
+    {
+      g_warning ("Error parsing args for MountUnmount(): %s: %s", dbus_error.name, dbus_error.message);
+      dbus_error_free (&dbus_error);
+      goto out;
+    }
+
+  ret = DBUS_HANDLER_RESULT_HANDLED;
+
+  mount = NULL;
+  mounts = g_volume_monitor_get_mounts (monitor);
+  for (l = mounts; l != NULL; l = l->next)
+    {
+      char *mount_id;
+
+      mount = G_MOUNT (l->data);
+      mount_id = g_strdup_printf ("%p", mount);
+      if (strcmp (mount_id, id) == 0)
+        break;
+
+      g_free (mount_id);
+    }
+  if (l == NULL)
+    mount = NULL;
+
+  if (mount == NULL)
+    {
+      DBusMessage *reply;
+      reply = dbus_message_new_error (message,
+                                      "org.gtk.Private.RemoteVolumeMonitor.NotFound",
+                                      "The given mount was not found");
+      dbus_connection_send (connection, reply, NULL);
+      dbus_message_unref (reply);
+      goto out;
+    }
+
+  g_mount_unmount (mount,
+                   unmount_flags,
+                   NULL,
+                   (GAsyncReadyCallback) mount_unmount_cb,
+                   dbus_message_ref (message));
+
+ out:
+  if (mounts != NULL)
+    {
+      g_list_foreach (mounts, (GFunc) g_object_unref, NULL);
+      g_list_free (mounts);
+    }
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+volume_mount_cb (GVolume *volume, GAsyncResult *result, DBusMessage *message)
+{
+  GError *error;
+  DBusMessage *reply;
+
+  error = NULL;
+  if (!g_volume_mount_finish (volume, result, &error))
+    {
+      reply = _dbus_message_new_from_gerror (message, error);
+      g_error_free (error);
+    }
+  else
+    {
+      reply = dbus_message_new_method_return (message);
+    }
+
+  dbus_connection_send (connection, reply, NULL);
+  dbus_message_unref (message);
+  dbus_message_unref (reply);
+}
+
+static DBusHandlerResult
+handle_volume_mount (DBusConnection *connection, DBusMessage *message)
+{
+  const char *id;
+  dbus_uint32_t mount_flags;
+  dbus_bool_t use_mount_operation;
+  DBusError dbus_error;
+  GList *volumes, *l;
+  GVolume *volume;
+  DBusHandlerResult ret;
+  GMountOperation *mount_operation;
+
+  volume = NULL;
+  mount_flags = 0;
+  ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+  dbus_error_init (&dbus_error);
+  if (!dbus_message_get_args (message, &dbus_error,
+                              DBUS_TYPE_STRING, &id,
+                              DBUS_TYPE_UINT32 &mount_flags,
+                              DBUS_TYPE_BOOLEAN, &use_mount_operation,
+                              DBUS_TYPE_INVALID))
+    {
+      g_warning ("Error parsing args for VolumeMount(): %s: %s", dbus_error.name, dbus_error.message);
+      dbus_error_free (&dbus_error);
+      goto out;
+    }
+
+  ret = DBUS_HANDLER_RESULT_HANDLED;
+
+  volume = NULL;
+  volumes = g_volume_monitor_get_volumes (monitor);
+  for (l = volumes; l != NULL; l = l->next)
+    {
+      char *volume_id;
+
+      volume = G_VOLUME (l->data);
+      volume_id = g_strdup_printf ("%p", volume);
+      if (strcmp (volume_id, id) == 0)
+        break;
+
+      g_free (volume_id);
+    }
+  if (l == NULL)
+    volume = NULL;
+
+  if (volume == NULL)
+    {
+      DBusMessage *reply;
+      reply = dbus_message_new_error (message,
+                                      "org.gtk.Private.RemoteVolumeMonitor.NotFound",
+                                      "The given volume was not found");
+      dbus_connection_send (connection, reply, NULL);
+      dbus_message_unref (reply);
+      goto out;
+    }
+
+  mount_operation = NULL;
+  if (use_mount_operation)
+    mount_operation = g_mount_operation_new ();
+
+  g_volume_mount (volume,
+                  mount_flags,
+                  mount_operation,
+                  NULL,
+                  (GAsyncReadyCallback) volume_mount_cb,
+                  dbus_message_ref (message));
+
+  if (mount_operation != NULL)
+    g_object_unref (mount_operation);
+
+ out:
+  if (volumes != NULL)
+    {
+      g_list_foreach (volumes, (GFunc) g_object_unref, NULL);
+      g_list_free (volumes);
+    }
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+drive_eject_cb (GDrive *drive, GAsyncResult *result, DBusMessage *message)
+{
+  GError *error;
+  DBusMessage *reply;
+
+  error = NULL;
+  if (!g_drive_eject_finish (drive, result, &error))
+    {
+      reply = _dbus_message_new_from_gerror (message, error);
+      g_error_free (error);
+    }
+  else
+    {
+      reply = dbus_message_new_method_return (message);
+    }
+
+  dbus_connection_send (connection, reply, NULL);
+  dbus_message_unref (message);
+  dbus_message_unref (reply);
+}
+
+static DBusHandlerResult
+handle_drive_eject (DBusConnection *connection, DBusMessage *message)
+{
+  const char *id;
+  dbus_uint32_t unmount_flags;
+  DBusError dbus_error;
+  GList *drives, *l;
+  GDrive *drive;
+  DBusHandlerResult ret;
+
+  drive = NULL;
+  unmount_flags = 0;
+  ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+  dbus_error_init (&dbus_error);
+  if (!dbus_message_get_args (message, &dbus_error,
+                              DBUS_TYPE_STRING, &id,
+                              DBUS_TYPE_UINT32 &unmount_flags,
+                              DBUS_TYPE_INVALID))
+    {
+      g_warning ("Error parsing args for DriveEject(): %s: %s", dbus_error.name, dbus_error.message);
+      dbus_error_free (&dbus_error);
+      goto out;
+    }
+
+  ret = DBUS_HANDLER_RESULT_HANDLED;
+
+  drive = NULL;
+  drives = g_volume_monitor_get_connected_drives (monitor);
+  for (l = drives; l != NULL; l = l->next)
+    {
+      char *drive_id;
+
+      drive = G_DRIVE (l->data);
+      drive_id = g_strdup_printf ("%p", drive);
+      if (strcmp (drive_id, id) == 0)
+        break;
+
+      g_free (drive_id);
+    }
+  if (l == NULL)
+    drive = NULL;
+
+  if (drive == NULL)
+    {
+      DBusMessage *reply;
+      reply = dbus_message_new_error (message,
+                                      "org.gtk.Private.RemoteVolumeMonitor.NotFound",
+                                      "The given drive was not found");
+      dbus_connection_send (connection, reply, NULL);
+      dbus_message_unref (reply);
+      goto out;
+    }
+
+  g_drive_eject (drive,
+                 unmount_flags,
+                 NULL,
+                 (GAsyncReadyCallback) drive_eject_cb,
+                 dbus_message_ref (message));
+
+ out:
+  if (drives != NULL)
+    {
+      g_list_foreach (drives, (GFunc) g_object_unref, NULL);
+      g_list_free (drives);
+    }
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+drive_poll_for_media_cb (GDrive *drive, GAsyncResult *result, DBusMessage *message)
+{
+  GError *error;
+  DBusMessage *reply;
+
+  error = NULL;
+  if (!g_drive_poll_for_media_finish (drive, result, &error))
+    {
+      reply = _dbus_message_new_from_gerror (message, error);
+      g_error_free (error);
+    }
+  else
+    {
+      reply = dbus_message_new_method_return (message);
+    }
+
+  dbus_connection_send (connection, reply, NULL);
+  dbus_message_unref (message);
+  dbus_message_unref (reply);
+}
+
+static DBusHandlerResult
+handle_drive_poll_for_media (DBusConnection *connection, DBusMessage *message)
+{
+  const char *id;
+  DBusError dbus_error;
+  GList *drives, *l;
+  GDrive *drive;
+  DBusHandlerResult ret;
+
+  drive = NULL;
+  ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+  dbus_error_init (&dbus_error);
+  if (!dbus_message_get_args (message, &dbus_error,
+                              DBUS_TYPE_STRING, &id,
+                              DBUS_TYPE_INVALID))
+    {
+      g_warning ("Error parsing args for DrivePollForMedia(): %s: %s", dbus_error.name, dbus_error.message);
+      dbus_error_free (&dbus_error);
+      goto out;
+    }
+
+  ret = DBUS_HANDLER_RESULT_HANDLED;
+
+  drive = NULL;
+  drives = g_volume_monitor_get_connected_drives (monitor);
+  for (l = drives; l != NULL; l = l->next)
+    {
+      char *drive_id;
+
+      drive = G_DRIVE (l->data);
+      drive_id = g_strdup_printf ("%p", drive);
+      if (strcmp (drive_id, id) == 0)
+        break;
+
+      g_free (drive_id);
+    }
+  if (l == NULL)
+    drive = NULL;
+
+  if (drive == NULL)
+    {
+      DBusMessage *reply;
+      reply = dbus_message_new_error (message,
+                                      "org.gtk.Private.RemoteVolumeMonitor.NotFound",
+                                      "The given drive was not found");
+      dbus_connection_send (connection, reply, NULL);
+      dbus_message_unref (reply);
+      goto out;
+    }
+
+  g_drive_poll_for_media (drive,
+                          NULL,
+                          (GAsyncReadyCallback) drive_poll_for_media_cb,
+                          dbus_message_ref (message));
+
+ out:
+  if (drives != NULL)
+    {
+      g_list_foreach (drives, (GFunc) g_object_unref, NULL);
+      g_list_free (drives);
+    }
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static DBusHandlerResult
+handle_is_supported (DBusConnection *connection, DBusMessage *message)
+{
+  dbus_bool_t is_supported;
+  DBusMessage *reply;
+  DBusMessageIter iter;
+
+  /* if monitor wasn't created on startup; try again */
+  if (monitor == NULL)
+    monitor_try_create ();
+
+  is_supported = (monitor != NULL);
+
+  reply = dbus_message_new_method_return (message);
+  dbus_message_iter_init_append (reply, &iter);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &is_supported);
+  dbus_connection_send (connection, reply, NULL);
+  dbus_message_unref (reply);
+
+  return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static DBusHandlerResult
+filter_function (DBusConnection *connection, DBusMessage *message, void *user_data)
+{
+  DBusHandlerResult ret;
+
+  ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+  if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "IsSupported") &&
+      strcmp (dbus_message_get_path (message), "/") == 0)
+    {
+      ret = handle_is_supported (connection, message);
+    }
+  else
+    {
+      if (monitor != NULL)
+        {
+          if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "List") &&
+              strcmp (dbus_message_get_path (message), "/") == 0)
+            ret = handle_list (connection, message);
+
+          else if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "MountUnmount") &&
+                   strcmp (dbus_message_get_path (message), "/") == 0)
+            ret = handle_mount_unmount (connection, message);
+
+          else if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "VolumeMount") &&
+                   strcmp (dbus_message_get_path (message), "/") == 0)
+            ret = handle_volume_mount (connection, message);
+
+          else if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "DriveEject") &&
+                   strcmp (dbus_message_get_path (message), "/") == 0)
+            ret = handle_drive_eject (connection, message);
+
+          else if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "DrivePollForMedia") &&
+                   strcmp (dbus_message_get_path (message), "/") == 0)
+            ret = handle_drive_poll_for_media (connection, message);
+        }
+    }
+
+  return ret;
+}
+
+typedef void (*AppendFunc) (void *object, DBusMessageIter *iter);
+
+static void
+emit_signal (DBusConnection *connection, const char *signal_name, void *object, AppendFunc func)
+{
+  char *id;
+  DBusMessage *message;
+  DBusMessageIter iter;
+
+  id = g_strdup_printf ("%p", object);
+
+  message = dbus_message_new_signal ("/", "org.gtk.Private.RemoteVolumeMonitor", signal_name);
+  dbus_message_iter_init_append (message, &iter);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &id);
+
+  func (object, &iter);
+
+  dbus_connection_send (connection, message, NULL);
+  dbus_message_unref (message);
+
+
+  g_free (id);
+}
+
+static void
+drive_changed (GVolumeMonitor *monitor, GDrive *drive, DBusConnection *connection)
+{
+  emit_signal (connection, "DriveChanged", drive, (AppendFunc) append_drive);
+}
+
+static void
+drive_connected (GVolumeMonitor *monitor, GDrive *drive, DBusConnection *connection)
+{
+  emit_signal (connection, "DriveConnected", drive, (AppendFunc) append_drive);
+}
+
+static void
+drive_disconnected (GVolumeMonitor *monitor, GDrive *drive, DBusConnection *connection)
+{
+  emit_signal (connection, "DriveDisconnected", drive, (AppendFunc) append_drive);
+}
+
+static void
+drive_eject_button (GVolumeMonitor *monitor, GDrive *drive, DBusConnection *connection)
+{
+  g_warning ("drive eject button!");
+  emit_signal (connection, "DriveEjectButton", drive, (AppendFunc) append_drive);
+}
+
+static void
+volume_changed (GVolumeMonitor *monitor, GVolume *volume, DBusConnection *connection)
+{
+  emit_signal (connection, "VolumeChanged", volume, (AppendFunc) append_volume);
+}
+
+static void
+volume_added (GVolumeMonitor *monitor, GVolume *volume, DBusConnection *connection)
+{
+  emit_signal (connection, "VolumeAdded", volume, (AppendFunc) append_volume);
+}
+
+static void
+volume_removed (GVolumeMonitor *monitor, GVolume *volume, DBusConnection *connection)
+{
+  emit_signal (connection, "VolumeRemoved", volume, (AppendFunc) append_volume);
+}
+
+static void
+mount_changed (GVolumeMonitor *monitor, GMount *mount, DBusConnection *connection)
+{
+  emit_signal (connection, "MountChanged", mount, (AppendFunc) append_mount);
+}
+
+static void
+mount_added (GVolumeMonitor *monitor, GMount *mount, DBusConnection *connection)
+{
+  emit_signal (connection, "MountAdded", mount, (AppendFunc) append_mount);
+}
+
+static void
+mount_pre_unmount (GVolumeMonitor *monitor, GMount *mount, DBusConnection *connection)
+{
+  emit_signal (connection, "MountPreUnmount", mount, (AppendFunc) append_mount);
+}
+
+static void
+mount_removed (GVolumeMonitor *monitor, GMount *mount, DBusConnection *connection)
+{
+  emit_signal (connection, "MountRemoved", mount, (AppendFunc) append_mount);
+}
+
+void
+g_vfs_proxy_volume_monitor_daemon_init (void)
+{
+  /* avoid loading the gio proxy module which will spawn ourselves
+   *
+   * see remote-volume-monitor-module.c
+   */
+  g_setenv ("GVFS_REMOTE_VOLUME_MONITOR_IGNORE", "1", TRUE);
+
+  setlocale (LC_ALL, "");
+
+  bindtextdomain (GETTEXT_PACKAGE, GVFS_LOCALEDIR);
+  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+  textdomain (GETTEXT_PACKAGE);
+
+  dbus_threads_init_default ();
+  g_thread_init (NULL);
+  g_type_init ();
+}
+
+
+static void
+monitor_try_create (void)
+{
+  GVolumeMonitorClass *klass;
+
+  monitor = NULL;
+  klass = G_VOLUME_MONITOR_CLASS (g_type_class_ref (the_volume_monitor_type));
+  if (klass == NULL)
+    {
+      g_warning ("Can't get class for type");
+      goto fail;
+    }
+
+  if (klass->is_supported != NULL)
+    {
+      if (! (klass->is_supported ()))
+        {
+          g_warning ("monitor says it's not supported");
+          goto fail;
+        }
+    }
+
+  monitor = G_VOLUME_MONITOR (g_object_new (the_volume_monitor_type, NULL));
+  if (monitor == NULL)
+    {
+      g_warning ("Cannot instantiate volume monitor");
+      goto fail;
+    }
+
+ fail:
+  if (klass != NULL)
+    g_type_class_unref (klass);
+}
+
+int
+g_vfs_proxy_volume_monitor_daemon_main (int argc,
+                                        char *argv[],
+                                        const char *dbus_name,
+                                        GType volume_monitor_type)
+{
+  int rc;
+  int ret;
+  GMainLoop *loop;
+  DBusError dbus_error;
+
+  ret = 1;
+
+  loop = g_main_loop_new (NULL, FALSE);
+
+  /* need to start up regardless of whether we can instantiate a
+   * volume monitor; this is because the proxy will need to be able to
+   * call IsSupported() on our D-Bus interface.
+   */
+
+  the_volume_monitor_type = volume_monitor_type;
+
+  /* try and create the monitor */
+  monitor_try_create ();
+
+  dbus_error_init (&dbus_error);
+  connection = dbus_bus_get (DBUS_BUS_SESSION, &dbus_error);
+  if (dbus_error_is_set (&dbus_error))
+    {
+      g_warning ("Cannot connect to session bus: %s: %s", dbus_error.name, dbus_error.message);
+      dbus_error_free (&dbus_error);
+      goto out;
+    }
+
+  _g_dbus_connection_integrate_with_main (connection);
+
+  rc = dbus_bus_request_name (connection, dbus_name, 0, &dbus_error);
+  if (dbus_error_is_set (&dbus_error))
+    {
+      g_warning ("dbus_bus_request_name failed: %s: %s", dbus_error.name, dbus_error.message);
+      dbus_error_free (&dbus_error);
+      goto out;
+    }
+  if (ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
+    {
+      g_warning ("Cannot become primary owner");
+      goto out;
+    }
+
+  if (!dbus_connection_add_filter (connection, filter_function, NULL, NULL))
+    {
+      g_warning ("Cannot add filter function");
+      goto out;
+    }
+
+  if (monitor != NULL)
+    {
+      g_signal_connect (monitor, "drive-changed", (GCallback) drive_changed, connection);
+      g_signal_connect (monitor, "drive-connected", (GCallback) drive_connected, connection);
+      g_signal_connect (monitor, "drive-disconnected", (GCallback) drive_disconnected, connection);
+      g_signal_connect (monitor, "drive-eject-button", (GCallback) drive_eject_button, connection);
+
+      g_signal_connect (monitor, "volume-changed", (GCallback) volume_changed, connection);
+      g_signal_connect (monitor, "volume-added", (GCallback) volume_added, connection);
+      g_signal_connect (monitor, "volume-removed", (GCallback) volume_removed, connection);
+
+      g_signal_connect (monitor, "mount-changed", (GCallback) mount_changed, connection);
+      g_signal_connect (monitor, "mount-added", (GCallback) mount_added, connection);
+      g_signal_connect (monitor, "mount-pre-unmount", (GCallback) mount_pre_unmount, connection);
+      g_signal_connect (monitor, "mount-removed", (GCallback) mount_removed, connection);
+    }
+
+  g_main_loop_run (loop);
+  g_main_loop_unref (loop);
+
+  ret = 0;
+
+out:
+  return ret;
+}

Added: trunk/monitor/proxy/gvfsproxyvolumemonitordaemon.h
==============================================================================
--- (empty file)
+++ trunk/monitor/proxy/gvfsproxyvolumemonitordaemon.h	Tue Jul  8 16:36:45 2008
@@ -0,0 +1,35 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2006-2008 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_VFS_PROXY_VOLUME_MONITOR_DAEMON_H__
+#define __G_VFS_PROXY_VOLUME_MONITOR_DAEMON_H__
+
+#include <gio/gio.h>
+
+void g_vfs_proxy_volume_monitor_daemon_init (void);
+int  g_vfs_proxy_volume_monitor_daemon_main (int         argc,
+                                             char       *argv[],
+                                             const char *dbus_name,
+                                             GType       volume_monitor_type);
+
+#endif

Added: trunk/monitor/proxy/remote-volume-monitor-module.c
==============================================================================
--- (empty file)
+++ trunk/monitor/proxy/remote-volume-monitor-module.c	Tue Jul  8 16:36:45 2008
@@ -0,0 +1,66 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* gvfs - extensions for gio
+ *
+ * Copyright (C) 2006-2008 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 <glib.h>
+#include <glib/gi18n-lib.h>
+#include <gmodule.h>
+#include <gio/gio.h>
+
+#include "gproxyvolumemonitor.h"
+#include "gproxyvolume.h"
+#include "gproxymount.h"
+#include "gproxydrive.h"
+
+void
+g_io_module_load (GIOModule *module)
+{
+  /* see gvfsproxyvolumemonitor.c:g_vfs_proxy_volume_monitor_daemon_init() */
+  if (g_getenv ("GVFS_REMOTE_VOLUME_MONITOR_IGNORE") != NULL)
+    goto out;
+
+  bindtextdomain (GETTEXT_PACKAGE, GVFS_LOCALEDIR);
+  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+
+  g_proxy_volume_monitor_setup_session_bus_connection ();
+
+  g_proxy_drive_register (module);
+  g_proxy_mount_register (module);
+  g_proxy_volume_register (module);
+  g_proxy_volume_monitor_register (module);
+out:
+  ;
+ }
+
+void
+g_io_module_unload (GIOModule *module)
+{
+  if (g_getenv ("GVFS_REMOTE_VOLUME_MONITOR_IGNORE") != NULL)
+    goto out;
+
+  g_proxy_volume_monitor_teardown_session_bus_connection ();
+
+out:
+  ;
+}

Modified: trunk/programs/gvfs-mount.c
==============================================================================
--- trunk/programs/gvfs-mount.c	(original)
+++ trunk/programs/gvfs-mount.c	Tue Jul  8 16:36:45 2008
@@ -362,6 +362,7 @@
   GDrive *drive;
   char *name;
   char *uuid;
+  GFile *activation_root;
   char **ids;
   GIcon *icon;
   
@@ -403,6 +404,15 @@
 	  uuid = g_volume_get_uuid (volume);
 	  if (uuid)
 	    g_print ("%*suuid=%s\n", indent + 2, "", uuid);
+	  activation_root = g_volume_get_activation_root (volume);
+	  if (activation_root)
+            {
+              char *uri;
+              uri = g_file_get_uri (activation_root);
+              g_print ("%*sactivation_root=%s\n", indent + 2, "", uri);
+              g_free (uri);
+              g_object_unref (activation_root);
+            }
       icon = g_volume_get_icon (volume);
       if (icon)
         {
@@ -437,6 +447,7 @@
   GDrive *drive;
   char *name;
   char **ids;
+  GIcon *icon;
   
   for (c = 0, l = drives; l != NULL; l = l->next, c++)
     {
@@ -462,6 +473,14 @@
 	    }
 	  g_strfreev (ids);
 
+          icon = g_drive_get_icon (drive);
+          if (icon)
+          {
+                  if (G_IS_THEMED_ICON (icon))
+                          show_themed_icon_names (G_THEMED_ICON (icon), indent + 2);
+                  g_object_unref (icon);
+          }
+
 	  g_print ("%*sis_media_removable=%d\n", indent + 2, "", g_drive_is_media_removable (drive));
 	  g_print ("%*shas_media=%d\n", indent + 2, "", g_drive_has_media (drive));
 	  g_print ("%*sis_media_check_automatic=%d\n", indent + 2, "", g_drive_is_media_check_automatic (drive));
@@ -502,6 +521,8 @@
   list_mounts (mounts, 0, TRUE);
   g_list_foreach (mounts, (GFunc)g_object_unref, NULL);
   g_list_free (mounts);
+
+  g_object_unref (volume_monitor);
 }
 
 static void
@@ -530,6 +551,7 @@
   g_list_foreach (mounts, (GFunc)g_object_unref, NULL);
   g_list_free (mounts);
 
+  g_object_unref (volume_monitor);
 }
 
 int



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