gvfs r2253 - in trunk: . monitor/proxy



Author: davidz
Date: Thu Feb 26 18:17:52 2009
New Revision: 2253
URL: http://svn.gnome.org/viewvc/gvfs?rev=2253&view=rev

Log:
2009-02-26  David Zeuthen  <davidz redhat com>

	Lots of proxy monitor fixes.

	Reviewed by: Alexander Larsson  <alexl redhat com>

	o  add support for proxying GMountOperation to the remote volume
	   monitor process.

	o  add support for proxying GCancellable to the remote volume
	   monitor process.

	o  make each volume monitor process track callers and cancels
	   operations initiated by callers that disconnects from the bus

	o  makes the client side proxy monitor track the remote monitor. If
	   the monitor process disconnects, then all drives/volumes/mounts
	   are removed. If the monitor process reconnects, we reseed the
	   internal monitor and add drives/volumes/mounts.

	o  Each monitor process now uses ALLOW_REPLACEMENT when claiming a
	   name and also kills itself on NameLost (e.g. if it is
	   replaced). Coupled with the above disconnection/reconnection logic
	   it this makes it a lot more tolerable to hack on a remote volume
	   monitor. Simply just compile it, start it and the
	   existing (system-wide) copy will kill itself. And all the
	   clients (Nautilus, panel, drive applet, gvfsd-computer, etc.) will
	   reconnect and do the right thing.

	o  make the get_mount_for_mount_path() method on the class
	   GNativeVolumeMonitor actually work. It turns out that at least
	   gvfs-mount -u needs that.

	o  use /org/gtk/Private/RemoteVolumeMonitor instead of / as the
	   object name. Cf. the version D-Bus debacle on Lennart Poettering's
	   blog.

	o  make the proxy monitor client module resident

	o For shadow mounts, redirect can_eject() and eject() to the
	  volume for the shadow mount. Without this patch eject on
	  e.g. cdda:// volumes won't work since cdda:// volumes are
	  GDaedmonMount and these don't implement eject.

	* monitor/proxy/*.[ch]: See above.



Modified:
   trunk/ChangeLog
   trunk/monitor/proxy/gproxydrive.c
   trunk/monitor/proxy/gproxymount.c
   trunk/monitor/proxy/gproxyshadowmount.c
   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/remote-volume-monitor-module.c

Modified: trunk/monitor/proxy/gproxydrive.c
==============================================================================
--- trunk/monitor/proxy/gproxydrive.c	(original)
+++ trunk/monitor/proxy/gproxydrive.c	Thu Feb 26 18:17:52 2009
@@ -128,10 +128,11 @@
  * boolean              can-poll-for-media
  * boolean              has-media
  * boolean              is-media-removable
+ * boolean              is-media-check-automatic
  * array:string         volume-ids
  * dict:string->string  identifiers
  */
-#define DRIVE_STRUCT_TYPE "(sssbbbasa{ss})"
+#define DRIVE_STRUCT_TYPE "(sssbbbbasa{ss})"
 
 void
 g_proxy_drive_update (GProxyDrive         *drive,
@@ -146,6 +147,7 @@
   dbus_bool_t can_poll_for_media;
   dbus_bool_t has_media;
   dbus_bool_t is_media_removable;
+  dbus_bool_t is_media_check_automatic;
   GPtrArray *volume_ids;
   GHashTable *identifiers;
 
@@ -164,6 +166,8 @@
   dbus_message_iter_next (&iter_struct);
   dbus_message_iter_get_basic (&iter_struct, &is_media_removable);
   dbus_message_iter_next (&iter_struct);
+  dbus_message_iter_get_basic (&iter_struct, &is_media_check_automatic);
+  dbus_message_iter_next (&iter_struct);
 
   volume_ids = g_ptr_array_new ();
   dbus_message_iter_recurse (&iter_struct, &iter_volume_ids_iter);
@@ -210,6 +214,7 @@
   drive->can_poll_for_media = can_poll_for_media;
   drive->has_media = has_media;
   drive->is_media_removable = is_media_removable;
+  drive->is_media_check_automatic = is_media_check_automatic;
   drive->identifiers = identifiers != NULL ? g_hash_table_ref (identifiers) : NULL;
   drive->volume_ids = g_strdupv ((char **) volume_ids->pdata);
 
@@ -397,32 +402,100 @@
 }
 
 typedef struct {
-  GObject *object;
+  GProxyDrive *drive;
   GAsyncReadyCallback callback;
   gpointer user_data;
+
+  gchar *cancellation_id;
   GCancellable *cancellable;
+  gulong cancelled_handler_id;
 } DBusOp;
 
 static void
+cancel_operation_reply_cb (DBusMessage *reply,
+                           GError      *error,
+                           gpointer     user_data)
+{
+  if (error != NULL)
+    {
+      g_warning ("Error from CancelOperation(): %s", error->message);
+    }
+}
+
+static void
+operation_cancelled (GCancellable *cancellable,
+                     gpointer      user_data)
+{
+  DBusOp *data = user_data;
+  GSimpleAsyncResult *simple;
+  DBusConnection *connection;
+  DBusMessage *message;
+  const char *name;
+
+  G_LOCK (proxy_drive);
+
+  simple = g_simple_async_result_new_error (G_OBJECT (data->drive),
+                                            data->callback,
+                                            data->user_data,
+                                            G_IO_ERROR,
+                                            G_IO_ERROR_CANCELLED,
+                                            _("Operation was cancelled"));
+  g_simple_async_result_complete_in_idle (simple);
+  g_object_unref (simple);
+
+  /* Now tell the remote volume monitor that the op has been cancelled */
+  connection = g_proxy_volume_monitor_get_dbus_connection (data->drive->volume_monitor);
+  name = g_proxy_volume_monitor_get_dbus_name (data->drive->volume_monitor);
+  message = dbus_message_new_method_call (name,
+                                          "/org/gtk/Private/RemoteVolumeMonitor",
+                                          "org.gtk.Private.RemoteVolumeMonitor",
+                                          "CancelOperation");
+  dbus_message_append_args (message,
+                            DBUS_TYPE_STRING,
+                            &(data->cancellation_id),
+                            DBUS_TYPE_INVALID);
+
+  G_UNLOCK (proxy_drive);
+
+  _g_dbus_connection_call_async (connection,
+                                 message,
+                                 -1,
+                                 (GAsyncDBusCallback) cancel_operation_reply_cb,
+                                 NULL);
+  dbus_message_unref (message);
+  dbus_connection_unref (connection);
+}
+
+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);
+  if (data->cancelled_handler_id > 0)
+    g_signal_handler_disconnect (data->cancellable, data->cancelled_handler_id);
+
+  if (!g_cancellable_is_cancelled (data->cancellable))
+    {
+      GSimpleAsyncResult *simple;
+
+      if (error != NULL)
+        simple = g_simple_async_result_new_from_error (G_OBJECT (data->drive),
+                                                       data->callback,
+                                                       data->user_data,
+                                                       error);
+      else
+        simple = g_simple_async_result_new (G_OBJECT (data->drive),
+                                            data->callback,
+                                            data->user_data,
+                                            NULL);
+      g_simple_async_result_complete (simple);
+      g_object_unref (simple);
+    }
 
-  g_object_unref (data->object);
+  g_object_unref (data->drive);
+  g_free (data->cancellation_id);
+  if (data->cancellable != NULL)
+    g_object_unref (data->cancellable);
   g_free (data);
 }
 
@@ -442,22 +515,52 @@
 
   G_LOCK (proxy_drive);
 
+  if (g_cancellable_is_cancelled (cancellable))
+    {
+      GSimpleAsyncResult *simple;
+      simple = g_simple_async_result_new_error (G_OBJECT (drive),
+                                                callback,
+                                                user_data,
+                                                G_IO_ERROR,
+                                                G_IO_ERROR_CANCELLED,
+                                                _("Operation was cancelled"));
+      g_simple_async_result_complete_in_idle (simple);
+      g_object_unref (simple);
+      G_UNLOCK (proxy_drive);
+      goto out;
+    }
+
   data = g_new0 (DBusOp, 1);
-  data->object = g_object_ref (drive);
+  data->drive = g_object_ref (drive);
   data->callback = callback;
   data->user_data = user_data;
-  data->cancellable = cancellable;
+
+  if (cancellable != NULL)
+    {
+      data->cancellation_id = g_strdup_printf ("%p", cancellable);
+      data->cancellable = g_object_ref (cancellable);
+      data->cancelled_handler_id = g_signal_connect (data->cancellable,
+                                                     "cancelled",
+                                                     G_CALLBACK (operation_cancelled),
+                                                     data);
+    }
+  else
+    {
+      data->cancellation_id = g_strdup ("");
+    }
 
   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",
                                           "org.gtk.Private.RemoteVolumeMonitor",
                                           "DriveEject");
   dbus_message_append_args (message,
                             DBUS_TYPE_STRING,
                             &(proxy_drive->id),
+                            DBUS_TYPE_STRING,
+                            &(data->cancellation_id),
                             DBUS_TYPE_UINT32,
                             &_flags,
                             DBUS_TYPE_INVALID);
@@ -470,6 +573,8 @@
                                  data);
   dbus_connection_unref (connection);
   dbus_message_unref (message);
+ out:
+  ;
 }
 
 static gboolean
@@ -487,21 +592,30 @@
                    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);
+  if (!g_cancellable_is_cancelled (data->cancellable))
+    {
+      GSimpleAsyncResult *simple;
 
-  g_object_unref (data->object);
+      if (error != NULL)
+        simple = g_simple_async_result_new_from_error (G_OBJECT (data->drive),
+                                                       data->callback,
+                                                       data->user_data,
+                                                       error);
+      else
+        simple = g_simple_async_result_new (G_OBJECT (data->drive),
+                                            data->callback,
+                                            data->user_data,
+                                            NULL);
+      g_simple_async_result_complete (simple);
+      g_object_unref (simple);
+    }
+
+  g_object_unref (data->drive);
+  g_free (data->cancellation_id);
+  if (data->cancelled_handler_id > 0)
+    g_signal_handler_disconnect (data->cancellable, data->cancelled_handler_id);
+  if (data->cancellable != NULL)
+    g_object_unref (data->cancellable);
   g_free (data);
 }
 
@@ -519,22 +633,52 @@
 
   G_LOCK (proxy_drive);
 
+  if (g_cancellable_is_cancelled (cancellable))
+    {
+      GSimpleAsyncResult *simple;
+      simple = g_simple_async_result_new_error (G_OBJECT (drive),
+                                                callback,
+                                                user_data,
+                                                G_IO_ERROR,
+                                                G_IO_ERROR_CANCELLED,
+                                                _("Operation was cancelled"));
+      g_simple_async_result_complete_in_idle (simple);
+      g_object_unref (simple);
+      G_UNLOCK (proxy_drive);
+      goto out;
+    }
+
   data = g_new0 (DBusOp, 1);
