diff options
author | Debarshi Ray <debarshir@gnome.org> | 2012-10-23 21:46:33 +0200 |
---|---|---|
committer | Debarshi Ray <debarshir@gnome.org> | 2013-02-05 17:25:39 +0100 |
commit | 267bb33e0e3586f078a0d38ee2ccc5bd1ccab911 (patch) | |
tree | acbae31e7facbdfb3297d76bcc8392877c2fc661 /monitor | |
parent | 1cffe26596fbc35c0dd63fa3cd26702973e0f08a (diff) |
Add GVfsGoaVolumeMonitor
https://bugzilla.gnome.org/show_bug.cgi?id=686526
Diffstat (limited to 'monitor')
-rw-r--r-- | monitor/Makefile.am | 6 | ||||
-rw-r--r-- | monitor/goa/Makefile.am | 46 | ||||
-rw-r--r-- | monitor/goa/goa.monitor | 4 | ||||
-rw-r--r-- | monitor/goa/goavolume.c | 585 | ||||
-rw-r--r-- | monitor/goa/goavolume.h | 66 | ||||
-rw-r--r-- | monitor/goa/goavolumemonitor.c | 475 | ||||
-rw-r--r-- | monitor/goa/goavolumemonitor.h | 63 | ||||
-rw-r--r-- | monitor/goa/goavolumemonitordaemon.c | 49 | ||||
-rw-r--r-- | monitor/goa/org.gtk.Private.GoaVolumeMonitor.service.in | 3 |
9 files changed, 1296 insertions, 1 deletions
diff --git a/monitor/Makefile.am b/monitor/Makefile.am index 7a4e87fc..f7cf7a21 100644 --- a/monitor/Makefile.am +++ b/monitor/Makefile.am @@ -1,4 +1,4 @@ -DIST_SUBDIRS = proxy hal gdu gphoto2 afc udisks2 mtp +DIST_SUBDIRS = proxy hal gdu gphoto2 afc udisks2 mtp goa SUBDIRS = proxy if USE_HAL @@ -24,3 +24,7 @@ endif if USE_LIBMTP SUBDIRS += mtp endif + +if USE_GOA +SUBDIRS += goa +endif diff --git a/monitor/goa/Makefile.am b/monitor/goa/Makefile.am new file mode 100644 index 00000000..e8ef5647 --- /dev/null +++ b/monitor/goa/Makefile.am @@ -0,0 +1,46 @@ +NULL = + +gvfs_src_dir = $(top_srcdir)/@with_gvfs_source@ + +libexec_PROGRAMS = gvfs-goa-volume-monitor + +gvfs_goa_volume_monitor_SOURCES = \ + goavolume.c goavolume.h \ + goavolumemonitor.c goavolumemonitor.h \ + goavolumemonitordaemon.c \ + $(NULL) + +gvfs_goa_volume_monitor_CFLAGS = \ + -DG_LOG_DOMAIN=\"GVFS-GOA\" \ + -I$(top_srcdir)/common \ + -I$(top_srcdir)/monitor/proxy \ + $(GLIB_CFLAGS) \ + $(GOA_CFLAGS) \ + $(WARN_CFLAGS) \ + -DGIO_MODULE_DIR=\"$(GIO_MODULE_DIR)\" \ + -DGVFS_LOCALEDIR=\"$(localedir)\" \ + $(NULL) + +gvfs_goa_volume_monitor_LDADD = \ + $(GLIB_LIBS) \ + $(GOA_LIBS) \ + $(top_builddir)/common/libgvfscommon.la \ + $(top_builddir)/monitor/proxy/libgvfsproxyvolumemonitordaemon-noin.la \ + $(NULL) + +remote_volume_monitorsdir = $(datadir)/gvfs/remote-volume-monitors +remote_volume_monitors_DATA = goa.monitor + +servicedir = $(datadir)/dbus-1/services +service_in_files = org.gtk.Private.GoaVolumeMonitor.service.in +service_DATA = $(service_in_files:.service.in=.service) + +$(service_DATA): $(service_in_files) Makefile + $(AM_V_GEN) $(SED) -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@ + +clean-local: + rm -f *~ *.loT $(BUILT_SOURCES) $(service_DATA) + +DISTCLEANFILES = $(service_DATA) + +EXTRA_DIST = $(service_in_files) goa.monitor diff --git a/monitor/goa/goa.monitor b/monitor/goa/goa.monitor new file mode 100644 index 00000000..a763c6b1 --- /dev/null +++ b/monitor/goa/goa.monitor @@ -0,0 +1,4 @@ +[RemoteVolumeMonitor] +Name=GProxyVolumeMonitorGoa +DBusName=org.gtk.Private.GoaVolumeMonitor +IsNative=false diff --git a/monitor/goa/goavolume.c b/monitor/goa/goavolume.c new file mode 100644 index 00000000..41334a96 --- /dev/null +++ b/monitor/goa/goavolume.c @@ -0,0 +1,585 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* gvfs - extensions for gio + * + * Copyright (C) 2012, 2013 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * Author: Debarshi Ray <debarshir@gnome.org> + */ + +#include <config.h> +#include <string.h> +#include <glib.h> +#include <gio/gio.h> +#include <glib/gi18n.h> + +#include "goavolume.h" + +struct _GVfsGoaVolume +{ + GObject parent; + GFile *root; + GMount *mount; + GoaObject *object; + gchar *uuid; + gchar *icon; + gchar *symbolic_icon; + gulong account_attention_needed_id; +}; + +struct _GVfsGoaVolumeClass +{ + GObjectClass parent_class; +}; + +enum +{ + PROP_0, + PROP_ACCOUNT, + PROP_UUID +}; + +static void g_vfs_goa_volume_iface_init (GVolumeIface *iface); + +G_DEFINE_TYPE_EXTENDED (GVfsGoaVolume, g_vfs_goa_volume, G_TYPE_OBJECT, 0, + G_IMPLEMENT_INTERFACE (G_TYPE_VOLUME, + g_vfs_goa_volume_iface_init)) + +/* ---------------------------------------------------------------------------------------------------- */ + +typedef struct +{ + GAsyncReadyCallback callback; + GCancellable *cancellable; + GMountOperation *mount_operation; + GSimpleAsyncResult *simple; + gchar *passwd; + gpointer user_data; +} MountOp; + +static void +mount_op_free (MountOp *data) +{ + g_clear_object (&data->cancellable); + g_clear_object (&data->mount_operation); + g_object_unref (data->simple); + g_free (data->passwd); + g_slice_free (MountOp, data); +} + +static void +mount_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) +{ + MountOp *data = user_data; + + if (data->callback != NULL) + data->callback (source_object, res, data->user_data); + + mount_op_free (data); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +account_attention_needed_cb (GObject *_object, GParamSpec *pspec, gpointer user_data) +{ + GoaAccount *account = GOA_ACCOUNT (_object); + GVfsGoaVolume *self = user_data; + + if (goa_account_get_attention_needed (account)) + { + if (self->mount != NULL) + { + g_mount_unmount_with_operation (self->mount, G_MOUNT_UNMOUNT_NONE, NULL, NULL, NULL, NULL); + g_clear_object (&self->mount); + } + } + else + g_volume_mount (G_VOLUME (self), G_MOUNT_MOUNT_NONE, NULL, NULL, NULL, NULL); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +mount_operation_ask_password_cb (GMountOperation *op, + gchar *message, + gchar *default_user, + gchar *default_domain, + GAskPasswordFlags flags, + gpointer user_data) +{ + MountOp *data = user_data; + + g_mount_operation_set_password (data->mount_operation, data->passwd); + g_mount_operation_reply (data->mount_operation, G_MOUNT_OPERATION_HANDLED); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +find_enclosing_mount_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) +{ + GFile *root = G_FILE (source_object); + GSimpleAsyncResult *simple = user_data; + GVfsGoaVolume *self; + GError *error; + MountOp *data; + + self = G_VFS_GOA_VOLUME (g_async_result_get_source_object (G_ASYNC_RESULT (simple))); + data = g_async_result_get_user_data (G_ASYNC_RESULT (simple)); + + error = NULL; + g_clear_object (&self->mount); + self->mount = g_file_find_enclosing_mount_finish (root, res, &error); + if (self->mount == NULL) + g_simple_async_result_take_error (simple, error); + + g_simple_async_result_complete_in_idle (simple); +} + +static void +mount_enclosing_volume_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) +{ + GFile *root = G_FILE (source_object); + GSimpleAsyncResult *simple = user_data; + GVfsGoaVolume *self; + GError *error; + MountOp *data; + + self = G_VFS_GOA_VOLUME (g_async_result_get_source_object (G_ASYNC_RESULT (simple))); + data = g_async_result_get_user_data (G_ASYNC_RESULT (simple)); + + error = NULL; + if (!g_file_mount_enclosing_volume_finish (root, res, &error)) + { + if (error->code != G_IO_ERROR_ALREADY_MOUNTED) + { + g_simple_async_result_take_error (simple, error); + g_simple_async_result_complete_in_idle (simple); + return; + } + else + { + g_warning ("Already mounted %s: %s", g_file_get_uri (root), error->message); + g_error_free (error); + } + } + + g_file_find_enclosing_mount_async (root, G_PRIORITY_DEFAULT, NULL, find_enclosing_mount_cb, simple); +} + +static void +get_password_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) +{ + GoaPasswordBased *passwd_based = GOA_PASSWORD_BASED (source_object); + GSimpleAsyncResult *simple = user_data; + GVfsGoaVolume *self; + GError *error; + GoaAccount *account; + GoaFiles *files; + MountOp *data; + + self = G_VFS_GOA_VOLUME (g_async_result_get_source_object (G_ASYNC_RESULT (simple))); + data = g_async_result_get_user_data (G_ASYNC_RESULT (simple)); + + error = NULL; + if (!goa_password_based_call_get_password_finish (passwd_based, &data->passwd, res, &error)) + { + g_simple_async_result_take_error (simple, error); + g_simple_async_result_complete_in_idle (simple); + return; + } + + account = goa_object_peek_account (self->object); + files = goa_object_peek_files (self->object); + if (files == NULL) + { + g_simple_async_result_set_error (simple, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Failed to get org.gnome.OnlineAccounts.Files for %s"), + goa_account_get_id (account)); + g_simple_async_result_complete_in_idle (simple); + return; + } + + g_mount_operation_set_username (data->mount_operation, goa_account_get_identity (account)); + g_file_mount_enclosing_volume (self->root, + G_MOUNT_MOUNT_NONE, + data->mount_operation, + data->cancellable, + mount_enclosing_volume_cb, + simple); +} + +static void +ensure_credentials_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) +{ + GoaAccount *account = GOA_ACCOUNT (source_object); + GSimpleAsyncResult *simple = user_data; + GVfsGoaVolume *self; + GError *error; + GoaPasswordBased *passwd_based; + MountOp *data; + const gchar *id; + + self = G_VFS_GOA_VOLUME (g_async_result_get_source_object (G_ASYNC_RESULT (simple))); + data = g_async_result_get_user_data (G_ASYNC_RESULT (simple)); + + error = NULL; + if (!goa_account_call_ensure_credentials_finish (account, NULL, res, &error)) + { + if (error->domain == GOA_ERROR && error->code == GOA_ERROR_NOT_AUTHORIZED) + { + g_simple_async_result_set_error (simple, + G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Invalid credentials for %s"), + goa_account_get_presentation_identity (account)); + g_error_free (error); + } + else + g_simple_async_result_take_error (simple, error); + + g_simple_async_result_complete_in_idle (simple); + return; + } + + passwd_based = goa_object_peek_password_based (self->object); + if (passwd_based == NULL) + { + g_simple_async_result_set_error (simple, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + _("Unsupported authentication method for %s"), + goa_account_get_presentation_identity (account)); + g_simple_async_result_complete_in_idle (simple); + return; + } + + id = goa_account_get_id (account); + goa_password_based_call_get_password (passwd_based, id, data->cancellable, get_password_cb, simple); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +g_vfs_goa_volume_can_eject (GVolume *_self) +{ + return FALSE; +} + +static gboolean +g_vfs_goa_volume_can_mount (GVolume *_self) +{ + return TRUE; +} + +static char ** +g_vfs_goa_volume_enumerate_identifiers (GVolume *_self) +{ + GVfsGoaVolume *self = G_VFS_GOA_VOLUME (_self); + GPtrArray *res; + + res = g_ptr_array_new (); + g_ptr_array_add (res, g_strdup (G_VOLUME_IDENTIFIER_KIND_CLASS)); + g_ptr_array_add (res, g_strdup (G_VOLUME_IDENTIFIER_KIND_UUID)); + g_ptr_array_add (res, NULL); + + return (gchar **) g_ptr_array_free (res, FALSE); +} + +static GFile * +g_vfs_goa_volume_get_activation_root (GVolume *_self) +{ + /* Even though we know the activation root before mounting the + * volume we can not reveal it, because we do not want it to be + * handled as a GProxyVolume. + */ + return NULL; +} + +static GDrive * +g_vfs_goa_volume_get_drive (GVolume *_self) +{ + return NULL; +} + +static GIcon * +g_vfs_goa_volume_get_icon (GVolume *_self) +{ + GVfsGoaVolume *self = G_VFS_GOA_VOLUME (_self); + return g_themed_icon_new_with_default_fallbacks (self->icon); +} + +static char * +g_vfs_goa_volume_get_identifier (GVolume *_self, const gchar *kind) +{ + GVfsGoaVolume *self = G_VFS_GOA_VOLUME (_self); + + if (g_strcmp0 (kind, G_VOLUME_IDENTIFIER_KIND_CLASS) == 0) + return g_strdup ("network"); + else if (g_strcmp0 (kind, G_VOLUME_IDENTIFIER_KIND_UUID) == 0) + return g_strdup (self->uuid); + + return NULL; +} + +static GMount * +g_vfs_goa_volume_get_mount (GVolume *_self) +{ + GVfsGoaVolume *self = G_VFS_GOA_VOLUME (_self); + GMount *mount; + + mount = NULL; + + if (self->mount != NULL) + mount = g_object_ref (self->mount); + + return mount; +} + +static char * +g_vfs_goa_volume_get_name (GVolume *_self) +{ + GVfsGoaVolume *self = G_VFS_GOA_VOLUME (_self); + GoaAccount *account; + + account = goa_object_peek_account (self->object); + return goa_account_dup_presentation_identity (account); +} + +static GIcon * +g_vfs_goa_volume_get_symbolic_icon (GVolume *_self) +{ + GVfsGoaVolume *self = G_VFS_GOA_VOLUME (_self); + return g_themed_icon_new_with_default_fallbacks (self->symbolic_icon); +} + +static char * +g_vfs_goa_volume_get_uuid (GVolume *_self) +{ + GVfsGoaVolume *self = G_VFS_GOA_VOLUME (_self); + return g_strdup (self->uuid); +} + +static void +g_vfs_goa_volume_mount (GVolume *_self, + GMountMountFlags flags, + GMountOperation *mount_operation, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GVfsGoaVolume *self = G_VFS_GOA_VOLUME (_self); + MountOp *data; + GSimpleAsyncResult *simple; + GoaAccount *account; + + data = g_slice_new0 (MountOp); + simple = g_simple_async_result_new (G_OBJECT (self), mount_cb, data, g_vfs_goa_volume_mount); + + data->simple = simple; + data->callback = callback; + data->user_data = user_data; + + if (cancellable != NULL) + { + data->cancellable = g_object_ref (cancellable); + g_simple_async_result_set_check_cancellable (simple, cancellable); + } + + /* We ignore the GMountOperation handed to us by the proxy volume + * monitor because it is set up to emit MountOpAskPassword on + * ask-password. + */ + data->mount_operation = g_mount_operation_new (); + g_signal_connect (data->mount_operation, "ask-password", G_CALLBACK (mount_operation_ask_password_cb), data); + + account = goa_object_peek_account (self->object); + goa_account_call_ensure_credentials (account, data->cancellable, ensure_credentials_cb, simple); +} + +static gboolean +g_vfs_goa_volume_mount_finish (GVolume *_self, GAsyncResult *res, GError **error) +{ + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res); + + g_return_val_if_fail (g_simple_async_result_is_valid (res, G_OBJECT (_self), g_vfs_goa_volume_mount), FALSE); + + if (g_simple_async_result_propagate_error (simple, error)) + return FALSE; + + return TRUE; +} + +static void +g_vfs_goa_volume_removed (GVolume *volume) +{ + GVfsGoaVolume *self = G_VFS_GOA_VOLUME (volume); + + if (self->mount == NULL) + return; + + g_mount_unmount_with_operation (self->mount, G_MOUNT_UNMOUNT_NONE, NULL, NULL, NULL, NULL); + g_clear_object (&self->mount); +} + +static gboolean +g_vfs_goa_volume_should_automount (GVolume *volume) +{ + return FALSE; +} + +static void +g_vfs_goa_volume_constructed (GObject *_self) +{ + GVfsGoaVolume *self = G_VFS_GOA_VOLUME (_self); + GoaAccount *account; + + G_OBJECT_CLASS (g_vfs_goa_volume_parent_class)->constructed (_self); + + self->root = g_file_new_for_uri (self->uuid); + + account = goa_object_peek_account (self->object); + self->account_attention_needed_id = g_signal_connect (account, + "notify::attention-needed", + G_CALLBACK (account_attention_needed_cb), + self); +} + +static void +g_vfs_goa_volume_dispose (GObject *_self) +{ + GVfsGoaVolume *self = G_VFS_GOA_VOLUME (_self); + + if (self->account_attention_needed_id != 0) + { + GoaAccount *account; + + account = goa_object_peek_account (self->object); + g_signal_handler_disconnect (account, self->account_attention_needed_id); + self->account_attention_needed_id = 0; + } + + g_clear_object (&self->root); + g_clear_object (&self->mount); + g_clear_object (&self->object); + + G_OBJECT_CLASS (g_vfs_goa_volume_parent_class)->dispose (_self); +} + +static void +g_vfs_goa_volume_finalize (GObject *_self) +{ + GVfsGoaVolume *self = G_VFS_GOA_VOLUME (_self); + + g_free (self->uuid); + g_free (self->icon); + g_free (self->symbolic_icon); + + G_OBJECT_CLASS (g_vfs_goa_volume_parent_class)->finalize (_self); +} + +static void +g_vfs_goa_volume_set_property (GObject *_self, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + GVfsGoaVolume *self = G_VFS_GOA_VOLUME (_self); + + switch (prop_id) + { + case PROP_ACCOUNT: + self->object = g_value_dup_object (value); + break; + + case PROP_UUID: + self->uuid = g_value_dup_string (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (_self, prop_id, pspec); + break; + } +} + +static void +g_vfs_goa_volume_class_init (GVfsGoaVolumeClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->constructed = g_vfs_goa_volume_constructed; + gobject_class->dispose = g_vfs_goa_volume_dispose; + gobject_class->finalize = g_vfs_goa_volume_finalize; + gobject_class->set_property = g_vfs_goa_volume_set_property; + + g_object_class_install_property (gobject_class, + PROP_ACCOUNT, + g_param_spec_object ("account", + "GoaObject object", + "The GOA account represented by the volume", + GOA_TYPE_OBJECT, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS | + G_PARAM_WRITABLE)); + + g_object_class_install_property (gobject_class, + PROP_UUID, + g_param_spec_string ("uuid", + "UUID", + "The UUID of the volume", + NULL, + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS | + G_PARAM_WRITABLE)); +} + +static void +g_vfs_goa_volume_init (GVfsGoaVolume *self) +{ + self->icon = g_strdup ("network-server"); + self->symbolic_icon = g_strdup ("network-server-symbolic"); +} + +static void +g_vfs_goa_volume_iface_init (GVolumeIface *iface) +{ + iface->removed = g_vfs_goa_volume_removed; + + iface->can_eject = g_vfs_goa_volume_can_eject; + iface->can_mount = g_vfs_goa_volume_can_mount; + iface->enumerate_identifiers = g_vfs_goa_volume_enumerate_identifiers; + iface->get_activation_root = g_vfs_goa_volume_get_activation_root; + iface->get_drive = g_vfs_goa_volume_get_drive; + iface->get_icon = g_vfs_goa_volume_get_icon; + iface->get_identifier = g_vfs_goa_volume_get_identifier; + iface->get_mount = g_vfs_goa_volume_get_mount; + iface->get_name = g_vfs_goa_volume_get_name; + iface->get_symbolic_icon = g_vfs_goa_volume_get_symbolic_icon; + iface->get_uuid = g_vfs_goa_volume_get_uuid; + iface->mount_fn = g_vfs_goa_volume_mount; + iface->mount_finish = g_vfs_goa_volume_mount_finish; + iface->should_automount = g_vfs_goa_volume_should_automount; +} + +GVolume * +g_vfs_goa_volume_new (GoaObject *object, const gchar *uuid) +{ + return g_object_new (G_VFS_TYPE_GOA_VOLUME, + "account", object, + "uuid", uuid, + NULL); +} diff --git a/monitor/goa/goavolume.h b/monitor/goa/goavolume.h new file mode 100644 index 00000000..2d013838 --- /dev/null +++ b/monitor/goa/goavolume.h @@ -0,0 +1,66 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* gvfs - extensions for gio + * + * Copyright (C) 2012 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * Author: Debarshi Ray <debarshir@gnome.org> + */ + +#ifndef __GOA_VOLUME_H__ +#define __GOA_VOLUME_H__ + +#include <glib-object.h> +#include <gio/gio.h> + +#define GOA_API_IS_SUBJECT_TO_CHANGE +#include <goa/goa.h> + +G_BEGIN_DECLS + +#define G_VFS_TYPE_GOA_VOLUME (g_vfs_goa_volume_get_type()) + +#define G_VFS_GOA_VOLUME(o) \ + (G_TYPE_CHECK_INSTANCE_CAST((o), \ + G_VFS_TYPE_GOA_VOLUME, GVfsGoaVolume)) + +#define G_VFS_GOA_VOLUME_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST((k), \ + G_VFS_TYPE_GOA_VOLUME, GVfsGoaVolumeClass)) + +#define G_VFS_IS_GOA_VOLUME(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE((o), \ + G_VFS_TYPE_GOA_VOLUME)) + +#define G_VFS_IS_GOA_VOLUME_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE((k), \ + G_VFS_TYPE_GOA_VOLUME)) + +#define G_VFS_GOA_VOLUME_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS((o), \ + G_VFS_TYPE_GOA_VOLUME, GVfsGoaVolumeClass)) + +typedef struct _GVfsGoaVolume GVfsGoaVolume; +typedef struct _GVfsGoaVolumeClass GVfsGoaVolumeClass; + +GType g_vfs_goa_volume_get_type (void) G_GNUC_CONST; + +GVolume *g_vfs_goa_volume_new (GoaObject *object, const gchar *uuid); + +G_END_DECLS + +#endif /* __GOA_VOLUME_H__ */ diff --git a/monitor/goa/goavolumemonitor.c b/monitor/goa/goavolumemonitor.c new file mode 100644 index 00000000..7c0fa5c6 --- /dev/null +++ b/monitor/goa/goavolumemonitor.c @@ -0,0 +1,475 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* gvfs - extensions for gio + * + * Copyright (C) 2012, 2013 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * Author: Debarshi Ray <debarshir@gnome.org> + */ + +#include <config.h> + +#include <stdio.h> + +#include <glib.h> +#include <gio/gio.h> + +#define GOA_API_IS_SUBJECT_TO_CHANGE +#include <goa/goa.h> + +#include <gvfsproxyvolumemonitordaemon.h> +#include "goavolume.h" +#include "goavolumemonitor.h" + +struct _GVfsGoaVolumeMonitor +{ + GNativeVolumeMonitor parent; + GList *accounts; + GList *volumes; + GoaClient *client; +}; + +struct _GVfsGoaVolumeMonitorClass +{ + GVolumeMonitorClass parent_class; +}; + +G_DEFINE_TYPE(GVfsGoaVolumeMonitor, g_vfs_goa_volume_monitor, G_TYPE_VOLUME_MONITOR) + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +diff_sorted_lists (GList *list1, + GList *list2, + GCompareFunc compare, + GList **added, + GList **removed, + GList **unchanged) +{ + int order; + + *added = *removed = NULL; + if (unchanged != NULL) + *unchanged = NULL; + + while (list1 != NULL && + list2 != NULL) + { + order = (*compare) (list1->data, list2->data); + if (order < 0) + { + *removed = g_list_prepend (*removed, list1->data); + list1 = list1->next; + } + else if (order > 0) + { + *added = g_list_prepend (*added, list2->data); + list2 = list2->next; + } + else + { /* same item */ + if (unchanged != NULL) + *unchanged = g_list_prepend (*unchanged, list1->data); + list1 = list1->next; + list2 = list2->next; + } + } + + while (list1 != NULL) + { + *removed = g_list_prepend (*removed, list1->data); + list1 = list1->next; + } + while (list2 != NULL) + { + *added = g_list_prepend (*added, list2->data); + list2 = list2->next; + } +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gint +account_compare (GoaObject *a, GoaObject *b) +{ + GoaAccount *account_a; + GoaAccount *account_b; + + account_a = goa_object_peek_account (a); + account_b = goa_object_peek_account (b); + + return g_strcmp0 (goa_account_get_id (account_a), goa_account_get_id (account_b)); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gint +uuid_compare (GVolume *volume_a, const gchar *uuid_b) +{ + gchar *uuid_a; + gint ret_val; + + uuid_a = g_volume_get_uuid (volume_a); + ret_val = g_strcmp0 (uuid_a, uuid_b); + g_free (uuid_a); + + return ret_val; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gint +volume_compare (GVolume *a, GVolume *b) +{ + gchar *uuid_a; + gchar *uuid_b; + gint ret_val; + + uuid_a = g_volume_get_uuid (a); + uuid_b = g_volume_get_uuid (b); + + ret_val = g_strcmp0 (uuid_a, uuid_b); + + g_free (uuid_a); + g_free (uuid_b); + + return ret_val; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +object_list_emit (GVfsGoaVolumeMonitor *monitor, + const gchar *monitor_signal, + const gchar *object_signal, + GList *objects) +{ + GList *l; + + for (l = objects; l != NULL; l = l->next) + { + g_signal_emit_by_name (monitor, monitor_signal, l->data); + if (object_signal) + g_signal_emit_by_name (l->data, object_signal); + } +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +update_accounts (GVfsGoaVolumeMonitor *self, GList **added_accounts, GList **removed_accounts) +{ + GList *added; + GList *l; + GList *new_accounts; + GList *new_files_accounts = NULL; + GList *removed; + + if (added_accounts != NULL) + *added_accounts = NULL; + + if (removed_accounts != NULL) + *removed_accounts = NULL; + + new_accounts = goa_client_get_accounts (self->client); + for (l = new_accounts; l != NULL; l = l->next) + { + GoaObject *object = GOA_OBJECT (l->data); + + if (goa_object_peek_files (object) != NULL) + new_files_accounts = g_list_prepend (new_files_accounts, object); + } + + new_files_accounts = g_list_sort (new_files_accounts, (GCompareFunc) account_compare); + diff_sorted_lists (self->accounts, + new_files_accounts, + (GCompareFunc) account_compare, + &added, + &removed, + NULL); + + for (l = removed; l != NULL; l = l->next) + { + GList *llink; + GoaObject *object = GOA_OBJECT (l->data); + + if (removed_accounts != NULL) + *removed_accounts = g_list_prepend (*removed_accounts, g_object_ref (object)); + + llink = g_list_find_custom (self->accounts, object, (GCompareFunc) account_compare); + self->accounts = g_list_remove_link (self->accounts, llink); + g_list_free_full (llink, g_object_unref); + } + + for (l = added; l != NULL; l = l->next) + { + GoaObject *object = GOA_OBJECT (l->data); + + self->accounts = g_list_prepend (self->accounts, g_object_ref (object)); + + if (added_accounts != NULL) + *added_accounts = g_list_prepend (*added_accounts, g_object_ref (object)); + } + + self->accounts = g_list_sort (self->accounts, (GCompareFunc) account_compare); + + g_list_free (added); + g_list_free (removed); + g_list_free (new_files_accounts); + g_list_free_full (new_accounts, g_object_unref); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +update_volumes (GVfsGoaVolumeMonitor *self, GList **added_volumes, GList **removed_volumes) +{ + GList *added; + GList *l; + GList *new_volumes = NULL; + GList *removed; + + if (added_volumes != NULL) + *added_volumes = NULL; + + if (removed_volumes != NULL) + *removed_volumes = NULL; + + for (l = self->accounts; l != NULL; l = l->next) + { + GVolume *volume; + GoaFiles *files; + GoaObject *object = GOA_OBJECT (l->data); + const gchar *uri; + + files = goa_object_peek_files (object); + uri = goa_files_get_uri (files); + + volume = g_vfs_goa_volume_new (object, uri); + new_volumes = g_list_prepend (new_volumes, volume); + } + + new_volumes = g_list_sort (new_volumes, (GCompareFunc) volume_compare); + diff_sorted_lists (self->volumes, + new_volumes, + (GCompareFunc) volume_compare, + &added, + &removed, + NULL); + + for (l = removed; l != NULL; l = l->next) + { + GList *llink; + GVolume *volume = G_VOLUME (l->data); + + if (removed_volumes != NULL) + *removed_volumes = g_list_prepend (*removed_volumes, g_object_ref (volume)); + + llink = g_list_find_custom (self->volumes, volume, (GCompareFunc) volume_compare); + self->volumes = g_list_remove_link (self->volumes, llink); + g_list_free_full (llink, g_object_unref); + } + + for (l = added; l != NULL; l = l->next) + { + GVolume *volume = G_VOLUME (l->data); + + self->volumes = g_list_prepend (self->volumes, g_object_ref (volume)); + + if (added_volumes != NULL) + *added_volumes = g_list_prepend (*added_volumes, g_object_ref (volume)); + } + + self->volumes = g_list_sort (self->volumes, (GCompareFunc) volume_compare); + + g_list_free (added); + g_list_free (removed); + g_list_free_full (new_volumes, g_object_unref); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +update_all (GVfsGoaVolumeMonitor *self) +{ + GList *added_volumes; + GList *removed_volumes; + + update_accounts (self, NULL, NULL); + update_volumes (self, &added_volumes, &removed_volumes); + + object_list_emit (self, "volume-removed", "removed", removed_volumes); + object_list_emit (self, "volume-added", NULL, added_volumes); + + g_list_free_full (added_volumes, g_object_unref); + g_list_free_full (removed_volumes, g_object_unref); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static GoaClient * +get_goa_client_sync (GError **error) +{ + static GoaClient *client = NULL; + static GError *_error = NULL; + static volatile gsize initialized = 0; + + if (g_once_init_enter (&initialized)) + { + client = goa_client_new_sync (NULL, &_error); + g_once_init_leave (&initialized, 1); + } + + if (_error != NULL && error != NULL) + *error = g_error_copy (_error); + + return client; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static GList * +g_vfs_goa_volume_monitor_get_connected_drives (GVolumeMonitor *_self) +{ + return NULL; +} + +static GMount * +g_vfs_goa_volume_monitor_get_mount_for_uuid (GVolumeMonitor *_self, const gchar *uuid) +{ + GVfsGoaVolumeMonitor *self = G_VFS_GOA_VOLUME_MONITOR (_self); + GMount *mount; + GVolume *volume; + + mount = NULL; + + volume = g_volume_monitor_get_volume_for_uuid (_self, uuid); + if (volume != NULL) + mount = g_volume_get_mount (G_VOLUME (volume)); + + return mount; +} + +static GList * +g_vfs_goa_volume_monitor_get_mounts (GVolumeMonitor *_self) +{ + GVfsGoaVolumeMonitor *self = G_VFS_GOA_VOLUME_MONITOR (_self); + GList *l; + GList *mounts = NULL; + + for (l = self->volumes; l != NULL; l = l->next) + { + GMount *mount; + GVolume *volume = G_VOLUME (l->data); + + mount = g_volume_get_mount (volume); + if (mount != NULL) + mounts = g_list_prepend (mounts, mount); + } + + return mounts; +} + +static GVolume * +g_vfs_goa_volume_monitor_get_volume_for_uuid (GVolumeMonitor *_self, const gchar *uuid) +{ + GVfsGoaVolumeMonitor *self = G_VFS_GOA_VOLUME_MONITOR (_self); + GList *llink; + GVolume *volume = NULL; + gpointer value; + + llink = g_list_find_custom (self->volumes, uuid, (GCompareFunc) uuid_compare); + if (llink != NULL) + volume = G_VOLUME (g_object_ref (llink->data)); + + return volume; +} + +static GList * +g_vfs_goa_volume_monitor_get_volumes (GVolumeMonitor *_self) +{ + GVfsGoaVolumeMonitor *self = G_VFS_GOA_VOLUME_MONITOR (_self); + + return g_list_copy_deep (self->volumes, (GCopyFunc) g_object_ref, NULL); +} + +static gboolean +g_vfs_goa_volume_monitor_is_supported (void) +{ + if (get_goa_client_sync (NULL) != NULL) + return TRUE; + return FALSE; +} + +static void +g_vfs_goa_volume_monitor_dispose (GObject *_self) +{ + GVfsGoaVolumeMonitor *self = G_VFS_GOA_VOLUME_MONITOR (_self); + + g_list_free_full (self->accounts, g_object_unref); + self->accounts = NULL; + + g_list_free_full (self->volumes, g_object_unref); + self->volumes = NULL; + + g_clear_object (&self->client); + + G_OBJECT_CLASS (g_vfs_goa_volume_monitor_parent_class)->dispose (_self); +} + +static void +g_vfs_goa_volume_monitor_class_init (GVfsGoaVolumeMonitorClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GVolumeMonitorClass *monitor_class = G_VOLUME_MONITOR_CLASS (klass); + + gobject_class->dispose = g_vfs_goa_volume_monitor_dispose; + + monitor_class->get_connected_drives = g_vfs_goa_volume_monitor_get_connected_drives; + monitor_class->get_mount_for_uuid = g_vfs_goa_volume_monitor_get_mount_for_uuid; + monitor_class->get_mounts = g_vfs_goa_volume_monitor_get_mounts; + monitor_class->get_volume_for_uuid = g_vfs_goa_volume_monitor_get_volume_for_uuid; + monitor_class->get_volumes = g_vfs_goa_volume_monitor_get_volumes; + monitor_class->is_supported = g_vfs_goa_volume_monitor_is_supported; +} + +static void +g_vfs_goa_volume_monitor_init (GVfsGoaVolumeMonitor *self) +{ + GError *error; + + error = NULL; + self->client = get_goa_client_sync (&error); + if (self->client == NULL) + { + g_warning ("Failed to connect to GOA: %s", error->message); + g_error_free (error); + return; + } + + update_all (self); + + g_signal_connect_swapped (self->client, "account-added", G_CALLBACK (update_all), self); + g_signal_connect_swapped (self->client, "account-changed", G_CALLBACK (update_all), self); + g_signal_connect_swapped (self->client, "account-removed", G_CALLBACK (update_all), self); +} + +GVolumeMonitor * +g_vfs_goa_volume_monitor_new (void) +{ + return g_object_new (G_VFS_TYPE_GOA_VOLUME_MONITOR, NULL); +} diff --git a/monitor/goa/goavolumemonitor.h b/monitor/goa/goavolumemonitor.h new file mode 100644 index 00000000..149f86e7 --- /dev/null +++ b/monitor/goa/goavolumemonitor.h @@ -0,0 +1,63 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* gvfs - extensions for gio + * + * Copyright (C) 2012 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * Author: Debarshi Ray <debarshir@gnome.org> + */ + +#ifndef __GOA_VOLUME_MONITOR_H__ +#define __GOA_VOLUME_MONITOR_H__ + +#include <glib-object.h> +#include <gio/gio.h> + +G_BEGIN_DECLS + +#define G_VFS_TYPE_GOA_VOLUME_MONITOR (g_vfs_goa_volume_monitor_get_type()) + +#define G_VFS_GOA_VOLUME_MONITOR(o) \ + (G_TYPE_CHECK_INSTANCE_CAST((o), \ + G_VFS_TYPE_GOA_VOLUME_MONITOR, GVfsGoaVolumeMonitor)) + +#define G_VFS_GOA_VOLUME_MONITOR_CLASS(k) \ + (G_TYPE_CHECK_CLASS_CAST((k), \ + G_VFS_TYPE_GOA_VOLUME_MONITOR, GVfsGoaVolumeMonitorClass)) + +#define G_VFS_IS_GOA_VOLUME_MONITOR(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE((o), \ + G_VFS_TYPE_GOA_VOLUME_MONITOR)) + +#define G_VFS_IS_GOA_VOLUME_MONITOR_CLASS(k) \ + (G_TYPE_CHECK_CLASS_TYPE((k), \ + G_VFS_TYPE_GOA_VOLUME_MONITOR)) + +#define G_VFS_GOA_VOLUME_MONITOR_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS((o), \ + G_VFS_TYPE_GOA_VOLUME_MONITOR, GVfsGoaVolumeMonitorClass)) + +typedef struct _GVfsGoaVolumeMonitor GVfsGoaVolumeMonitor; +typedef struct _GVfsGoaVolumeMonitorClass GVfsGoaVolumeMonitorClass; + +GType g_vfs_goa_volume_monitor_get_type (void) G_GNUC_CONST; + +GVolumeMonitor *g_vfs_goa_volume_monitor_new (void); + +G_END_DECLS + +#endif /* __GOA_VOLUME_MONITOR_H__ */ diff --git a/monitor/goa/goavolumemonitordaemon.c b/monitor/goa/goavolumemonitordaemon.c new file mode 100644 index 00000000..5765a671 --- /dev/null +++ b/monitor/goa/goavolumemonitordaemon.c @@ -0,0 +1,49 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* gvfs - extensions for gio + * + * Copyright (C) 2012 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * Author: Debarshi Ray <debarshir@gnome.org> + */ + +#include <config.h> + +#include <locale.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <gio/gio.h> + +#include <gvfsproxyvolumemonitordaemon.h> +#include "goavolumemonitor.h" + +int +main (int argc, char *argv[]) +{ + setlocale (LC_ALL, ""); + + bindtextdomain (GETTEXT_PACKAGE, GVFS_LOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + g_vfs_proxy_volume_monitor_daemon_init (); + return g_vfs_proxy_volume_monitor_daemon_main (argc, + argv, + "org.gtk.Private.GoaVolumeMonitor", + G_VFS_TYPE_GOA_VOLUME_MONITOR); +} diff --git a/monitor/goa/org.gtk.Private.GoaVolumeMonitor.service.in b/monitor/goa/org.gtk.Private.GoaVolumeMonitor.service.in new file mode 100644 index 00000000..cb9a3511 --- /dev/null +++ b/monitor/goa/org.gtk.Private.GoaVolumeMonitor.service.in @@ -0,0 +1,3 @@ +[D-BUS Service] +Name=org.gtk.Private.GoaVolumeMonitor +Exec=@libexecdir@/gvfs-goa-volume-monitor |