diff options
Diffstat (limited to 'trunk/monitor/proxy/gproxydrive.c')
-rw-r--r-- | trunk/monitor/proxy/gproxydrive.c | 731 |
1 files changed, 731 insertions, 0 deletions
diff --git a/trunk/monitor/proxy/gproxydrive.c b/trunk/monitor/proxy/gproxydrive.c new file mode 100644 index 00000000..79459935 --- /dev/null +++ b/trunk/monitor/proxy/gproxydrive.c @@ -0,0 +1,731 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* gvfs - extensions for gio + * + * Copyright (C) 2006-2008 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: David Zeuthen <davidz@redhat.com> + */ + +#include <config.h> + +#include <string.h> +#include <sys/wait.h> +#include <unistd.h> + +#include <glib.h> +#include <glib/gi18n-lib.h> + +#include <gdbusutils.h> + +#include "gproxyvolumemonitor.h" +#include "gproxydrive.h" +#include "gproxyvolume.h" + +/* Protects all fields of GProxyDrive that can change */ +G_LOCK_DEFINE_STATIC(proxy_drive); + +struct _GProxyDrive { + GObject parent; + + GProxyVolumeMonitor *volume_monitor; + + char *id; + char *name; + GIcon *icon; + char **volume_ids; + gboolean can_eject; + gboolean can_poll_for_media; + gboolean is_media_check_automatic; + gboolean has_media; + gboolean is_media_removable; + + GHashTable *identifiers; +}; + +static void g_proxy_drive_drive_iface_init (GDriveIface *iface); + +#define _G_IMPLEMENT_INTERFACE_DYNAMIC(TYPE_IFACE, iface_init) { \ + const GInterfaceInfo g_implement_interface_info = { \ + (GInterfaceInitFunc) iface_init, NULL, NULL \ + }; \ + g_type_module_add_interface (type_module, g_define_type_id, TYPE_IFACE, &g_implement_interface_info); \ +} + +G_DEFINE_DYNAMIC_TYPE_EXTENDED (GProxyDrive, g_proxy_drive, G_TYPE_OBJECT, 0, + _G_IMPLEMENT_INTERFACE_DYNAMIC (G_TYPE_DRIVE, + g_proxy_drive_drive_iface_init)) + +static void +g_proxy_drive_finalize (GObject *object) +{ + GProxyDrive *drive; + + drive = G_PROXY_DRIVE (object); + + if (drive->volume_monitor != NULL) + g_object_unref (drive->volume_monitor); + g_free (drive->id); + g_free (drive->name); + if (drive->icon != NULL) + g_object_unref (drive->icon); + g_strfreev (drive->volume_ids); + if (drive->identifiers != NULL) + g_hash_table_unref (drive->identifiers); + + if (G_OBJECT_CLASS (g_proxy_drive_parent_class)->finalize) + (*G_OBJECT_CLASS (g_proxy_drive_parent_class)->finalize) (object); +} + +static void +g_proxy_drive_class_init (GProxyDriveClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = g_proxy_drive_finalize; +} + +static void +g_proxy_drive_class_finalize (GProxyDriveClass *klass) +{ +} + +static void +g_proxy_drive_init (GProxyDrive *proxy_drive) +{ +} + +GProxyDrive * +g_proxy_drive_new (GProxyVolumeMonitor *volume_monitor) +{ + GProxyDrive *drive; + drive = g_object_new (G_TYPE_PROXY_DRIVE, NULL); + drive->volume_monitor = g_object_ref (volume_monitor); + g_object_set_data (G_OBJECT (drive), + "g-proxy-drive-volume-monitor-name", + (gpointer) g_type_name (G_TYPE_FROM_INSTANCE (volume_monitor))); + return drive; +} + +/* string id + * string name + * string gicon_data + * boolean can-eject + * boolean can-poll-for-media + * boolean has-media + * boolean is-media-removable + * boolean is-media-check-automatic + * array:string volume-ids + * dict:string->string identifiers + */ +#define DRIVE_STRUCT_TYPE "(sssbbbbasa{ss})" + +void +g_proxy_drive_update (GProxyDrive *drive, + DBusMessageIter *iter) +{ + DBusMessageIter iter_struct; + DBusMessageIter iter_volume_ids_iter; + const char *id; + const char *name; + const char *gicon_data; + dbus_bool_t can_eject; + dbus_bool_t can_poll_for_media; + dbus_bool_t has_media; + dbus_bool_t is_media_removable; + dbus_bool_t is_media_check_automatic; + GPtrArray *volume_ids; + GHashTable *identifiers; + + dbus_message_iter_recurse (iter, &iter_struct); + dbus_message_iter_get_basic (&iter_struct, &id); + dbus_message_iter_next (&iter_struct); + dbus_message_iter_get_basic (&iter_struct, &name); + dbus_message_iter_next (&iter_struct); + dbus_message_iter_get_basic (&iter_struct, &gicon_data); + dbus_message_iter_next (&iter_struct); + dbus_message_iter_get_basic (&iter_struct, &can_eject); + dbus_message_iter_next (&iter_struct); + dbus_message_iter_get_basic (&iter_struct, &can_poll_for_media); + dbus_message_iter_next (&iter_struct); + dbus_message_iter_get_basic (&iter_struct, &has_media); + dbus_message_iter_next (&iter_struct); + dbus_message_iter_get_basic (&iter_struct, &is_media_removable); + dbus_message_iter_next (&iter_struct); + 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); + while (dbus_message_iter_get_arg_type (&iter_volume_ids_iter) != DBUS_TYPE_INVALID) + { + const char *volume_id; + dbus_message_iter_get_basic (&iter_volume_ids_iter, &volume_id); + dbus_message_iter_next (&iter_volume_ids_iter); + g_ptr_array_add (volume_ids, (gpointer) volume_id); + } + g_ptr_array_add (volume_ids, NULL); + dbus_message_iter_next (&iter_struct); + + identifiers = _get_identifiers (&iter_struct); + dbus_message_iter_next (&iter_struct); + + if (drive->id != NULL && strcmp (drive->id, id) != 0) + { + g_warning ("id mismatch during update of drive"); + goto out; + } + + if (strlen (name) == 0) + name = NULL; + + /* out with the old */ + g_free (drive->id); + g_free (drive->name); + if (drive->icon != NULL) + g_object_unref (drive->icon); + g_strfreev (drive->volume_ids); + if (drive->identifiers != NULL) + g_hash_table_unref (drive->identifiers); + + /* in with the new */ + drive->id = g_strdup (id); + drive->name = g_strdup (name); + if (*gicon_data == 0) + drive->icon = NULL; + else + drive->icon = g_icon_new_for_string (gicon_data, NULL); + + drive->can_eject = can_eject; + drive->can_poll_for_media = can_poll_for_media; + drive->has_media = has_media; + drive->is_media_removable = is_media_removable; + drive->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); + + out: + g_ptr_array_free (volume_ids, TRUE); + g_hash_table_unref (identifiers); +} + +static GIcon * +g_proxy_drive_get_icon (GDrive *drive) +{ + GProxyDrive *proxy_drive = G_PROXY_DRIVE (drive); + GIcon *icon; + + G_LOCK (proxy_drive); + icon = proxy_drive->icon != NULL ? g_object_ref (proxy_drive->icon) : NULL; + G_UNLOCK (proxy_drive); + + return icon; +} + +static char * +g_proxy_drive_get_name (GDrive *drive) +{ + GProxyDrive *proxy_drive = G_PROXY_DRIVE (drive); + char *name; + + G_LOCK (proxy_drive); + name = g_strdup (proxy_drive->name); + G_UNLOCK (proxy_drive); + + return name; +} + +static GList * +g_proxy_drive_get_volumes (GDrive *drive) +{ + GProxyDrive *proxy_drive = G_PROXY_DRIVE (drive); + GList *l; + + l = NULL; + + G_LOCK (proxy_drive); + if (proxy_drive->volume_monitor != NULL && proxy_drive->volume_ids != NULL) + { + int n; + + for (n = 0; proxy_drive->volume_ids[n] != NULL; n++) + { + GProxyVolume *volume; + volume = g_proxy_volume_monitor_get_volume_for_id (proxy_drive->volume_monitor, proxy_drive->volume_ids[n]); + if (volume != NULL) + l = g_list_append (l, volume); + } + } + G_UNLOCK (proxy_drive); + + return l; +} + +static gboolean +g_proxy_drive_has_volumes (GDrive *drive) +{ + GProxyDrive *proxy_drive = G_PROXY_DRIVE (drive); + gboolean res; + + G_LOCK (proxy_drive); + res = (proxy_drive->volume_ids != NULL && g_strv_length (proxy_drive->volume_ids) > 0); + G_UNLOCK (proxy_drive); + + return res; +} + +static gboolean +g_proxy_drive_is_media_removable (GDrive *drive) +{ + GProxyDrive *proxy_drive = G_PROXY_DRIVE (drive); + gboolean res; + + G_LOCK (proxy_drive); + res = proxy_drive->is_media_removable; + G_UNLOCK (proxy_drive); + + return res; +} + +static gboolean +g_proxy_drive_has_media (GDrive *drive) +{ + GProxyDrive *proxy_drive = G_PROXY_DRIVE (drive); + gboolean res; + + G_LOCK (proxy_drive); + res = proxy_drive->has_media; + G_UNLOCK (proxy_drive); + + return res; +} + +static gboolean +g_proxy_drive_is_media_check_automatic (GDrive *drive) +{ + GProxyDrive *proxy_drive = G_PROXY_DRIVE (drive); + gboolean res; + + G_LOCK (proxy_drive); + res = proxy_drive->is_media_check_automatic; + G_UNLOCK (proxy_drive); + + return res; +} + +static gboolean +g_proxy_drive_can_eject (GDrive *drive) +{ + GProxyDrive *proxy_drive = G_PROXY_DRIVE (drive); + gboolean res; + + G_LOCK (proxy_drive); + res = proxy_drive->can_eject; + G_UNLOCK (proxy_drive); + + return res; +} + +static gboolean +g_proxy_drive_can_poll_for_media (GDrive *drive) +{ + GProxyDrive *proxy_drive = G_PROXY_DRIVE (drive); + gboolean res; + + G_LOCK (proxy_drive); + res = proxy_drive->can_poll_for_media; + G_UNLOCK (proxy_drive); + + return res; +} + +static char * +g_proxy_drive_get_identifier (GDrive *drive, + const char *kind) +{ + GProxyDrive *proxy_drive = G_PROXY_DRIVE (drive); + char *res; + + G_LOCK (proxy_drive); + if (proxy_drive->identifiers != NULL) + res = g_strdup (g_hash_table_lookup (proxy_drive->identifiers, kind)); + else + res = NULL; + G_UNLOCK (proxy_drive); + + return res; +} + +static void +add_identifier_key (const char *key, const char *value, GPtrArray *res) +{ + g_ptr_array_add (res, g_strdup (key)); +} + +static char ** +g_proxy_drive_enumerate_identifiers (GDrive *drive) +{ + GProxyDrive *proxy_drive = G_PROXY_DRIVE (drive); + GPtrArray *res; + + res = g_ptr_array_new (); + + G_LOCK (proxy_drive); + if (proxy_drive->identifiers != NULL) + g_hash_table_foreach (proxy_drive->identifiers, (GHFunc) add_identifier_key, res); + G_UNLOCK (proxy_drive); + + /* Null-terminate */ + g_ptr_array_add (res, NULL); + + return (char **) g_ptr_array_free (res, FALSE); +} + +const char * +g_proxy_drive_get_id (GProxyDrive *drive) +{ + return drive->id; +} + +typedef struct { + 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) +{ + 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->drive); + g_free (data->cancellation_id); + if (data->cancellable != NULL) + g_object_unref (data->cancellable); + g_free (data); +} + +static void +g_proxy_drive_eject (GDrive *drive, + GMountUnmountFlags flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GProxyDrive *proxy_drive = G_PROXY_DRIVE (drive); + DBusConnection *connection; + const char *name; + DBusMessage *message; + DBusOp *data; + dbus_uint32_t _flags = flags; + + G_LOCK (proxy_drive); + + 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->drive = g_object_ref (drive); + 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 (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); + G_UNLOCK (proxy_drive); + + _g_dbus_connection_call_async (connection, + message, + -1, + (GAsyncDBusCallback) eject_cb, + data); + dbus_connection_unref (connection); + dbus_message_unref (message); + out: + ; +} + +static gboolean +g_proxy_drive_eject_finish (GDrive *drive, + GAsyncResult *result, + GError **error) +{ + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) + return FALSE; + return TRUE; +} + +static void +poll_for_media_cb (DBusMessage *reply, + GError *error, + DBusOp *data) +{ + 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->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); +} + +static void +g_proxy_drive_poll_for_media (GDrive *drive, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GProxyDrive *proxy_drive = G_PROXY_DRIVE (drive); + DBusConnection *connection; + const char *name; + DBusMessage *message; + DBusOp *data; + + G_LOCK (proxy_drive); + + 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->drive = g_object_ref (drive); + 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 (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); + + _g_dbus_connection_call_async (connection, + message, + -1, + (GAsyncDBusCallback) poll_for_media_cb, + data); + dbus_connection_unref (connection); + dbus_message_unref (message); + out: + ; +} + +static gboolean +g_proxy_drive_poll_for_media_finish (GDrive *drive, + GAsyncResult *result, + GError **error) +{ + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) + return FALSE; + return TRUE; +} + + +static void +g_proxy_drive_drive_iface_init (GDriveIface *iface) +{ + iface->get_name = g_proxy_drive_get_name; + iface->get_icon = g_proxy_drive_get_icon; + iface->has_volumes = g_proxy_drive_has_volumes; + iface->get_volumes = g_proxy_drive_get_volumes; + iface->is_media_removable = g_proxy_drive_is_media_removable; + iface->has_media = g_proxy_drive_has_media; + iface->is_media_check_automatic = g_proxy_drive_is_media_check_automatic; + iface->can_eject = g_proxy_drive_can_eject; + iface->can_poll_for_media = g_proxy_drive_can_poll_for_media; + iface->eject = g_proxy_drive_eject; + iface->eject_finish = g_proxy_drive_eject_finish; + iface->poll_for_media = g_proxy_drive_poll_for_media; + iface->poll_for_media_finish = g_proxy_drive_poll_for_media_finish; + iface->get_identifier = g_proxy_drive_get_identifier; + iface->enumerate_identifiers = g_proxy_drive_enumerate_identifiers; +} + +void +g_proxy_drive_register (GIOModule *module) +{ + g_proxy_drive_register_type (G_TYPE_MODULE (module)); +} |