-  data->object = g_object_ref (drive);
+  data->drive = g_object_ref (drive);
   data->callback = callback;
   data->user_data = user_data;
-  data->cancellable = cancellable;
+
+  if (cancellable != NULL)
+    {
+      data->cancellation_id = g_strdup_printf ("%p", cancellable);
+      data->cancellable = g_object_ref (cancellable);
+      data->cancelled_handler_id = g_signal_connect (data->cancellable,
+                                                     "cancelled",
+                                                     G_CALLBACK (operation_cancelled),
+                                                     data);
+    }
+  else
+    {
+      data->cancellation_id = g_strdup ("");
+    }
 
   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",
                                           "org.gtk.Private.RemoteVolumeMonitor",
                                           "DrivePollForMedia");
   dbus_message_append_args (message,
                             DBUS_TYPE_STRING,
                             &(proxy_drive->id),
+                            DBUS_TYPE_STRING,
+                            &(data->cancellation_id),
                             DBUS_TYPE_INVALID);
   G_UNLOCK (proxy_drive);
 
@@ -545,6 +689,8 @@
                                  data);
   dbus_connection_unref (connection);
   dbus_message_unref (message);
+ out:
+  ;
 }
 
 static gboolean

Modified: trunk/monitor/proxy/gproxymount.c
==============================================================================
--- trunk/monitor/proxy/gproxymount.c	(original)
+++ trunk/monitor/proxy/gproxymount.c	Thu Feb 26 18:17:52 2009
@@ -415,32 +415,99 @@
 }
 
 typedef struct {
-  GObject *object;
+  GProxyMount *mount;
   GAsyncReadyCallback callback;
   gpointer user_data;
+
+  gchar *cancellation_id;
   GCancellable *cancellable;
+  gulong cancelled_handler_id;
 } DBusOp;
 
 static void
+cancel_operation_reply_cb (DBusMessage *reply,
+                           GError      *error,
+                           gpointer     user_data)
+{
+  if (error != NULL)
+    {
+      g_warning ("Error from CancelOperation(): %s", error->message);
+    }
+}
+
+static void
+operation_cancelled (GCancellable *cancellable,
+                     gpointer      user_data)
+{
+  DBusOp *data = user_data;
+  GSimpleAsyncResult *simple;
+  DBusConnection *connection;
+  DBusMessage *message;
+  const char *name;
+
+  G_LOCK (proxy_mount);
+
+  simple = g_simple_async_result_new_error (G_OBJECT (data->mount),
+                                            data->callback,
+                                            data->user_data,
+                                            G_IO_ERROR,
+                                            G_IO_ERROR_CANCELLED,
+                                            _("Operation was cancelled"));
+  g_simple_async_result_complete_in_idle (simple);
+  g_object_unref (simple);
+
+  /* Now tell the remote volume monitor that the op has been cancelled */
+  connection = g_proxy_volume_monitor_get_dbus_connection (data->mount->volume_monitor);
+  name = g_proxy_volume_monitor_get_dbus_name (data->mount->volume_monitor);
+  message = dbus_message_new_method_call (name,
+                                          "/org/gtk/Private/RemoteVolumeMonitor",
+                                          "org.gtk.Private.RemoteVolumeMonitor",
+                                          "CancelOperation");
+  dbus_message_append_args (message,
+                            DBUS_TYPE_STRING,
+                            &(data->cancellation_id),
+                            DBUS_TYPE_INVALID);
+
+  G_UNLOCK (proxy_mount);
+
+  _g_dbus_connection_call_async (connection,
+                                 message,
+                                 -1,
+                                 (GAsyncDBusCallback) cancel_operation_reply_cb,
+                                 NULL);
+  dbus_message_unref (message);
+  dbus_connection_unref (connection);
+}
+
+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);
+  if (data->cancelled_handler_id > 0)
+    g_signal_handler_disconnect (data->cancellable, data->cancelled_handler_id);
 
-  g_object_unref (data->object);
+  if (!g_cancellable_is_cancelled (data->cancellable))
+    {
+      GSimpleAsyncResult *simple;
+      if (error != NULL)
+        simple = g_simple_async_result_new_from_error (G_OBJECT (data->mount),
+                                                       data->callback,
+                                                       data->user_data,
+                                                       error);
+      else
+        simple = g_simple_async_result_new (G_OBJECT (data->mount),
+                                            data->callback,
+                                            data->user_data,
+                                            NULL);
+      g_simple_async_result_complete (simple);
+      g_object_unref (simple);
+    }
+
+  g_object_unref (data->mount);
+  g_free (data->cancellation_id);
+  if (data->cancellable != NULL)
+    g_object_unref (data->cancellable);
   g_free (data);
 }
 
@@ -460,22 +527,52 @@
 
   G_LOCK (proxy_mount);
 
+  if (g_cancellable_is_cancelled (cancellable))
+    {
+      GSimpleAsyncResult *simple;
+      simple = g_simple_async_result_new_error (G_OBJECT (mount),
+                                                callback,
+                                                user_data,
+                                                G_IO_ERROR,
+                                                G_IO_ERROR_CANCELLED,
+                                                _("Operation was cancelled"));
+      g_simple_async_result_complete_in_idle (simple);
+      g_object_unref (simple);
+      G_UNLOCK (proxy_mount);
+      goto out;
+    }
+
   data = g_new0 (DBusOp, 1);
-  data->object = g_object_ref (mount);
+  data->mount = g_object_ref (mount);
   data->callback = callback;
   data->user_data = user_data;
-  data->cancellable = cancellable;
+
+  if (cancellable != NULL)
+    {
+      data->cancellation_id = g_strdup_printf ("%p", cancellable);
+      data->cancellable = g_object_ref (cancellable);
+      data->cancelled_handler_id = g_signal_connect (data->cancellable,
+                                                     "cancelled",
+                                                     G_CALLBACK (operation_cancelled),
+                                                     data);
+    }
+  else
+    {
+      data->cancellation_id = g_strdup ("");
+    }
 
   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",
                                           "org.gtk.Private.RemoteVolumeMonitor",
                                           "MountUnmount");
   dbus_message_append_args (message,
                             DBUS_TYPE_STRING,
                             &(proxy_mount->id),
+                            DBUS_TYPE_STRING,
+                            &(data->cancellation_id),
                             DBUS_TYPE_UINT32,
                             &_flags,
                             DBUS_TYPE_INVALID);
@@ -489,6 +586,8 @@
 
   dbus_message_unref (message);
   dbus_connection_unref (connection);
+ out:
+  ;
 }
 
 static gboolean

Modified: trunk/monitor/proxy/gproxyshadowmount.c
==============================================================================
--- trunk/monitor/proxy/gproxyshadowmount.c	(original)
+++ trunk/monitor/proxy/gproxyshadowmount.c	Thu Feb 26 18:17:52 2009
@@ -263,12 +263,30 @@
   gboolean res;
 
   G_LOCK (proxy_shadow_mount);
-  res = g_mount_can_eject (G_MOUNT (proxy_shadow_mount->real_mount));
+  res = g_volume_can_eject (G_VOLUME (proxy_shadow_mount->volume));
   G_UNLOCK (proxy_shadow_mount);
 
   return res;
 }
 
+
+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_object_unref (data->object);
+  g_free (data);
+}
+
 static void
 g_proxy_shadow_mount_eject (GMount              *mount,
                             GMountUnmountFlags   flags,
@@ -277,12 +295,15 @@
                             gpointer             user_data)
 {
   GProxyShadowMount *proxy_shadow_mount = G_PROXY_SHADOW_MOUNT (mount);
+  EjectWrapperOp *data;
 
-  g_mount_eject (proxy_shadow_mount->real_mount,
-                 flags,
-                 cancellable,
-                 callback,
-                 user_data);
+  G_LOCK (proxy_shadow_mount);
+  data = g_new0 (EjectWrapperOp, 1);
+  data->object = g_object_ref (mount);
+  data->callback = callback;
+  data->user_data = user_data;
+  g_volume_eject (G_VOLUME (proxy_shadow_mount->volume), flags, cancellable, eject_wrapper_callback, data);
+  G_UNLOCK (proxy_shadow_mount);
 }
 
 static gboolean
@@ -291,10 +312,13 @@
                                    GError       **error)
 {
   GProxyShadowMount *proxy_shadow_mount = G_PROXY_SHADOW_MOUNT (mount);
+  gboolean res;
 
-  return g_mount_eject_finish (proxy_shadow_mount->real_mount,
-                               result,
-                               error);
+  G_LOCK (proxy_shadow_mount);
+  res = g_volume_eject_finish (G_VOLUME (proxy_shadow_mount->volume), result, error);
+  G_UNLOCK (proxy_shadow_mount);
+
+  return res;
 }
 
 static void

Modified: trunk/monitor/proxy/gproxyvolume.c
==============================================================================
--- trunk/monitor/proxy/gproxyvolume.c	(original)
+++ trunk/monitor/proxy/gproxyvolume.c	Thu Feb 26 18:17:52 2009
@@ -63,6 +63,8 @@
   gboolean should_automount;
 
   GProxyShadowMount *shadow_mount;
+
+  GHashTable *hash_mount_op_id_to_data;
 };
 
 static void g_proxy_volume_volume_iface_init (GVolumeIface *iface);
@@ -141,6 +143,8 @@
       g_object_unref (volume->volume_monitor);
     }
 
+  g_hash_table_unref (volume->hash_mount_op_id_to_data);
+
   if (G_OBJECT_CLASS (g_proxy_volume_parent_class)->finalize)
     (*G_OBJECT_CLASS (g_proxy_volume_parent_class)->finalize) (object);
 }
@@ -161,6 +165,7 @@
 static void
 g_proxy_volume_init (GProxyVolume *proxy_volume)
 {
+  proxy_volume->hash_mount_op_id_to_data = g_hash_table_new (g_str_hash, g_str_equal);
 }
 
 GProxyVolume *
