diff options
Diffstat (limited to 'trunk/monitor/proxy/gproxyvolume.c')
-rw-r--r-- | trunk/monitor/proxy/gproxyvolume.c | 1229 |
1 files changed, 1229 insertions, 0 deletions
diff --git a/trunk/monitor/proxy/gproxyvolume.c b/trunk/monitor/proxy/gproxyvolume.c new file mode 100644 index 00000000..2d171219 --- /dev/null +++ b/trunk/monitor/proxy/gproxyvolume.c @@ -0,0 +1,1229 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* gvfs - extensions for gio + * + * Copyright (C) 2006-2008 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: David Zeuthen <davidz@redhat.com> + */ + +#include <config.h> + +#include <string.h> +#include <sys/wait.h> +#include <unistd.h> + +#include <glib.h> +#include <glib/gi18n-lib.h> +#include <gio/gio.h> + +#include <gdbusutils.h> + +#include "gproxydrive.h" +#include "gproxyvolume.h" +#include "gproxymount.h" + +static void signal_emit_in_idle (gpointer object, const char *signal_name, gpointer other_object); + +/* Protects all fields of GProxyVolume that can change */ +G_LOCK_DEFINE_STATIC(proxy_volume); + +struct _GProxyVolume { + GObject parent; + + GProxyVolumeMonitor *volume_monitor; + + /* non-NULL only if activation_uri != NULL */ + GVolumeMonitor *union_monitor; + + char *id; + char *name; + char *uuid; + char *activation_uri; + GIcon *icon; + char *drive_id; + char *mount_id; + GHashTable *identifiers; + + gboolean can_mount; + gboolean should_automount; + + GProxyShadowMount *shadow_mount; + + GHashTable *hash_mount_op_id_to_data; +}; + +static void g_proxy_volume_volume_iface_init (GVolumeIface *iface); + +#define _G_IMPLEMENT_INTERFACE_DYNAMIC(TYPE_IFACE, iface_init) { \ + const GInterfaceInfo g_implement_interface_info = { \ + (GInterfaceInitFunc) iface_init, NULL, NULL \ + }; \ + g_type_module_add_interface (type_module, g_define_type_id, TYPE_IFACE, &g_implement_interface_info); \ +} + +G_DEFINE_DYNAMIC_TYPE_EXTENDED (GProxyVolume, g_proxy_volume, G_TYPE_OBJECT, 0, + _G_IMPLEMENT_INTERFACE_DYNAMIC (G_TYPE_VOLUME, + g_proxy_volume_volume_iface_init)) + + +static void union_monitor_mount_added (GVolumeMonitor *union_monitor, + GMount *mount, + GProxyVolume *volume); + +static void union_monitor_mount_removed (GVolumeMonitor *union_monitor, + GMount *mount, + GProxyVolume *volume); + +static void union_monitor_mount_changed (GVolumeMonitor *union_monitor, + GMount *mount, + GProxyVolume *volume); + +static void update_shadow_mount (GProxyVolume *volume); + +GProxyShadowMount * +g_proxy_volume_get_shadow_mount (GProxyVolume *volume) +{ + if (volume->shadow_mount != NULL) + return g_object_ref (volume->shadow_mount); + else + return NULL; +} + +static void +g_proxy_volume_finalize (GObject *object) +{ + GProxyVolume *volume; + + volume = G_PROXY_VOLUME (object); + + g_free (volume->id); + g_free (volume->name); + g_free (volume->uuid); + g_free (volume->activation_uri); + if (volume->icon != NULL) + g_object_unref (volume->icon); + g_free (volume->drive_id); + g_free (volume->mount_id); + if (volume->identifiers != NULL) + g_hash_table_unref (volume->identifiers); + + if (volume->shadow_mount != NULL) + { + signal_emit_in_idle (volume->shadow_mount, "unmounted", NULL); + signal_emit_in_idle (volume->volume_monitor, "mount-removed", volume->shadow_mount); + g_proxy_shadow_mount_remove (volume->shadow_mount); + g_object_unref (volume->shadow_mount); + } + + if (volume->union_monitor != NULL) + { + g_signal_handlers_disconnect_by_func (volume->union_monitor, union_monitor_mount_added, volume); + g_signal_handlers_disconnect_by_func (volume->union_monitor, union_monitor_mount_removed, volume); + g_signal_handlers_disconnect_by_func (volume->union_monitor, union_monitor_mount_changed, volume); + g_object_unref (volume->union_monitor); + } + + if (volume->volume_monitor != NULL) + { + 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); +} + +static void +g_proxy_volume_class_init (GProxyVolumeClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = g_proxy_volume_finalize; +} + +static void +g_proxy_volume_class_finalize (GProxyVolumeClass *klass) +{ +} + +static void +g_proxy_volume_init (GProxyVolume *proxy_volume) +{ + proxy_volume->hash_mount_op_id_to_data = g_hash_table_new (g_str_hash, g_str_equal); +} + +GProxyVolume * +g_proxy_volume_new (GProxyVolumeMonitor *volume_monitor) +{ + GProxyVolume *volume; + volume = g_object_new (G_TYPE_PROXY_VOLUME, NULL); + volume->volume_monitor = g_object_ref (volume_monitor); + g_object_set_data (G_OBJECT (volume), + "g-proxy-volume-volume-monitor-name", + (gpointer) g_type_name (G_TYPE_FROM_INSTANCE (volume_monitor))); + return volume; +} + + +static void +union_monitor_mount_added (GVolumeMonitor *union_monitor, + GMount *mount, + GProxyVolume *volume) +{ + update_shadow_mount (volume); +} + +static void +union_monitor_mount_removed (GVolumeMonitor *union_monitor, + GMount *mount, + GProxyVolume *volume) +{ + update_shadow_mount (volume); +} + +static void +union_monitor_mount_changed (GVolumeMonitor *union_monitor, + GMount *mount, + GProxyVolume *volume) +{ + if (volume->shadow_mount != NULL) + { + GMount *real_mount; + real_mount = g_proxy_shadow_mount_get_real_mount (volume->shadow_mount); + if (mount == real_mount) + { + signal_emit_in_idle (volume->shadow_mount, "changed", NULL); + signal_emit_in_idle (volume->volume_monitor, "mount-changed", volume->shadow_mount); + } + g_object_unref (real_mount); + } +} + +static void +update_shadow_mount (GProxyVolume *volume) +{ + GFile *activation_root; + GList *mounts; + GList *l; + GMount *mount_to_shadow; + + activation_root = NULL; + mount_to_shadow = NULL; + + if (volume->activation_uri == NULL) + goto out; + + activation_root = g_file_new_for_uri (volume->activation_uri); + + if (volume->union_monitor == NULL) + { + volume->union_monitor = g_volume_monitor_get (); + g_signal_connect (volume->union_monitor, "mount-added", (GCallback) union_monitor_mount_added, volume); + g_signal_connect (volume->union_monitor, "mount-removed", (GCallback) union_monitor_mount_removed, volume); + g_signal_connect (volume->union_monitor, "mount-changed", (GCallback) union_monitor_mount_changed, volume); + } + + mounts = g_volume_monitor_get_mounts (volume->union_monitor); + for (l = mounts; l != NULL; l = l->next) + { + GMount *mount = G_MOUNT (l->data); + GFile *mount_root; + + /* don't consider our (possibly) existing shadow mount */ + if (G_IS_PROXY_SHADOW_MOUNT (mount)) + continue; + + mount_root = g_mount_get_root (mount); + if (g_file_has_prefix (activation_root, mount_root)) + { + mount_to_shadow = g_object_ref (mount); + break; + } + } + g_list_foreach (mounts, (GFunc) g_object_unref, NULL); + g_list_free (mounts); + + if (mount_to_shadow != NULL) + { + /* there's now a mount to shadow, if we don't have a GProxyShadowMount then create one */ + if (volume->shadow_mount == NULL) + { + volume->shadow_mount = g_proxy_shadow_mount_new (volume->volume_monitor, + volume, + mount_to_shadow); + signal_emit_in_idle (volume->volume_monitor, "mount-added", volume->shadow_mount); + } + else + { + GFile *current_activation_root; + + /* we have a GProxyShadowMount already. However, we need to replace it if the + * activation root has changed. + */ + current_activation_root = g_proxy_shadow_mount_get_activation_root (volume->shadow_mount); + if (!g_file_equal (current_activation_root, activation_root)) + { + signal_emit_in_idle (volume->shadow_mount, "unmounted", NULL); + signal_emit_in_idle (volume->volume_monitor, "mount-removed", volume->shadow_mount); + g_proxy_shadow_mount_remove (volume->shadow_mount); + g_object_unref (volume->shadow_mount); + volume->shadow_mount = NULL; + + volume->shadow_mount = g_proxy_shadow_mount_new (volume->volume_monitor, + volume, + mount_to_shadow); + signal_emit_in_idle (volume->volume_monitor, "mount-added", volume->shadow_mount); + } + g_object_unref (current_activation_root); + } + } + else + { + /* no mount to shadow; if we have a GProxyShadowMount then remove it */ + if (volume->shadow_mount != NULL) + { + signal_emit_in_idle (volume->shadow_mount, "unmounted", NULL); + signal_emit_in_idle (volume->volume_monitor, "mount-removed", volume->shadow_mount); + g_proxy_shadow_mount_remove (volume->shadow_mount); + g_object_unref (volume->shadow_mount); + volume->shadow_mount = NULL; + } + } + + out: + + if (activation_root != NULL) + g_object_unref (activation_root); + + if (mount_to_shadow != NULL) + g_object_unref (mount_to_shadow); +} + +static gboolean +update_shadow_mount_in_idle_do (GProxyVolume *volume) +{ + update_shadow_mount (volume); + g_object_unref (volume); + return FALSE; +} + +static void +update_shadow_mount_in_idle (GProxyVolume *volume) +{ + g_idle_add ((GSourceFunc) update_shadow_mount_in_idle_do, g_object_ref (volume)); +} + +/* string id + * string name + * string gicon_data + * string uuid + * string activation_uri + * boolean can-mount + * boolean should-automount + * string drive-id + * string mount-id + * dict:string->string identifiers + */ + +void g_proxy_volume_update (GProxyVolume *volume, + DBusMessageIter *iter) +{ + DBusMessageIter iter_struct; + const char *id; + const char *name; + const char *gicon_data; + const char *uuid; + const char *activation_uri; + const char *drive_id; + const char *mount_id; + dbus_bool_t can_mount; + dbus_bool_t should_automount; + GHashTable *identifiers; + + dbus_message_iter_recurse (iter, &iter_struct); + dbus_message_iter_get_basic (&iter_struct, &id); + dbus_message_iter_next (&iter_struct); + dbus_message_iter_get_basic (&iter_struct, &name); + dbus_message_iter_next (&iter_struct); + dbus_message_iter_get_basic (&iter_struct, &gicon_data); + dbus_message_iter_next (&iter_struct); + dbus_message_iter_get_basic (&iter_struct, &uuid); + dbus_message_iter_next (&iter_struct); + dbus_message_iter_get_basic (&iter_struct, &activation_uri); + dbus_message_iter_next (&iter_struct); + dbus_message_iter_get_basic (&iter_struct, &can_mount); + dbus_message_iter_next (&iter_struct); + dbus_message_iter_get_basic (&iter_struct, &should_automount); + dbus_message_iter_next (&iter_struct); + dbus_message_iter_get_basic (&iter_struct, &drive_id); + dbus_message_iter_next (&iter_struct); + dbus_message_iter_get_basic (&iter_struct, &mount_id); + dbus_message_iter_next (&iter_struct); + + identifiers = _get_identifiers (&iter_struct); + dbus_message_iter_next (&iter_struct); + + if (volume->id != NULL && strcmp (volume->id, id) != 0) + { + g_warning ("id mismatch during update of volume"); + goto out; + } + + if (strlen (name) == 0) + name = NULL; + if (strlen (uuid) == 0) + uuid = NULL; + if (strlen (activation_uri) == 0) + activation_uri = NULL; + + /* out with the old */ + g_free (volume->id); + g_free (volume->name); + g_free (volume->uuid); + g_free (volume->activation_uri); + if (volume->icon != NULL) + g_object_unref (volume->icon); + g_free (volume->drive_id); + g_free (volume->mount_id); + if (volume->identifiers != NULL) + g_hash_table_unref (volume->identifiers); + + /* in with the new */ + volume->id = g_strdup (id); + volume->name = g_strdup (name); + volume->uuid = g_strdup (uuid); + volume->activation_uri = g_strdup (activation_uri); + if (*gicon_data == 0) + volume->icon = NULL; + else + volume->icon = g_icon_new_for_string (gicon_data, NULL); + volume->drive_id = g_strdup (drive_id); + volume->mount_id = g_strdup (mount_id); + volume->can_mount = can_mount; + volume->should_automount = should_automount; + volume->identifiers = identifiers != NULL ? g_hash_table_ref (identifiers) : NULL; + + /* this calls into the union monitor; do it in idle to avoid locking issues */ + update_shadow_mount_in_idle (volume); + + out: + g_hash_table_unref (identifiers); +} + +const char * +g_proxy_volume_get_id (GProxyVolume *volume) +{ + return volume->id; +} + +static GIcon * +g_proxy_volume_get_icon (GVolume *volume) +{ + GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume); + GIcon *icon; + + G_LOCK (proxy_volume); + icon = proxy_volume->icon != NULL ? g_object_ref (proxy_volume->icon) : NULL; + G_UNLOCK (proxy_volume); + return icon; +} + +static char * +g_proxy_volume_get_name (GVolume *volume) +{ + GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume); + char *name; + + G_LOCK (proxy_volume); + name = g_strdup (proxy_volume->name); + G_UNLOCK (proxy_volume); + return name; +} + +static char * +g_proxy_volume_get_uuid (GVolume *volume) +{ + GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume); + char *uuid; + + G_LOCK (proxy_volume); + uuid = g_strdup (proxy_volume->uuid); + G_UNLOCK (proxy_volume); + return uuid; +} + +static gboolean +g_proxy_volume_can_mount (GVolume *volume) +{ + GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume); + gboolean res; + + G_LOCK (proxy_volume); + res = proxy_volume->can_mount; + G_UNLOCK (proxy_volume); + return res; +} + +static gboolean +g_proxy_volume_can_eject (GVolume *volume) +{ + GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume); + GProxyDrive *drive; + gboolean res; + + G_LOCK (proxy_volume); + res = FALSE; + if (proxy_volume->drive_id != NULL && strlen (proxy_volume->drive_id) > 0) + { + drive = g_proxy_volume_monitor_get_drive_for_id (proxy_volume->volume_monitor, + proxy_volume->drive_id); + if (drive != NULL) + { + res = g_drive_can_eject (G_DRIVE (drive)); + g_object_unref (drive); + } + } + G_UNLOCK (proxy_volume); + + return res; +} + +static gboolean +g_proxy_volume_should_automount (GVolume *volume) +{ + GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume); + gboolean res; + + G_LOCK (proxy_volume); + res = proxy_volume->should_automount; + G_UNLOCK (proxy_volume); + + return res; +} + +static GDrive * +g_proxy_volume_get_drive (GVolume *volume) +{ + GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume); + GProxyDrive *drive; + + G_LOCK (proxy_volume); + drive = NULL; + if (proxy_volume->drive_id != NULL && strlen (proxy_volume->drive_id) > 0) + drive = g_proxy_volume_monitor_get_drive_for_id (proxy_volume->volume_monitor, + proxy_volume->drive_id); + G_UNLOCK (proxy_volume); + + return drive != NULL ? G_DRIVE (drive) : NULL; +} + +static GMount * +g_proxy_volume_get_mount (GVolume *volume) +{ + GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume); + GMount *mount; + + mount = NULL; + + G_LOCK (proxy_volume); + + if (proxy_volume->shadow_mount != NULL) + { + mount = g_object_ref (proxy_volume->shadow_mount); + } + else if (proxy_volume->mount_id != NULL && strlen (proxy_volume->mount_id) > 0) + { + GProxyMount *proxy_mount; + proxy_mount = g_proxy_volume_monitor_get_mount_for_id (proxy_volume->volume_monitor, + proxy_volume->mount_id); + if (proxy_mount != NULL) + mount = G_MOUNT (proxy_mount); + } + G_UNLOCK (proxy_volume); + + return mount; +} + +typedef struct { + GObject *object; + GAsyncReadyCallback callback; + gpointer user_data; +} EjectWrapperOp; + +static void +eject_wrapper_callback (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + EjectWrapperOp *data = user_data; + data->callback (data->object, res, data->user_data); + g_object_unref (data->object); + g_free (data); +} + +static void +g_proxy_volume_eject (GVolume *volume, + GMountUnmountFlags flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume); + GProxyDrive *drive; + + drive = NULL; + G_LOCK (proxy_volume); + if (proxy_volume->drive_id != NULL && strlen (proxy_volume->drive_id) > 0) + { + drive = g_proxy_volume_monitor_get_drive_for_id (proxy_volume->volume_monitor, + proxy_volume->drive_id); + } + G_UNLOCK (proxy_volume); + + if (drive != NULL) + { + EjectWrapperOp *data; + data = g_new0 (EjectWrapperOp, 1); + data->object = g_object_ref (volume); + data->callback = callback; + data->user_data = user_data; + g_drive_eject (G_DRIVE (drive), flags, cancellable, eject_wrapper_callback, data); + g_object_unref (drive); + } +} + +static gboolean +g_proxy_volume_eject_finish (GVolume *volume, + GAsyncResult *result, + GError **error) +{ + GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume); + GProxyDrive *drive; + gboolean res; + + G_LOCK (proxy_volume); + res = TRUE; + drive = NULL; + if (proxy_volume->drive_id != NULL && strlen (proxy_volume->drive_id) > 0) + drive = g_proxy_volume_monitor_get_drive_for_id (proxy_volume->volume_monitor, + proxy_volume->drive_id); + G_UNLOCK (proxy_volume); + + if (drive != NULL) + { + res = g_drive_eject_finish (G_DRIVE (drive), result, error); + g_object_unref (drive); + } + + return res; +} + +static char * +g_proxy_volume_get_identifier (GVolume *volume, + const char *kind) +{ + GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume); + char *res; + + G_LOCK (proxy_volume); + if (proxy_volume->identifiers != NULL) + res = g_strdup (g_hash_table_lookup (proxy_volume->identifiers, kind)); + else + res = NULL; + G_UNLOCK (proxy_volume); + + return res; +} + +static void +add_identifier_key (const char *key, const char *value, GPtrArray *res) +{ + g_ptr_array_add (res, g_strdup (key)); +} + +static char ** +g_proxy_volume_enumerate_identifiers (GVolume *volume) +{ + GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume); + GPtrArray *res; + + res = g_ptr_array_new (); + + G_LOCK (proxy_volume); + if (proxy_volume->identifiers != NULL) + g_hash_table_foreach (proxy_volume->identifiers, (GHFunc) add_identifier_key, res); + G_UNLOCK (proxy_volume); + + /* Null-terminate */ + g_ptr_array_add (res, NULL); + + return (char **) g_ptr_array_free (res, FALSE); +} + +typedef struct { + 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) +{ + 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_free (data); +} + +typedef struct +{ + GProxyVolume *enclosing_volume; + GAsyncReadyCallback callback; + gpointer user_data; +} ForeignMountOp; + +static void +mount_foreign_callback (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + ForeignMountOp *data = user_data; + data->callback (G_OBJECT (data->enclosing_volume), res, data->user_data); + g_object_unref (data->enclosing_volume); + g_free (data); +} + +static void +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, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume); + + G_LOCK (proxy_volume); + if (proxy_volume->activation_uri != NULL) + { + ForeignMountOp *data; + GFile *root; + + data = g_new0 (ForeignMountOp, 1); + data->enclosing_volume = g_object_ref (volume); + data->callback = callback; + data->user_data = user_data; + + root = g_file_new_for_uri (proxy_volume->activation_uri); + + G_UNLOCK (proxy_volume); + + g_file_mount_enclosing_volume (root, + flags, + mount_operation, + cancellable, + mount_foreign_callback, + data); + + g_object_unref (root); + } + else + { + DBusOp *data; + DBusConnection *connection; + const char *name; + DBusMessage *message; + dbus_uint32_t _flags = flags; + + 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->volume = g_object_ref (volume); + data->callback = callback; + data->user_data = user_data; + 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_STRING, + &(data->mount_op_id), + DBUS_TYPE_INVALID); + G_UNLOCK (proxy_volume); + + _g_dbus_connection_call_async (connection, + message, + 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 +g_proxy_volume_mount_finish (GVolume *volume, + GAsyncResult *result, + GError **error) +{ + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) + return FALSE; + return TRUE; +} + +static GFile * +g_proxy_volume_get_activation_root (GVolume *volume) +{ + GProxyVolume *proxy_volume = G_PROXY_VOLUME (volume); + if (proxy_volume->activation_uri == NULL) + return NULL; + else + return g_file_new_for_uri (proxy_volume->activation_uri); +} + +static void +g_proxy_volume_volume_iface_init (GVolumeIface *iface) +{ + iface->get_name = g_proxy_volume_get_name; + iface->get_icon = g_proxy_volume_get_icon; + iface->get_uuid = g_proxy_volume_get_uuid; + iface->get_drive = g_proxy_volume_get_drive; + iface->get_mount = g_proxy_volume_get_mount; + iface->can_mount = g_proxy_volume_can_mount; + iface->can_eject = g_proxy_volume_can_eject; + iface->should_automount = g_proxy_volume_should_automount; + iface->mount_fn = g_proxy_volume_mount; + iface->mount_finish = g_proxy_volume_mount_finish; + iface->eject = g_proxy_volume_eject; + iface->eject_finish = g_proxy_volume_eject_finish; + iface->get_identifier = g_proxy_volume_get_identifier; + iface->enumerate_identifiers = g_proxy_volume_enumerate_identifiers; + iface->get_activation_root = g_proxy_volume_get_activation_root; +} + +void +g_proxy_volume_register (GIOModule *module) +{ + g_proxy_volume_register_type (G_TYPE_MODULE (module)); +} + +typedef struct { + const char *signal_name; + GObject *object; + GObject *other_object; +} SignalEmitIdleData; + +static gboolean +signal_emit_in_idle_do (SignalEmitIdleData *data) +{ + if (data->other_object != NULL) + { + g_signal_emit_by_name (data->object, data->signal_name, data->other_object); + g_object_unref (data->other_object); + } + else + { + g_signal_emit_by_name (data->object, data->signal_name); + } + g_object_unref (data->object); + g_free (data); + + return FALSE; +} + +static void +signal_emit_in_idle (gpointer object, const char *signal_name, gpointer other_object) +{ + SignalEmitIdleData *data; + + data = g_new0 (SignalEmitIdleData, 1); + data->signal_name = signal_name; + data->object = g_object_ref (G_OBJECT (object)); + data->other_object = other_object != NULL ? g_object_ref (G_OBJECT (other_object)) : NULL; + g_idle_add ((GSourceFunc) signal_emit_in_idle_do, data); +} |