@@ -672,32 +677,60 @@
 }
 
 typedef struct {
-  GObject *object;
+  GProxyVolume *volume;
   GAsyncReadyCallback callback;
   gpointer user_data;
+
+  gchar *cancellation_id;
   GCancellable *cancellable;
+  gulong cancelled_handler_id;
+
+  gchar *mount_op_id;
+  GMountOperation *mount_operation;
+  gulong reply_handler_id;
 } DBusOp;
 
 static void
 mount_cb (DBusMessage *reply,
-          GError *error,
-          DBusOp *data)
+          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_in_idle (simple);
-  g_object_unref (simple);
+  if (data->cancelled_handler_id > 0)
+    g_signal_handler_disconnect (data->cancellable, data->cancelled_handler_id);
+
+  if (!g_cancellable_is_cancelled (data->cancellable))
+    {
+      GSimpleAsyncResult *simple;
+
+      if (error != NULL)
+        simple = g_simple_async_result_new_from_error (G_OBJECT (data->volume),
+                                                       data->callback,
+                                                       data->user_data,
+                                                       error);
+      else
+        simple = g_simple_async_result_new (G_OBJECT (data->volume),
+                                            data->callback,
+                                            data->user_data,
+                                            NULL);
+      g_simple_async_result_complete_in_idle (simple);
+      g_object_unref (simple);
+    }
+
+  /* free DBusOp */
+  if (strlen (data->mount_op_id) > 0)
+    g_hash_table_remove (data->volume->hash_mount_op_id_to_data, data->mount_op_id);
+  g_object_unref (data->volume);
+
+  g_free (data->mount_op_id);
+  if (data->reply_handler_id > 0)
+    g_signal_handler_disconnect (data->mount_operation, data->reply_handler_id);
+  if (data->mount_operation != NULL)
+    g_object_unref (data->mount_operation);
+
+  g_free (data->cancellation_id);
+  if (data->cancellable != NULL)
+    g_object_unref (data->cancellable);
 
-  g_object_unref (data->object);
   g_free (data);
 }
 
@@ -720,6 +753,61 @@
 }
 
 static void
+cancel_operation_reply_cb (DBusMessage *reply,
+                           GError      *error,
+                           gpointer     user_data)
+{
+  if (error != NULL)
+    {
+      g_warning ("Error from CancelOperation(): %s", error->message);
+    }
+}
+
+static void
+mount_cancelled (GCancellable *cancellable,
+                 gpointer      user_data)
+{
+  DBusOp *data = user_data;
+  GSimpleAsyncResult *simple;
+  DBusConnection *connection;
+  DBusMessage *message;
+  const char *name;
+
+  G_LOCK (proxy_volume);
+
+  simple = g_simple_async_result_new_error (G_OBJECT (data->volume),
+                                            data->callback,
+                                            data->user_data,
+                                            G_IO_ERROR,
+                                            G_IO_ERROR_CANCELLED,
+                                            _("Operation was cancelled"));
+  g_simple_async_result_complete_in_idle (simple);
+  g_object_unref (simple);
+
+  /* Now tell the remote volume monitor that the op has been cancelled */
+  connection = g_proxy_volume_monitor_get_dbus_connection (data->volume->volume_monitor);
+  name = g_proxy_volume_monitor_get_dbus_name (data->volume->volume_monitor);
+  message = dbus_message_new_method_call (name,
+                                          "/org/gtk/Private/RemoteVolumeMonitor",
+                                          "org.gtk.Private.RemoteVolumeMonitor",
+                                          "CancelOperation");
+  dbus_message_append_args (message,
+                            DBUS_TYPE_STRING,
+                            &(data->cancellation_id),
+                            DBUS_TYPE_INVALID);
+
+  G_UNLOCK (proxy_volume);
+
+  _g_dbus_connection_call_async (connection,
+                                 message,
+                                 -1,
+                                 (GAsyncDBusCallback) cancel_operation_reply_cb,
+                                 NULL);
+  dbus_message_unref (message);
+  dbus_connection_unref (connection);
+}
+
+static void
 g_proxy_volume_mount (GVolume             *volume,
                       GMountMountFlags     flags,
                       GMountOperation     *mount_operation,
@@ -760,41 +848,302 @@
       const char *name;
       DBusMessage *message;
       dbus_uint32_t _flags = flags;
-      dbus_bool_t use_mount_operation = mount_operation != NULL;
 
-      /* TODO: support mount_operation */
+      if (g_cancellable_is_cancelled (cancellable))
+        {
+          GSimpleAsyncResult *simple;
+          simple = g_simple_async_result_new_error (G_OBJECT (volume),
+                                                    callback,
+                                                    user_data,
+                                                    G_IO_ERROR,
+                                                    G_IO_ERROR_CANCELLED,
+                                                    _("Operation was cancelled"));
+          g_simple_async_result_complete_in_idle (simple);
+          g_object_unref (simple);
+          G_UNLOCK (proxy_volume);
+          goto out;
+        }
 
       data = g_new0 (DBusOp, 1);
-      data->object = g_object_ref (volume);
+      data->volume = g_object_ref (volume);
       data->callback = callback;
       data->user_data = user_data;
-      data->cancellable = cancellable;
+      if (cancellable != NULL)
+        {
+          data->cancellation_id = g_strdup_printf ("%p", cancellable);
+          data->cancellable = g_object_ref (cancellable);
+          data->cancelled_handler_id = g_signal_connect (data->cancellable,
+                                                         "cancelled",
+                                                         G_CALLBACK (mount_cancelled),
+                                                         data);
+        }
+      else
+        {
+          data->cancellation_id = g_strdup ("");
+        }
+
+      if (mount_operation != NULL)
+        {
+          data->mount_op_id = g_strdup_printf ("%p", mount_operation);
+          data->mount_operation = g_object_ref (mount_operation);
+          g_hash_table_insert (proxy_volume->hash_mount_op_id_to_data,
+                               data->mount_op_id,
+                               data);
+        }
+      else
+        {
+          data->mount_op_id = g_strdup ("");
+        }
 
       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",
                                               "org.gtk.Private.RemoteVolumeMonitor",
                                               "VolumeMount");
       dbus_message_append_args (message,
                                 DBUS_TYPE_STRING,
                                 &(proxy_volume->id),
+                                DBUS_TYPE_STRING,
+                                &(data->cancellation_id),
                                 DBUS_TYPE_UINT32,
                                 &_flags,
-                                DBUS_TYPE_BOOLEAN,
-                                &use_mount_operation,
+                                DBUS_TYPE_STRING,
+                                &(data->mount_op_id),
                                 DBUS_TYPE_INVALID);
       G_UNLOCK (proxy_volume);
 
       _g_dbus_connection_call_async (connection,
                                      message,
-                                     -1,
+                                     30 * 60 * 1000,                /* 30 minute timeout */
                                      (GAsyncDBusCallback) mount_cb,
                                      data);
       dbus_message_unref (message);
       dbus_connection_unref (connection);
     }
+
+ out:
+  ;
+}
+
+
+static void
+mount_op_reply_cb (DBusMessage *reply,
+                   GError      *error,
+                   DBusOp      *data)
+{
+  if (error != NULL)
+    {
+      g_warning ("Error from MountOpReply(): %s", error->message);
+    }
+}
+
+static void
+mount_operation_reply (GMountOperation        *mount_operation,
+                       GMountOperationResult  result,
+                       gpointer               user_data)
+{
+  DBusOp *data = user_data;
+  DBusConnection *connection;
+  const char *name;
+  DBusMessage *message;
+  const char *user_name;
+  const char *domain;
+  const char *password;
+  char *encoded_password;
+  dbus_uint32_t password_save;
+  dbus_uint32_t choice;
+  dbus_bool_t anonymous;
+
+  connection = g_proxy_volume_monitor_get_dbus_connection (data->volume->volume_monitor);
+  name = g_proxy_volume_monitor_get_dbus_name (data->volume->volume_monitor);
+
+  user_name     = g_mount_operation_get_username (mount_operation);
+  domain        = g_mount_operation_get_domain (mount_operation);
+  password      = g_mount_operation_get_password (mount_operation);
+  password_save = g_mount_operation_get_password_save (mount_operation);
+  choice        = g_mount_operation_get_choice (mount_operation);
+  anonymous     = g_mount_operation_get_anonymous (mount_operation);
+
+  if (user_name == NULL)
+    user_name = "";
+  if (domain == NULL)
+    domain = "";
+  if (password == NULL)
+    password = "";
+
+  /* NOTE: this is not to add "security", it's merely to prevent accidental exposure
+   *       of passwords when running dbus-monitor
+   */
+  encoded_password = g_base64_encode ((const guchar *) password, (gsize) (strlen (password) + 1));
+
+  message = dbus_message_new_method_call (name,
+                                          "/org/gtk/Private/RemoteVolumeMonitor",
+                                          "org.gtk.Private.RemoteVolumeMonitor",
+                                          "MountOpReply");
+  dbus_message_append_args (message,
+                            DBUS_TYPE_STRING,
+                            &(data->volume->id),
+                            DBUS_TYPE_STRING,
+                            &(data->mount_op_id),
+                            DBUS_TYPE_INT32,
+                            &result,
+                            DBUS_TYPE_STRING,
+                            &user_name,
+                            DBUS_TYPE_STRING,
+                            &domain,
+                            DBUS_TYPE_STRING,
+                            &encoded_password,
+                            DBUS_TYPE_INT32,
+                            &password_save,
+                            DBUS_TYPE_INT32,
+                            &choice,
+                            DBUS_TYPE_BOOLEAN,
+                            &anonymous,
+                            DBUS_TYPE_INVALID);
+
+  _g_dbus_connection_call_async (connection,
+                                 message,
+                                 -1,
+                                 (GAsyncDBusCallback) mount_op_reply_cb,
+                                 data);
+
+  g_free (encoded_password);
+  dbus_message_unref (message);
+  dbus_connection_unref (connection);
+}
+
+void
+g_proxy_volume_handle_mount_op_ask_password (GProxyVolume        *volume,
+                                             DBusMessageIter     *iter)
+{
+  const char *mount_op_id;
+  const char *message;
+  const char *default_user;
+  const char *default_domain;
+  dbus_int32_t flags;
+  DBusOp *data;
+
+  dbus_message_iter_get_basic (iter, &mount_op_id);
+  dbus_message_iter_next (iter);
+
+  dbus_message_iter_get_basic (iter, &message);
+  dbus_message_iter_next (iter);
+
+  dbus_message_iter_get_basic (iter, &default_user);
+  dbus_message_iter_next (iter);
+
+  dbus_message_iter_get_basic (iter, &default_domain);
+  dbus_message_iter_next (iter);
+
+  dbus_message_iter_get_basic (iter, &flags);
+  dbus_message_iter_next (iter);
+
+  data = g_hash_table_lookup (volume->hash_mount_op_id_to_data, mount_op_id);
+
+  /* since eavesdropping is enabled on the session bus we get this signal even if it
+   * is for another application; so silently ignore it if it's not for us
+   */
+  if (data == NULL)
+    goto out;
+
+  if (data->reply_handler_id == 0)
+    {
+      data->reply_handler_id = g_signal_connect (data->mount_operation,
+                                                 "reply",
+                                                 G_CALLBACK (mount_operation_reply),
+                                                 data);
+    }
+
+  g_signal_emit_by_name (data->mount_operation,
+                         "ask-password",
+                         message,
+                         default_user,
+                         default_domain,
+                         flags);
+
+ out:
+  ;
+}
+
+void
+g_proxy_volume_handle_mount_op_ask_question (GProxyVolume        *volume,
+                                             DBusMessageIter     *iter)
+{
+  const char *mount_op_id;
+  const char *message;
+  GPtrArray *choices;
+  DBusMessageIter iter_array;
+  DBusOp *data;
+
+  choices = NULL;
+
+  dbus_message_iter_get_basic (iter, &mount_op_id);
+  dbus_message_iter_next (iter);
+
+  dbus_message_iter_get_basic (iter, &message);
+  dbus_message_iter_next (iter);
+
+  choices = g_ptr_array_new ();
+  dbus_message_iter_recurse (iter, &iter_array);
+  while (dbus_message_iter_get_arg_type (&iter_array) != DBUS_TYPE_INVALID)
+    {
+      const char *choice;
+      dbus_message_iter_get_basic (&iter_array, &choice);
+      dbus_message_iter_next (&iter_array);
+
+      g_ptr_array_add (choices, g_strdup (choice));
+    }
+  g_ptr_array_add (choices, NULL);
+
+  data = g_hash_table_lookup (volume->hash_mount_op_id_to_data, mount_op_id);
+
+  /* since eavesdropping is enabled on the session bus we get this signal even if it
+   * is for another application; so silently ignore it if it's not for us
+   */
+  if (data == NULL)
+    goto out;
+
+  if (data->reply_handler_id == 0)
+    {
+      data->reply_handler_id = g_signal_connect (data->mount_operation,
+                                                 "reply",
+                                                 G_CALLBACK (mount_operation_reply),
+                                                 data);
+    }
+
+  g_signal_emit_by_name (data->mount_operation,
+                         "ask-question",
+                         message,
+                         choices->pdata);
+
+ out:
+  g_ptr_array_free (choices, TRUE);
+}
+
+void
+g_proxy_volume_handle_mount_op_aborted (GProxyVolume        *volume,
+                                        DBusMessageIter     *iter)
+{
+  const char *mount_op_id;
+  DBusOp *data;
+
+  dbus_message_iter_get_basic (iter, &mount_op_id);
+  dbus_message_iter_next (iter);
+
+  data = g_hash_table_lookup (volume->hash_mount_op_id_to_data, mount_op_id);
+
+  /* since eavesdropping is enabled on the session bus we get this signal even if it
+   * is for another application; so silently ignore it if it's not for us
+   */
+  if (data == NULL)
+    goto out;
+
+  g_signal_emit_by_name (data->mount_operation, "aborted");
+
+ out:
+  ;
 }
 
 static gboolean

Modified: trunk/monitor/proxy/gproxyvolume.h
==============================================================================
--- trunk/monitor/proxy/gproxyvolume.h	(original)
+++ trunk/monitor/proxy/gproxyvolume.h	Thu Feb 26 18:17:52 2009
@@ -53,6 +53,15 @@
 
 GProxyShadowMount *g_proxy_volume_get_shadow_mount (GProxyVolume        *volume);
 
+void          g_proxy_volume_handle_mount_op_ask_password (GProxyVolume        *volume,
+                                                           DBusMessageIter     *iter);
+
+void          g_proxy_volume_handle_mount_op_ask_question (GProxyVolume        *volume,
+                                                           DBusMessageIter     *iter);
+
+void          g_proxy_volume_handle_mount_op_aborted      (GProxyVolume        *volume,
+                                                           DBusMessageIter     *iter);
+
 G_END_DECLS
 
 #endif /* __G_PROXY_VOLUME_H__ */

Modified: trunk/monitor/proxy/gproxyvolumemonitor.c
==============================================================================
--- trunk/monitor/proxy/gproxyvolumemonitor.c	(original)
+++ trunk/monitor/proxy/gproxyvolumemonitor.c	Thu Feb 26 18:17:52 2009
@@ -1,7 +1,7 @@
 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
 /* gvfs - extensions for gio
  *
- * Copyright (C) 2006-2008 Red Hat, Inc.
+ * Copyright (C) 2006-2009 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
@@ -21,22 +21,9 @@
  * 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 even when no volume monitor is constructed
- *
- *  - 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.
+/* TODO: handle force_rescan in g_mount_guess_content_type(); right now we
+ *       just scan in the daemon first time the GMount is seen and
+ *       cache that result forever.
  */
 
 #include <config.h>
@@ -68,6 +55,9 @@
   GHashTable *drives;
   GHashTable *volumes;
   GHashTable *mounts;
+
+  /* The unique D-Bus name of the remote monitor or NULL if disconnected */
+  gchar *unique_name;
 };
 
 G_DEFINE_DYNAMIC_TYPE_EXTENDED (GProxyVolumeMonitor,
@@ -107,11 +97,21 @@
 };
 
 static char *
-get_match_rule (GProxyVolumeMonitor *monitor)
+get_match_rule_for_signals (GProxyVolumeMonitor *monitor)
 {
   return g_strdup_printf ("type='signal',"
                           "interface='org.gtk.Private.RemoteVolumeMonitor',"
-                          "sender='%s'",
+                          "sender='%s',",
+                          g_proxy_volume_monitor_get_dbus_name (monitor));
+}
+
+static char *
+get_match_rule_for_name_owner_changed (GProxyVolumeMonitor *monitor)
+{
+  return g_strdup_printf ("type='signal',"
+                          "interface='org.freedesktop.DBus',"
+                          "member='NameOwnerChanged',"
+                          "arg0='%s'",
                           g_proxy_volume_monitor_get_dbus_name (monitor));
 }
 
@@ -135,20 +135,32 @@
   g_hash_table_unref (monitor->volumes);
   g_hash_table_unref (monitor->mounts);
 
+  g_free (monitor->unique_name);
+
   dbus_connection_remove_filter (monitor->session_bus, filter_function, monitor);
-  match_rule = get_match_rule (monitor);
+
+  match_rule = get_match_rule_for_signals (monitor);
   dbus_error_init (&dbus_error);
   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);
+
+  match_rule = get_match_rule_for_name_owner_changed (monitor);
+  dbus_error_init (&dbus_error);
+  dbus_bus_remove_match (monitor->session_bus,
+                         match_rule,
+                         &dbus_error);
+  if (dbus_error_is_set (&dbus_error)) {
+    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)
@@ -306,16 +318,35 @@
   GHashTableIter vm_hash_iter;
   GHashTableIter vol_hash_iter;
   GProxyMount *candidate_mount;
+  static GVolumeMonitor *union_monitor = NULL;
 
-  /* 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.
+  /* There's a problem here insofar that this static method on GNativeVolumeMonitor can
+   * be called *before* any of our monitors are constructed. Since this method doesn't
+   * pass in the class structure we *know* which native remote monitor to use.
    *
-   * TODO: we don't handle the case when there's no volume monitor ever
-   * constructed. See TODO at the top of this file on how to handle that.
+   * To work around that, we get the singleton GVolumeMonitor... This will trigger
+   * construction of a GUnionVolumeMonitor in gio which will construct the *appropriate*
+   * remote volume monitors to use (it's up to gio to pick which one to use).
+   *
+   * Note that we will *hold* on to this reference effectively making us a resident
+   * module. And effectively keeping volume monitoring alive.
+   *
+   * The reason we hold on to the reference is that otherwise we'd be constructing/destructing
+   * *all* proxy volume monitors (which includes synchronous D-Bus calls to seed the monitor)
+   * every time this method is called.
+   *
+   * Note that *simple* GIO apps that a) don't use volume monitors; and b) don't use the
+   * g_file_find_enclosing_mount() method will never see any volume monitor overhead.
    */
 
+  /* Note that g_volume_monitor_get() is thread safe. We don't want to call it while
+   * holding the proxy_vm lock since it might end up calling our constructor.
+   */
+  if (union_monitor == NULL)
+    union_monitor = g_volume_monitor_get ();
+
   mount = NULL;
+
   G_LOCK (proxy_vm);
 
   /* First find the native volume monitor if one exists */
@@ -390,14 +421,24 @@
   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);
+
+  /* listen to volume monitor signals */
+  match_rule = get_match_rule_for_signals (monitor);
+  dbus_bus_add_match (monitor->session_bus,
+                      match_rule,
+                      &dbus_error);
+  if (dbus_error_is_set (&dbus_error)) {
+    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);
+
+  /* listen to when the owner of the service appears/disappears */
+  match_rule = get_match_rule_for_name_owner_changed (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);
   }
@@ -470,178 +511,283 @@
 
   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, &the_dbus_name);
-    dbus_message_iter_next (&iter);
-    dbus_message_iter_get_basic (&iter, &id);
-    dbus_message_iter_next (&iter);
-
-    if (strcmp (the_dbus_name, klass->dbus_name) != 0)
-      goto not_for_us;
-
-    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);
-          }
-      }
+  if (dbus_message_is_signal (message, "org.freedesktop.DBus", "NameOwnerChanged"))
+    {
+      GHashTableIter hash_iter;
+      GProxyMount *mount;
+      GProxyVolume *volume;
+      GProxyDrive *drive;
+      const gchar *name;
+      const gchar *old_owner;
+      const gchar *new_owner;
+
+      dbus_message_iter_init (message, &iter);
+      dbus_message_iter_get_basic (&iter, &name);
+      dbus_message_iter_next (&iter);
+      dbus_message_iter_get_basic (&iter, &old_owner);
+      dbus_message_iter_next (&iter);
+      dbus_message_iter_get_basic (&iter, &new_owner);
+      dbus_message_iter_next (&iter);
 
-  } 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, &the_dbus_name);
-    dbus_message_iter_next (&iter);
-    dbus_message_iter_get_basic (&iter, &id);
-    dbus_message_iter_next (&iter);
-
-    if (strcmp (the_dbus_name, klass->dbus_name) != 0)
-      goto not_for_us;
-
-    if (strcmp (member, "VolumeChanged") == 0)
-      {
-        volume = g_hash_table_lookup (monitor->volumes, id);
-        if (volume != NULL)
-          {
-            GProxyShadowMount *shadow_mount;
+      if (strcmp (name, klass->dbus_name) != 0)
+        goto not_for_us;
 
-            g_proxy_volume_update (volume, &iter);
-            signal_emit_in_idle (volume, "changed", NULL);
-            signal_emit_in_idle (monitor, "volume-changed", volume);
-
-            shadow_mount = g_proxy_volume_get_shadow_mount (volume);
-            if (shadow_mount != NULL)
-              {
-                signal_emit_in_idle (shadow_mount, "changed", NULL);
-                signal_emit_in_idle (monitor, "mount-changed", shadow_mount);
-                g_object_unref (shadow_mount);
-              }
-          }
-      }
-    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);
+      if (monitor->unique_name != NULL && g_strcmp0 (new_owner, monitor->unique_name) != 0)
+        {
+          g_warning ("Owner %s of volume monitor %s disconnected from the bus; removing drives/volumes/mounts",
+                     monitor->unique_name,
+                     klass->dbus_name);
+
+          g_hash_table_iter_init (&hash_iter, monitor->mounts);
+          while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &mount))
+            {
+              signal_emit_in_idle (mount, "unmounted", NULL);
+              signal_emit_in_idle (monitor, "mount-removed", mount);
+            }
+          g_hash_table_remove_all (monitor->mounts);
+
+          g_hash_table_iter_init (&hash_iter, monitor->volumes);
+          while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &volume))
+            {
+              signal_emit_in_idle (volume, "removed", NULL);
+              signal_emit_in_idle (monitor, "volume-removed", volume);
+            }
+          g_hash_table_remove_all (monitor->volumes);
+
+          g_hash_table_iter_init (&hash_iter, monitor->drives);
+          while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &drive))
+            {
+              signal_emit_in_idle (drive, "disconnected", NULL);
+              signal_emit_in_idle (monitor, "drive-disconnected", drive);
+            }
+          g_hash_table_remove_all (monitor->drives);
+
+          g_free (monitor->unique_name);
+          monitor->unique_name = NULL;
+
+          /* TODO: maybe try to relaunch the monitor? */
+
+        }
+
+      if (strlen (new_owner) > 0 && monitor->unique_name == NULL)
+        {
+          g_warning ("New owner %s for volume monitor %s connected to the bus; seeding drives/volumes/mounts",
+                     new_owner,
+                     klass->dbus_name);
+
+          seed_monitor (monitor);
+
+          /* emit signals for all the drives/volumes/mounts "added" */
+          g_hash_table_iter_init (&hash_iter, monitor->drives);
+          while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &drive))
+            signal_emit_in_idle (monitor, "drive-connected", drive);
+
+          g_hash_table_iter_init (&hash_iter, monitor->volumes);
+          while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &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, &the_dbus_name);
-    dbus_message_iter_next (&iter);
-    dbus_message_iter_get_basic (&iter, &id);
-    dbus_message_iter_next (&iter);
-
-    if (strcmp (the_dbus_name, klass->dbus_name) != 0)
-      goto not_for_us;
-
-    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);
+          g_hash_table_iter_init (&hash_iter, monitor->mounts);
+          while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &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);
-          }
-      }
+        }
 
-  }
+    }
+  else  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, &the_dbus_name);
+      dbus_message_iter_next (&iter);
+      dbus_message_iter_get_basic (&iter, &id);
+      dbus_message_iter_next (&iter);
+
+      if (strcmp (the_dbus_name, klass->dbus_name) != 0)
+        goto not_for_us;
+
+      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_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "MountOpAskPassword") ||
+           dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "MountOpAskQuestion") ||
+           dbus_message_is_signal (message, "org.gtk.Private.RemoteVolumeMonitor", "MountOpAborted"))
+    {
+      dbus_message_iter_init (message, &iter);
+      dbus_message_iter_get_basic (&iter, &the_dbus_name);
+      dbus_message_iter_next (&iter);
+      dbus_message_iter_get_basic (&iter, &id);
+      dbus_message_iter_next (&iter);
+
+      if (strcmp (the_dbus_name, klass->dbus_name) != 0)
+        goto not_for_us;
+
+      if (strcmp (member, "VolumeChanged") == 0)
+        {
+          volume = g_hash_table_lookup (monitor->volumes, id);
+          if (volume != NULL)
+            {
+              GProxyShadowMount *shadow_mount;
+
+              g_proxy_volume_update (volume, &iter);
+              signal_emit_in_idle (volume, "changed", NULL);
+              signal_emit_in_idle (monitor, "volume-changed", volume);
+
+              shadow_mount = g_proxy_volume_get_shadow_mount (volume);
+              if (shadow_mount != NULL)
+                {
+                  signal_emit_in_idle (shadow_mount, "changed", NULL);
+                  signal_emit_in_idle (monitor, "mount-changed", shadow_mount);
+                  g_object_unref (shadow_mount);
+                }
+            }
+        }
+      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 (strcmp (member, "MountOpAskPassword") == 0)
+        {
+          volume = g_hash_table_lookup (monitor->volumes, id);
+          if (volume != NULL)
+            g_proxy_volume_handle_mount_op_ask_password (volume, &iter);
+        }
+      else if (strcmp (member, "MountOpAskQuestion") == 0)
+        {
+          volume = g_hash_table_lookup (monitor->volumes, id);
+          if (volume != NULL)
+            g_proxy_volume_handle_mount_op_ask_question (volume, &iter);
+        }
+      else if (strcmp (member, "MountOpAborted") == 0)
+        {
+          volume = g_hash_table_lookup (monitor->volumes, id);
+          if (volume != NULL)
+            g_proxy_volume_handle_mount_op_aborted (volume, &iter);
+        }
+
+    }
+  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, &the_dbus_name);
+      dbus_message_iter_next (&iter);
+      dbus_message_iter_get_basic (&iter, &id);
+      dbus_message_iter_next (&iter);
+
+      if (strcmp (the_dbus_name, klass->dbus_name) != 0)
+        goto not_for_us;
+
+      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);
+            }
+        }
+    }
 
  not_for_us:
   G_UNLOCK (proxy_vm);
@@ -706,7 +852,7 @@
   is_supported = FALSE;
 
   message = dbus_message_new_method_call (dbus_name,
-                                          "/",
+                                          "/org/gtk/Private/RemoteVolumeMonitor",
                                           "org.gtk.Private.RemoteVolumeMonitor",
                                           "IsSupported");
   if (message == NULL)
@@ -788,7 +934,7 @@
   native_class->get_mount_for_mount_path = get_mount_for_mount_path;
 }
 
-/* Must be called with no locks held */
+/* Call with proxy_vm lock held */
 static void
 seed_monitor (GProxyVolumeMonitor *monitor)
 {
@@ -799,7 +945,7 @@
   DBusMessageIter iter_array;
 
   message = dbus_message_new_method_call (g_proxy_volume_monitor_get_dbus_name (monitor),
-                                          "/",
+                                          "/org/gtk/Private/RemoteVolumeMonitor",
                                           "org.gtk.Private.RemoteVolumeMonitor",
                                           "List");
   if (message == NULL)
@@ -869,6 +1015,8 @@
     }
   dbus_message_iter_next (&iter_reply);
 
+  monitor->unique_name = g_strdup (dbus_message_get_sender (reply));
+
   dbus_message_unref (reply);
 
  fail:

Modified: trunk/monitor/proxy/gproxyvolumemonitor.h
==============================================================================
--- trunk/monitor/proxy/gproxyvolumemonitor.h	(original)
+++ trunk/monitor/proxy/gproxyvolumemonitor.h	Thu Feb 26 18:17:52 2009
@@ -31,11 +31,12 @@
 
 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))
+#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_PROXY_VOLUME_MONITOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), 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;

Modified: trunk/monitor/proxy/gvfsproxyvolumemonitordaemon.c
==============================================================================
--- trunk/monitor/proxy/gvfsproxyvolumemonitordaemon.c	(original)
+++ trunk/monitor/proxy/gvfsproxyvolumemonitordaemon.c	Thu Feb 26 18:17:52 2009
@@ -28,15 +28,173 @@
 #include <dbus/dbus.h>
 #include <glib/gi18n.h>
 #include <stdlib.h>
+#include <glib/gprintf.h>
 
 #include "gdbusutils.h"
 #include "gvfsproxyvolumemonitordaemon.h"
 
+/* ---------------------------------------------------------------------------------------------------- */
+
+/* #define DEBUG_ENABLED */
+
+#ifdef DEBUG_ENABLED
+static void
+print_debug (const gchar *format, ...)
+{
+  va_list      var_args;
+
+  va_start (var_args, format);
+
+  g_print ("### debug: ");
+  g_vprintf (format, var_args);
+  g_print ("\n");
+
+  va_end (var_args);
+}
+#else
+static void
+print_debug (const gchar *format, ...)
+{
+}
+#endif
+
+
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+GType g_proxy_mount_operation_get_type (void) G_GNUC_CONST;
+
+typedef struct
+{
+  GMountOperation parent_instance;
+} GProxyMountOperation;
+
+typedef struct
+{
+  GMountOperationClass parent_class;
+} GProxyMountOperationClass;
+
+
+static GMountOperation *
+g_proxy_mount_operation_new (void)
+{
+  return G_MOUNT_OPERATION (g_object_new (g_proxy_mount_operation_get_type(), NULL));
+}
+
+G_DEFINE_TYPE (GProxyMountOperation, g_proxy_mount_operation, G_TYPE_MOUNT_OPERATION)
+
+static void
+g_proxy_mount_operation_init (GProxyMountOperation *mount_operation)
+{
+}
+
+static void
+g_proxy_mount_operation_ask_password (GMountOperation *op,
+                                      const char      *message,
+                                      const char      *default_user,
+                                      const char      *default_domain,
+                                      GAskPasswordFlags flags)
+{
+  /* do nothing */
+}
+
+static void
+g_proxy_mount_operation_ask_question (GMountOperation *op,
+                                      const char      *message,
+                                      const char      *choices[])
+{
+  /* do nothing */
+}
+
+static void
+g_proxy_mount_operation_class_init (GProxyMountOperationClass *klass)
+{
+  GMountOperationClass *mount_op_class;
+
+  mount_op_class = G_MOUNT_OPERATION_CLASS (klass);
+
+  mount_op_class->ask_password = g_proxy_mount_operation_ask_password;
+  mount_op_class->ask_question = g_proxy_mount_operation_ask_question;
+}
+
+
 static GVolumeMonitor *monitor = NULL;
 static DBusConnection *connection = NULL;
 static GType the_volume_monitor_type;
 static const char *the_dbus_name = NULL;
 
+static GList *outstanding_ops = NULL;
+
+static GHashTable *unique_names_being_watched = NULL;
+
+static void
+cancellable_destroyed_cb (gpointer user_data,
+                          GObject *where_the_cancellable_was)
+{
+  outstanding_ops = g_list_remove (outstanding_ops, where_the_cancellable_was);
+}
+
+static void
+remove_name_owned_changed_for_unique_name (const gchar *unique_name)
+{
+  const gchar *match_rule;
+  DBusError dbus_error;
+
+  match_rule = g_hash_table_lookup (unique_names_being_watched, unique_name);
+  if (match_rule == NULL)
+    {
+      g_warning ("Was asked to remove match rule for unique_name %s but we don't have one", unique_name);
+      goto out;
+    }
+
+  dbus_error_init (&dbus_error);
+  dbus_bus_remove_match (connection,
+                         match_rule,
+                         &dbus_error);
+  if (dbus_error_is_set (&dbus_error)) {
+    g_warning ("cannot remove match rule '%s': %s: %s", match_rule, dbus_error.name, dbus_error.message);
+    dbus_error_free (&dbus_error);
+  }
+
+  g_hash_table_remove (unique_names_being_watched, unique_name);
+
+ out:
+  ;
+}
+
+static void
+ensure_name_owner_changed_for_unique_name (const gchar *unique_name)
+{
+  gchar *match_rule;
+  DBusError dbus_error;
+
+  if (g_hash_table_lookup (unique_names_being_watched, unique_name) != NULL)
+    goto out;
+
+  match_rule = g_strdup_printf ("type='signal',"
+                                "interface='org.freedesktop.DBus',"
+                                "member='NameOwnerChanged',"
+                                "arg0='%s'",
+                                unique_name);
+
+  dbus_error_init (&dbus_error);
+  dbus_bus_add_match (connection,
+                      match_rule,
+                      &dbus_error);
+  if (dbus_error_is_set (&dbus_error))
+    {
+      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);
+      goto out;
+    }
+
+  g_hash_table_insert (unique_names_being_watched, g_strdup (unique_name), match_rule);
+
+ out:
+  ;
+}
+
 static void monitor_try_create (void);
 
 /* string               id
@@ -46,10 +204,11 @@
  * boolean              can-poll-for-media
  * boolean              has-media
  * boolean              is-media-removable
+ * boolean              is-media-check-automatic
  * array:string         volume-ids
  * dict:string->string  identifiers
  */
-#define DRIVE_STRUCT_TYPE "(sssbbbbasa{ss})"
+#define DRIVE_STRUCT_TYPE "(sssbbbbbasa{ss})"
 
 static void
 append_drive (GDrive *drive, DBusMessageIter *iter_array)
@@ -65,6 +224,7 @@
   gboolean can_poll_for_media;
   gboolean has_media;
   gboolean is_media_removable;
+  gboolean is_media_check_automatic;
   GList *volumes, *l;
   char **identifiers;
   int n;
@@ -82,6 +242,7 @@
   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);
+  is_media_check_automatic = g_drive_is_media_check_automatic (drive);
   volumes = g_drive_get_volumes (drive);
   identifiers = g_drive_enumerate_identifiers (drive);
 
@@ -95,6 +256,7 @@
   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_append_basic (&iter_struct, DBUS_TYPE_BOOLEAN, &is_media_check_automatic);
 
   dbus_message_iter_open_container (&iter_struct, DBUS_TYPE_ARRAY, "s", &iter_volume_array);
   for (l = volumes; l != NULL; l = l->next)
@@ -108,7 +270,7 @@
   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++)
+  for (n = 0; identifiers != NULL && identifiers[n] != NULL; n++)
     {
       DBusMessageIter iter_dict_entry;
       char *id_value;
@@ -214,7 +376,7 @@
   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++)
+  for (n = 0; identifiers != NULL && identifiers[n] != NULL; n++)
     {
       DBusMessageIter iter_dict_entry;
       char *id_value;
@@ -343,6 +505,8 @@
   DBusMessageIter iter_array;
   DBusMessage *reply;
 
+  print_debug ("in handle_list");
+
   drives = g_volume_monitor_get_connected_drives (monitor);
   volumes = g_volume_monitor_get_volumes (monitor);
   mounts = g_volume_monitor_get_mounts (monitor);
@@ -383,14 +547,20 @@
   GError *error;
   DBusMessage *reply;
 
+  print_debug ("in mount_unmount_cb");
+
+  g_object_set_data (G_OBJECT (mount), "cancellable", NULL);
+
   error = NULL;
   if (!g_mount_unmount_finish (mount, result, &error))
     {
+      print_debug ("  error: %s", error->message);
       reply = _dbus_message_new_from_gerror (message, error);
       g_error_free (error);
     }
   else
     {
+      print_debug (" success");
       reply = dbus_message_new_method_return (message);
     }
 
@@ -403,6 +573,9 @@
 handle_mount_unmount (DBusConnection *connection, DBusMessage *message)
 {
   const char *id;
+  const char *cancellation_id;
+  const char *sender;
+  GCancellable *cancellable;
   dbus_uint32_t unmount_flags;
   DBusError dbus_error;
   GList *mounts, *l;
@@ -416,7 +589,8 @@
   dbus_error_init (&dbus_error);
   if (!dbus_message_get_args (message, &dbus_error,
                               DBUS_TYPE_STRING, &id,
-                              DBUS_TYPE_UINT32 &unmount_flags,
+                              DBUS_TYPE_STRING, &cancellation_id,
+                              DBUS_TYPE_UINT32, &unmount_flags,
                               DBUS_TYPE_INVALID))
     {
       g_warning ("Error parsing args for MountUnmount(): %s: %s", dbus_error.name, dbus_error.message);
@@ -424,8 +598,12 @@
       goto out;
     }
 
+  print_debug ("in handle_mount_unmount");
+
   ret = DBUS_HANDLER_RESULT_HANDLED;
 
+  sender = dbus_message_get_sender (message);
+
   mount = NULL;
   mounts = g_volume_monitor_get_mounts (monitor);
   for (l = mounts; l != NULL; l = l->next)
@@ -453,6 +631,26 @@
       goto out;
     }
 
+  if (g_object_get_data (G_OBJECT (mount), "cancellable") != NULL)
+    {
+      DBusMessage *reply;
+      reply = dbus_message_new_error (message,
+                                      "org.gtk.Private.RemoteVolumeMonitor.Failed",
+                                      "An operation is already pending");
+      dbus_connection_send (connection, reply, NULL);
+      dbus_message_unref (reply);
+      goto out;
+    }
+
+  cancellable = g_cancellable_new ();
+  g_object_set_data_full (G_OBJECT (mount), "cancellable", cancellable, g_object_unref);
+  g_object_set_data_full (G_OBJECT (cancellable), "owner", g_strdup (sender), g_free);
+  g_object_set_data_full (G_OBJECT (cancellable), "cancellation_id", g_strdup (cancellation_id), g_free);
+  outstanding_ops = g_list_prepend (outstanding_ops, cancellable);
+  g_object_weak_ref (G_OBJECT (cancellable),
+                     cancellable_destroyed_cb,
+                     NULL);
+
   g_mount_unmount (mount,
                    unmount_flags,
                    NULL,
@@ -470,20 +668,140 @@
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+static DBusHandlerResult
+handle_mount_op_reply (DBusConnection *connection, DBusMessage *message)
+{
+  const char *id;
+  const char *mount_op_id;
+  dbus_int32_t result;
+  const char *user_name;
+  const char *domain;
+  const char *encoded_password;
+  char *decoded_password;
+  gsize decoded_password_len;
+  dbus_int32_t password_save;
+  dbus_int32_t choice;
+  dbus_bool_t anonymous;
+  DBusError dbus_error;
+  DBusHandlerResult ret;
+  GList *volumes, *l;
+  GVolume *volume;
+  DBusMessage *reply;
+  GMountOperation *mount_operation;
+
+  volumes = NULL;
+  decoded_password = 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_STRING, &mount_op_id,
+                              DBUS_TYPE_INT32, &result,
+                              DBUS_TYPE_STRING, &user_name,
+                              DBUS_TYPE_STRING, &domain,
+                              DBUS_TYPE_STRING, &encoded_password,
+                              DBUS_TYPE_INT32, &password_save,
+                              DBUS_TYPE_INT32, &choice,
+                              DBUS_TYPE_BOOLEAN, &anonymous,
+                              DBUS_TYPE_INVALID))
+    {
+      g_warning ("Error parsing args for MountOpReply(): %s: %s", dbus_error.name, dbus_error.message);
+      dbus_error_free (&dbus_error);
+      goto out;
+    }
+
+  print_debug ("in handle_mount_op_reply");
+
+  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 = g_object_get_data (G_OBJECT (volume), "mount_operation");
+  if (mount_operation == NULL)
+    {
+      DBusMessage *reply;
+      reply = dbus_message_new_error (message,
+                                      "org.gtk.Private.RemoteVolumeMonitor.NotFound",
+                                      "No outstanding mount operation");
+      dbus_connection_send (connection, reply, NULL);
+      dbus_message_unref (reply);
+      goto out;
+    }
+
+  decoded_password = (gchar *) g_base64_decode (encoded_password, &decoded_password_len);
+
+  g_mount_operation_set_username (mount_operation, user_name);
+  g_mount_operation_set_domain (mount_operation, domain);
+  g_mount_operation_set_password (mount_operation, decoded_password);
+  g_mount_operation_set_password_save (mount_operation, password_save);
+  g_mount_operation_set_choice (mount_operation, choice);
+  g_mount_operation_set_anonymous (mount_operation, anonymous);
+
+  g_mount_operation_reply (mount_operation, result);
+
+  reply = dbus_message_new_method_return (message);
+  dbus_connection_send (connection, reply, NULL);
+  dbus_message_unref (reply);
+
+ out:
+  g_free (decoded_password);
+  if (volumes != NULL)
+    {
+      g_list_foreach (volumes, (GFunc) g_object_unref, NULL);
+      g_list_free (volumes);
+    }
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
 static void
 volume_mount_cb (GVolume *volume, GAsyncResult *result, DBusMessage *message)
 {
   GError *error;
   DBusMessage *reply;
 
+  print_debug ("in volume_mount_cb");
+
+  g_object_set_data (G_OBJECT (volume), "mount_operation", NULL);
+  g_object_set_data (G_OBJECT (volume), "cancellable", NULL);
+
   error = NULL;
   if (!g_volume_mount_finish (volume, result, &error))
     {
+      print_debug ("  error: %s", error->message);
       reply = _dbus_message_new_from_gerror (message, error);
       g_error_free (error);
     }
   else
     {
+      print_debug (" success");
       reply = dbus_message_new_method_return (message);
     }
 
@@ -492,26 +810,170 @@
   dbus_message_unref (reply);
 }
 
+static void
+ask_password_cb (GMountOperation  *mount_operation,
+                 const gchar      *message_to_show,
+                 const gchar      *default_user,
+                 const gchar      *default_domain,
+                 GAskPasswordFlags flags,
+                 gpointer          user_data)
+{
+  gchar *id;
+  DBusMessage *message;
+  DBusMessageIter iter;
+  GVolume *volume;
+  const gchar *mount_op_id;
+  const gchar *mount_op_owner;
+
+  print_debug ("in ask_password_cb %s", message_to_show);
+
+  volume = G_VOLUME (user_data);
+
+  id = g_strdup_printf ("%p", volume);
+
+  mount_op_id = g_object_get_data (G_OBJECT (mount_operation), "mount_op_id");
+  mount_op_owner = g_object_get_data (G_OBJECT (mount_operation), "mount_op_owner");
+
+  message = dbus_message_new_signal ("/org/gtk/Private/RemoteVolumeMonitor",
+                                     "org.gtk.Private.RemoteVolumeMonitor",
+                                     "MountOpAskPassword");
+  dbus_message_iter_init_append (message, &iter);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &the_dbus_name);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &id);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &mount_op_id);
+
+  if (message_to_show == NULL)
+    message_to_show = "";
+
+  if (default_user == NULL)
+    default_user = "";
+
+  if (default_domain == NULL)
+    default_domain = "";
+
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &message_to_show);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &default_user);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &default_domain);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &flags);
+
+  dbus_message_set_destination (message, mount_op_owner);
+
+  dbus_connection_send (connection, message, NULL);
+  dbus_message_unref (message);
+
+  g_free (id);
+}
+
+static void
+ask_question_cb (GMountOperation  *mount_operation,
+                 const gchar      *message_to_show,
+                 gchar           **choices,
+                 gpointer          user_data)
+{
+  gchar *id;
+  DBusMessage *message;
+  DBusMessageIter iter;
+  DBusMessageIter iter_string_array;
+  const gchar *mount_op_id;
+  const gchar *mount_op_owner;
+  GVolume *volume;
+  guint n;
+
+  print_debug ("in ask_question_cb %s", message_to_show);
+
+  volume = G_VOLUME (user_data);
+
+  id = g_strdup_printf ("%p", volume);
+
+  mount_op_id = g_object_get_data (G_OBJECT (mount_operation), "mount_op_id");
+  mount_op_owner = g_object_get_data (G_OBJECT (mount_operation), "mount_op_owner");
+
+  message = dbus_message_new_signal ("/org/gtk/Private/RemoteVolumeMonitor",
+                                     "org.gtk.Private.RemoteVolumeMonitor",
+                                     "MountOpAskQuestion");
+  dbus_message_iter_init_append (message, &iter);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &the_dbus_name);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &id);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &mount_op_id);
+
+  if (message_to_show == NULL)
+    message_to_show = "";
+
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &message_to_show);
+
+  dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &iter_string_array);
+  for (n = 0; choices != NULL && choices[n] != NULL; n++)
+    dbus_message_iter_append_basic (&iter_string_array, DBUS_TYPE_STRING, &(choices[n]));
+  dbus_message_iter_close_container (&iter, &iter_string_array);
+
+  dbus_message_set_destination (message, mount_op_owner);
+
+  dbus_connection_send (connection, message, NULL);
+  dbus_message_unref (message);
+
+  g_free (id);
+}
+
+static void
+aborted_cb (GMountOperation  *mount_operation,
+            gpointer          user_data)
+{
+  gchar *id;
+  DBusMessage *message;
+  DBusMessageIter iter;
+  GVolume *volume;
+  const gchar *mount_op_id;
+  const gchar *mount_op_owner;
+
+  print_debug ("in aborted_cb");
+
+  volume = G_VOLUME (user_data);
+
+  id = g_strdup_printf ("%p", volume);
+
+  mount_op_id = g_object_get_data (G_OBJECT (mount_operation), "mount_op_id");
+  mount_op_owner = g_object_get_data (G_OBJECT (mount_operation), "mount_op_owner");
+
+  message = dbus_message_new_signal ("/org/gtk/Private/RemoteVolumeMonitor",
+                                     "org.gtk.Private.RemoteVolumeMonitor",
+                                     "MountOpAborted");
+  dbus_message_iter_init_append (message, &iter);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &the_dbus_name);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &id);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &mount_op_id);
+
+  dbus_message_set_destination (message, mount_op_owner);
+
+  dbus_connection_send (connection, message, NULL);
+  dbus_message_unref (message);
+
+  g_free (id);
+}
+
 static DBusHandlerResult
 handle_volume_mount (DBusConnection *connection, DBusMessage *message)
 {
   const char *id;
+  const char *cancellation_id;
+  const char *sender;
   dbus_uint32_t mount_flags;
-  dbus_bool_t use_mount_operation;
+  const char *mount_op_id;
   DBusError dbus_error;
   GList *volumes, *l;
   GVolume *volume;
   DBusHandlerResult ret;
   GMountOperation *mount_operation;
+  GCancellable *cancellable;
 
-  volume = NULL;
+  volumes = 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_STRING, &cancellation_id,
                               DBUS_TYPE_UINT32, &mount_flags,
-                              DBUS_TYPE_BOOLEAN, &use_mount_operation,
+                              DBUS_TYPE_STRING, &mount_op_id,
                               DBUS_TYPE_INVALID))
     {
       g_warning ("Error parsing args for VolumeMount(): %s: %s", dbus_error.name, dbus_error.message);
@@ -519,8 +981,12 @@
       goto out;
     }
 
+  print_debug ("in handle_volume_mount");
+
   ret = DBUS_HANDLER_RESULT_HANDLED;
 
+  sender = dbus_message_get_sender (message);
+
   volume = NULL;
   volumes = g_volume_monitor_get_volumes (monitor);
   for (l = volumes; l != NULL; l = l->next)
@@ -548,20 +1014,45 @@
       goto out;
     }
 
+  if (g_object_get_data (G_OBJECT (volume), "cancellable") != NULL)
+    {
+      DBusMessage *reply;
+      reply = dbus_message_new_error (message,
+                                      "org.gtk.Private.RemoteVolumeMonitor.Failed",
+                                      "An operation is already pending");
+      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 ();
+  if (mount_op_id != NULL && strlen (mount_op_id) > 0)
+    {
+      mount_operation = g_proxy_mount_operation_new ();
+      g_signal_connect (mount_operation, "ask-password", G_CALLBACK (ask_password_cb), volume);
+      g_signal_connect (mount_operation, "ask-question", G_CALLBACK (ask_question_cb), volume);
+      g_signal_connect (mount_operation, "aborted", G_CALLBACK (aborted_cb), volume);
+      g_object_set_data_full (G_OBJECT (mount_operation), "mount_op_id", g_strdup (mount_op_id), g_free);
+      g_object_set_data_full (G_OBJECT (mount_operation), "mount_op_owner", g_strdup (sender), g_free);
+      g_object_set_data_full (G_OBJECT (volume), "mount_operation", mount_operation, g_object_unref);
+    }
+
+  cancellable = g_cancellable_new ();
+  g_object_set_data_full (G_OBJECT (volume), "cancellable", cancellable, g_object_unref);
+  g_object_set_data_full (G_OBJECT (cancellable), "owner", g_strdup (sender), g_free);
+  g_object_set_data_full (G_OBJECT (cancellable), "cancellation_id", g_strdup (cancellation_id), g_free);
+  outstanding_ops = g_list_prepend (outstanding_ops, cancellable);
+  g_object_weak_ref (G_OBJECT (cancellable),
+                     cancellable_destroyed_cb,
+                     NULL);
 
   g_volume_mount (volume,
                   mount_flags,
                   mount_operation,
-                  NULL,
+                  cancellable,
                   (GAsyncReadyCallback) volume_mount_cb,
                   dbus_message_ref (message));
 
-  if (mount_operation != NULL)
-    g_object_unref (mount_operation);
-
  out:
   if (volumes != NULL)
     {
@@ -579,14 +1070,20 @@
   GError *error;
   DBusMessage *reply;
 
+  print_debug ("in drive_eject_cb");
+
+  g_object_set_data (G_OBJECT (drive), "cancellable", NULL);
+
   error = NULL;
   if (!g_drive_eject_finish (drive, result, &error))
     {
+      print_debug ("  error: %s", error->message);
       reply = _dbus_message_new_from_gerror (message, error);
       g_error_free (error);
     }
   else
     {
+      print_debug (" success");
       reply = dbus_message_new_method_return (message);
     }
 
@@ -599,6 +1096,9 @@
 handle_drive_eject (DBusConnection *connection, DBusMessage *message)
 {
   const char *id;
+  const char *cancellation_id;
+  const char *sender;
+  GCancellable *cancellable;
   dbus_uint32_t unmount_flags;
   DBusError dbus_error;
   GList *drives, *l;
@@ -606,12 +1106,14 @@
   DBusHandlerResult ret;
 
   drive = NULL;
+  drives = 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_STRING, &cancellation_id,
                               DBUS_TYPE_UINT32 &unmount_flags,
                               DBUS_TYPE_INVALID))
     {
@@ -620,8 +1122,12 @@
       goto out;
     }
 
+  print_debug ("in handle_drive_eject");
+
   ret = DBUS_HANDLER_RESULT_HANDLED;
 
+  sender = dbus_message_get_sender (message);
+
   drive = NULL;
   drives = g_volume_monitor_get_connected_drives (monitor);
   for (l = drives; l != NULL; l = l->next)
@@ -649,9 +1155,29 @@
       goto out;
     }
 
+  if (g_object_get_data (G_OBJECT (drive), "cancellable") != NULL)
+    {
+      DBusMessage *reply;
+      reply = dbus_message_new_error (message,
+                                      "org.gtk.Private.RemoteVolumeMonitor.Failed",
+                                      "An operation is already pending");
+      dbus_connection_send (connection, reply, NULL);
+      dbus_message_unref (reply);
+      goto out;
+    }
+
+  cancellable = g_cancellable_new ();
+  g_object_set_data_full (G_OBJECT (drive), "cancellable", cancellable, g_object_unref);
+  g_object_set_data_full (G_OBJECT (cancellable), "owner", g_strdup (sender), g_free);
+  g_object_set_data_full (G_OBJECT (cancellable), "cancellation_id", g_strdup (cancellation_id), g_free);
+  outstanding_ops = g_list_prepend (outstanding_ops, cancellable);
+  g_object_weak_ref (G_OBJECT (cancellable),
+                     cancellable_destroyed_cb,
+                     NULL);
+
   g_drive_eject (drive,
                  unmount_flags,
-                 NULL,
+                 cancellable,
                  (GAsyncReadyCallback) drive_eject_cb,
                  dbus_message_ref (message));
 
@@ -672,14 +1198,20 @@
   GError *error;
   DBusMessage *reply;
 
+  print_debug ("in drive_poll_for_media_cb");
+
+  g_object_set_data (G_OBJECT (drive), "cancellable", NULL);
+
   error = NULL;
   if (!g_drive_poll_for_media_finish (drive, result, &error))
     {
+      print_debug ("  error: %s", error->message);
       reply = _dbus_message_new_from_gerror (message, error);
       g_error_free (error);
     }
   else
     {
+      print_debug (" success");
       reply = dbus_message_new_method_return (message);
     }
 
@@ -692,17 +1224,22 @@
 handle_drive_poll_for_media (DBusConnection *connection, DBusMessage *message)
 {
   const char *id;
+  const char *cancellation_id;
+  const char *sender;
+  GCancellable *cancellable;
   DBusError dbus_error;
   GList *drives, *l;
   GDrive *drive;
   DBusHandlerResult ret;
 
   drive = NULL;
+  drives = 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_STRING, &cancellation_id,
                               DBUS_TYPE_INVALID))
     {
       g_warning ("Error parsing args for DrivePollForMedia(): %s: %s", dbus_error.name, dbus_error.message);
@@ -710,8 +1247,12 @@
       goto out;
     }
 
+  print_debug ("in handle_drive_poll_for_media");
+
   ret = DBUS_HANDLER_RESULT_HANDLED;
 
+  sender = dbus_message_get_sender (message);
+
   drive = NULL;
   drives = g_volume_monitor_get_connected_drives (monitor);
   for (l = drives; l != NULL; l = l->next)
@@ -739,8 +1280,28 @@
       goto out;
     }
 
+  if (g_object_get_data (G_OBJECT (drive), "cancellable") != NULL)
+    {
+      DBusMessage *reply;
+      reply = dbus_message_new_error (message,
+                                      "org.gtk.Private.RemoteVolumeMonitor.Failed",
+                                      "An operation is already pending");
+      dbus_connection_send (connection, reply, NULL);
+      dbus_message_unref (reply);
+      goto out;
+    }
+
+  cancellable = g_cancellable_new ();
+  g_object_set_data_full (G_OBJECT (drive), "cancellable", cancellable, g_object_unref);
+  g_object_set_data_full (G_OBJECT (cancellable), "owner", g_strdup (sender), g_free);
+  g_object_set_data_full (G_OBJECT (cancellable), "cancellation_id", g_strdup (cancellation_id), g_free);
+  outstanding_ops = g_list_prepend (outstanding_ops, cancellable);
+  g_object_weak_ref (G_OBJECT (cancellable),
+                     cancellable_destroyed_cb,
+                     NULL);
+
   g_drive_poll_for_media (drive,
-                          NULL,
+                          cancellable,
                           (GAsyncReadyCallback) drive_poll_for_media_cb,
                           dbus_message_ref (message));
 
@@ -762,6 +1323,8 @@
   DBusMessage *reply;
   DBusMessageIter iter;
 
+  print_debug ("in handle_supported");
+
   /* if monitor wasn't created on startup; try again */
   if (monitor == NULL)
     monitor_try_create ();
@@ -780,40 +1343,157 @@
 /* ---------------------------------------------------------------------------------------------------- */
 
 static DBusHandlerResult
+handle_cancel_operation (DBusConnection *connection, DBusMessage *message)
+{
+  DBusMessage *reply;
+  DBusMessageIter iter;
+  DBusError dbus_error;
+  dbus_bool_t was_cancelled;
+  const char *sender;
+  const char *cancellation_id;
+  GList *l;
+
+  was_cancelled = FALSE;
+
+  sender = dbus_message_get_sender (message);
+
+  dbus_error_init (&dbus_error);
+  if (!dbus_message_get_args (message, &dbus_error,
+                              DBUS_TYPE_STRING, &cancellation_id,
+                              DBUS_TYPE_INVALID))
+    {
+      g_warning ("Error parsing args for CancelOperation(): %s: %s", dbus_error.name, dbus_error.message);
+      dbus_error_free (&dbus_error);
+      goto out;
+    }
+
+  print_debug ("in handle_cancel_operation");
+
+  /* Find GCancellable to cancel */
+  for (l = outstanding_ops; l != NULL; l = l->next)
+    {
+      GCancellable *cancellable = G_CANCELLABLE (l->data);
+      const gchar *owner;
+      const gchar *id;
+
+      owner = g_object_get_data (G_OBJECT (cancellable), "owner");
+      id = g_object_get_data (G_OBJECT (cancellable), "cancellation_id");
+      if (g_strcmp0 (owner, sender) == 0 && g_strcmp0 (id, cancellation_id) == 0)
+        {
+          print_debug ("found op to cancel");
+          g_cancellable_cancel (cancellable);
+
+          was_cancelled = TRUE;
+          break;
+        }
+    }
+
+  if (!was_cancelled)
+    g_warning ("didn't find op to cancel");
+
+ out:
+  reply = dbus_message_new_method_return (message);
+  dbus_message_iter_init_append (reply, &iter);
+  dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &was_cancelled);
+  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)
+  if (dbus_message_is_signal (message, "org.freedesktop.DBus", "NameLost"))
     {
-      ret = handle_is_supported (connection, message);
+      /* means that someone has claimed our name (we allow replacement) */
+      g_warning ("Got NameLost, some other instance replaced us");
+      exit (0);
+    }
+  else if (dbus_message_is_signal (message, "org.freedesktop.DBus", "NameOwnerChanged"))
+    {
+      DBusMessageIter iter;
+      const gchar *name;
+      const gchar *old_owner;
+      const gchar *new_owner;
+
+      dbus_message_iter_init (message, &iter);
+      dbus_message_iter_get_basic (&iter, &name);
+      dbus_message_iter_next (&iter);
+      dbus_message_iter_get_basic (&iter, &old_owner);
+      dbus_message_iter_next (&iter);
+      dbus_message_iter_get_basic (&iter, &new_owner);
+      dbus_message_iter_next (&iter);
+
+      print_debug ("NameOwnerChanged: '%s' '%s' '%s'", name, old_owner, new_owner);
+
+      if (strlen (new_owner) == 0)
+        {
+          GList *l;
+
+          /* see if @name has outstanding ops; if so, cancel them */
+          for (l = outstanding_ops; l != NULL; l = l->next)
+            {
+              GCancellable *cancellable = G_CANCELLABLE (l->data);
+              const gchar *owner;
+
+              owner = g_object_get_data (G_OBJECT (cancellable), "owner");
+              print_debug ("looking at op for %s", owner);
+              if (g_strcmp0 (owner, name) == 0)
+                {
+                  print_debug ("****** name has an outstanding op");
+                  g_cancellable_cancel (cancellable);
+                }
+            }
+
+          remove_name_owned_changed_for_unique_name (name);
+        }
+
     }
-  else
+  else if (g_strcmp0 (dbus_message_get_interface (message), "org.gtk.Private.RemoteVolumeMonitor") == 0 &&
+           g_strcmp0 (dbus_message_get_path (message), "/org/gtk/Private/RemoteVolumeMonitor") == 0)
     {
-      if (monitor != NULL)
+      /* If someone is calling into this object and interface, start watching their name so
+       * we can cancel operations initiated by them when they disconnect
+       */
+      ensure_name_owner_changed_for_unique_name (dbus_message_get_sender (message));
+
+      if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "IsSupported"))
+        {
+          ret = handle_is_supported (connection, message);
+        }
+      else
         {
-          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);
+          if (monitor != NULL)
+            {
+              if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "List"))
+                ret = handle_list (connection, message);
+
+              else if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "CancelOperation"))
+                ret = handle_cancel_operation (connection, message);
+
+              else if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "MountUnmount"))
+                ret = handle_mount_unmount (connection, message);
+
+              else if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "MountOpReply"))
+                ret = handle_mount_op_reply (connection, message);
+
+              else if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "VolumeMount"))
+                ret = handle_volume_mount (connection, message);
+
+              else if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "DriveEject"))
+                ret = handle_drive_eject (connection, message);
+
+              else if (dbus_message_is_method_call (message, "org.gtk.Private.RemoteVolumeMonitor", "DrivePollForMedia"))
+                ret = handle_drive_poll_for_media (connection, message);
+
+            }
         }
     }
 
@@ -831,7 +1511,9 @@
 
   id = g_strdup_printf ("%p", object);
 
-  message = dbus_message_new_signal ("/", "org.gtk.Private.RemoteVolumeMonitor", signal_name);
+  message = dbus_message_new_signal ("/org/gtk/Private/RemoteVolumeMonitor",
+                                     "org.gtk.Private.RemoteVolumeMonitor",
+                                     signal_name);
   dbus_message_iter_init_append (message, &iter);
   dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &the_dbus_name);
   dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &id);
@@ -1006,6 +1688,7 @@
 
   the_volume_monitor_type = volume_monitor_type;
   the_dbus_name = dbus_name;
+  unique_names_being_watched = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
 
   /* try and create the monitor */
   monitor_try_create ();
@@ -1021,7 +1704,12 @@
 
   _g_dbus_connection_integrate_with_main (connection);
 
-  rc = dbus_bus_request_name (connection, dbus_name, 0, &dbus_error);
+  rc = dbus_bus_request_name (connection,
+                              dbus_name,
+                              DBUS_NAME_FLAG_ALLOW_REPLACEMENT |
+                              DBUS_NAME_FLAG_DO_NOT_QUEUE |
+                              DBUS_NAME_FLAG_REPLACE_EXISTING,
+                              &dbus_error);
   if (dbus_error_is_set (&dbus_error))
     {
       g_warning ("dbus_bus_request_name failed: %s: %s", dbus_error.name, dbus_error.message);

Modified: trunk/monitor/proxy/remote-volume-monitor-module.c
==============================================================================
--- trunk/monitor/proxy/remote-volume-monitor-module.c	(original)
+++ trunk/monitor/proxy/remote-volume-monitor-module.c	Thu Feb 26 18:17:52 2009
@@ -41,6 +41,15 @@
   if (g_getenv ("GVFS_REMOTE_VOLUME_MONITOR_IGNORE") != NULL)
     goto out;
 
+  /* We make this module resident since we *may* hold on to an instance
+   * of the union monitor in the static method get_mount_for_mount_path()
+   * on GNativeVolumeMonitor. And it doesn't make much sense to unload
+   * the module *anyway*.
+   *
+   * See the comment gproxyvolumemonitor.c:get_mount_for_mount_path().
+   */
+  g_type_module_use (G_TYPE_MODULE (module));
+
   bindtextdomain (GETTEXT_PACKAGE, GVFS_LOCALEDIR);
   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
 



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