summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--daemon/gvfsbackendgoogle.c223
-rw-r--r--meson.build2
2 files changed, 192 insertions, 33 deletions
diff --git a/daemon/gvfsbackendgoogle.c b/daemon/gvfsbackendgoogle.c
index 1b5db93c..27f670ac 100644
--- a/daemon/gvfsbackendgoogle.c
+++ b/daemon/gvfsbackendgoogle.c
@@ -56,6 +56,8 @@ struct _GVfsBackendGoogle
GVfsBackend parent;
GDataDocumentsService *service;
GDataEntry *root;
+ GDataEntry *home;
+ GDataEntry *shared_with_me_dir;
GHashTable *entries; /* gchar *entry_id -> GDataEntry */
GHashTable *dir_entries; /* DirEntriesKey -> GDataEntry */
GHashTable *dir_timestamps; /* gchar *entry_id -> gint64 *timestamp */
@@ -86,6 +88,9 @@ G_DEFINE_TYPE(GVfsBackendGoogle, g_vfs_backend_google, G_VFS_TYPE_BACKEND)
#define SOURCE_ID_PROPERTY_KEY "GVfsSourceID"
#define PARENT_ID_PROPERTY_KEY "GVfsParentID"
+
+#define ROOT_ID "GVfsRoot"
+#define SHARED_WITH_ME_ID "GVfsSharedWithMe"
/* ---------------------------------------------------------------------------------------------------- */
typedef struct
@@ -440,6 +445,12 @@ is_owner (GVfsBackendGoogle *self, GDataEntry *entry)
return FALSE;
}
+static gboolean
+is_shared_with_me (GDataEntry *entry)
+{
+ return gdata_documents_entry_get_shared_with_me_date (GDATA_DOCUMENTS_ENTRY (entry)) > 0;
+}
+
/* ---------------------------------------------------------------------------------------------------- */
static const gchar *
@@ -717,6 +728,27 @@ insert_entry (GVfsBackendGoogle *self,
}
static void
+insert_custom_entry (GVfsBackendGoogle *self,
+ GDataEntry *entry,
+ const gchar *parent_id)
+{
+ DirEntriesKey *k;
+ const gchar *id;
+ const gchar *title;
+
+ id = gdata_entry_get_id (entry);
+ title = gdata_entry_get_title (entry);
+
+ g_hash_table_insert (self->entries, g_strdup (id), g_object_ref (entry));
+
+ k = dir_entries_key_new (id, parent_id);
+ g_hash_table_insert (self->dir_entries, k, g_object_ref (entry));
+
+ k = dir_entries_key_new (title, parent_id);
+ g_hash_table_insert (self->dir_entries, k, g_object_ref (entry));
+}
+
+static void
remove_entry_full (GVfsBackendGoogle *self,
GDataEntry *entry,
gboolean restore_dir_collisions)
@@ -732,6 +764,9 @@ remove_entry_full (GVfsBackendGoogle *self,
g_hash_table_remove (self->entries, id);
+ if (is_shared_with_me (entry))
+ g_hash_table_remove (self->dir_timestamps, SHARED_WITH_ME_ID);
+
parent_ids = get_parent_ids (self, entry);
for (ll = parent_ids; ll != NULL; ll = ll->next)
{
@@ -874,6 +909,9 @@ is_entry_valid (GDataEntry *entry)
gint64 *timestamp;
timestamp = g_object_get_data (G_OBJECT (entry), "timestamp");
+ if (timestamp == NULL)
+ return TRUE;
+
return (g_get_real_time () - *timestamp < REBUILD_ENTRIES_TIMEOUT * G_USEC_PER_SEC);
}
@@ -882,6 +920,9 @@ is_dir_listing_valid (GVfsBackendGoogle *self, GDataEntry *parent)
{
gint64 *timestamp;
+ if (parent == self->root)
+ return TRUE;
+
timestamp = g_hash_table_lookup (self->dir_timestamps, gdata_entry_get_id (parent));
if (timestamp != NULL)
return (g_get_real_time () - *timestamp < REBUILD_ENTRIES_TIMEOUT * G_USEC_PER_SEC);
@@ -904,7 +945,10 @@ rebuild_dir (GVfsBackendGoogle *self,
/* g_strdup() is necessary to prevent segfault because gdata_entry_get_id() calls g_free() */
parent_id = g_strdup (gdata_entry_get_id (parent));
- search = g_strdup_printf ("'%s' in parents", parent_id);
+ if (parent == self->shared_with_me_dir)
+ search = g_strdup ("sharedWithMe");
+ else
+ search = g_strdup_printf ("'%s' in parents", parent_id);
query = gdata_documents_query_new_with_limits (search, 1, G_MAXUINT);
gdata_documents_query_set_show_folders (query, TRUE);
g_free (search);
@@ -971,10 +1015,16 @@ resolve_child (GVfsBackendGoogle *self,
GDataEntry *entry;
const gchar *parent_id;
GError *local_error = NULL;
+ gboolean is_shared_with_me_dir = (parent == self->shared_with_me_dir);
parent_id = gdata_entry_get_id (parent);
k = dir_entries_key_new (basename, parent_id);
- entry = g_hash_table_lookup (self->dir_entries, k);
+
+ if (is_shared_with_me_dir)
+ entry = g_hash_table_lookup (self->entries, basename);
+ else
+ entry = g_hash_table_lookup (self->dir_entries, k);
+
if ((entry == NULL && !is_dir_listing_valid (self, parent)) ||
(entry != NULL && !is_entry_valid (entry)))
{
@@ -985,7 +1035,10 @@ resolve_child (GVfsBackendGoogle *self,
goto out;
}
- entry = g_hash_table_lookup (self->dir_entries, k);
+ if (is_shared_with_me_dir)
+ entry = g_hash_table_lookup (self->entries, basename);
+ else
+ entry = g_hash_table_lookup (self->dir_entries, k);
}
if (entry == NULL)
@@ -1212,6 +1265,9 @@ build_file_info (GVfsBackendGoogle *self,
gint64 ctime;
gint64 mtime;
gsize i;
+ gboolean is_shared_with_me_dir = (entry == self->shared_with_me_dir);
+ gboolean is_home = (entry == self->home);
+ gboolean can_edit;
if (GDATA_IS_DOCUMENTS_FOLDER (entry))
is_folder = TRUE;
@@ -1225,7 +1281,10 @@ build_file_info (GVfsBackendGoogle *self,
symlink_name = g_path_get_basename (filename);
}
- g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME, !is_root);
+ /* TODO: It is not always possible to rename, delete, or list children.
+ * However, the proper implementation of gdata_documents_entry_can_rename/
+ * _delete/_list_children would require port to Google Drive API v3. */
+ g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME, !is_root && !is_home && !is_shared_with_me_dir);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE, is_folder);
@@ -1233,7 +1292,7 @@ build_file_info (GVfsBackendGoogle *self,
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_STANDARD_IS_VOLATILE, is_symlink);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, FALSE);
- g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, !is_root);
+ g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, !is_root && !is_home && !is_shared_with_me_dir);
if (is_folder)
{
@@ -1285,10 +1344,23 @@ build_file_info (GVfsBackendGoogle *self,
g_file_info_set_content_type (info, content_type);
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE, content_type);
- icon = g_content_type_get_icon (content_type);
- g_file_info_set_icon (info, icon);
+ if (is_home)
+ {
+ icon = g_themed_icon_new_with_default_fallbacks ("user-home");
+ symbolic_icon = g_themed_icon_new_with_default_fallbacks ("user-home-symbolic");
+ }
+ else if (is_shared_with_me_dir)
+ {
+ icon = g_themed_icon_new_with_default_fallbacks ("folder-publicshare");
+ symbolic_icon = g_themed_icon_new_with_default_fallbacks ("folder-publicshare-symbolic");
+ }
+ else
+ {
+ icon = g_content_type_get_icon (content_type);
+ symbolic_icon = g_content_type_get_symbolic_icon (content_type);
+ }
- symbolic_icon = g_content_type_get_symbolic_icon (content_type);
+ g_file_info_set_icon (info, icon);
g_file_info_set_symbolic_icon (info, symbolic_icon);
g_object_unref (icon);
@@ -1309,15 +1381,11 @@ build_file_info (GVfsBackendGoogle *self,
g_file_info_set_name (info, name);
- if (is_root)
- title = g_vfs_backend_get_display_name (G_VFS_BACKEND (self));
- else
- title = gdata_entry_get_title (entry);
-
+ title = gdata_entry_get_title (entry);
g_file_info_set_display_name (info, title);
g_file_info_set_edit_name (info, title);
- if (is_root)
+ if (is_root || is_home || is_shared_with_me_dir)
goto out;
copy_name = generate_copy_name (self, entry, entry_path);
@@ -1466,6 +1534,13 @@ g_vfs_backend_google_copy (GVfsBackend *_self,
goto out;
}
+ if (source_entry == self->root || source_parent == self->root ||
+ destination_parent == self->root || destination_parent == self->shared_with_me_dir)
+ {
+ g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Operation not supported"));
+ goto out;
+ }
+
id = gdata_entry_get_id (source_entry);
title = gdata_entry_get_title (source_entry);
source_parent_id = gdata_entry_get_id (source_parent);
@@ -1880,6 +1955,13 @@ g_vfs_backend_google_move (GVfsBackend *_self,
goto out;
}
+ if (source_entry == self->root || source_parent == self->root ||
+ destination_parent == self->root || destination_parent == self->shared_with_me_dir)
+ {
+ g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Operation not supported"));
+ goto out;
+ }
+
source_id = gdata_entry_get_id (source_entry);
source_parent_id = gdata_entry_get_id (source_parent);
destination_parent_id = gdata_entry_get_id (destination_parent);
@@ -2293,7 +2375,18 @@ g_vfs_backend_google_delete (GVfsBackend *_self,
/* g_strdup() is necessary to prevent segfault because gdata_entry_get_id() calls g_free() */
id = g_strdup (gdata_entry_get_id (entry));
- if (GDATA_IS_DOCUMENTS_FOLDER (entry))
+ parent = resolve_dir (self, filename, cancellable, NULL, NULL, &error);
+ if (error != NULL)
+ {
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ goto out;
+ }
+
+ /* The G_IO_ERROR_NOT_EMPTY error is not intentionally returned for folders in
+ * Shared with me folder, because the recursive delete would not work, or could
+ * really remove the files from the original folder also for the owner... */
+ if (GDATA_IS_DOCUMENTS_FOLDER (entry) && parent != self->shared_with_me_dir)
{
GHashTableIter iter;
DirEntriesKey *key;
@@ -2323,17 +2416,9 @@ g_vfs_backend_google_delete (GVfsBackend *_self,
}
}
- parent = resolve_dir (self, filename, cancellable, NULL, NULL, &error);
- if (error != NULL)
- {
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- goto out;
- }
-
g_debug (" entry path: %s\n", entry_path);
- if (entry == self->root)
+ if (entry == self->root || entry == self->home || entry == self->shared_with_me_dir)
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Operation not supported"));
goto out;
@@ -2347,7 +2432,45 @@ g_vfs_backend_google_delete (GVfsBackend *_self,
parent_ids = get_parent_ids (self, entry);
parent_ids_len = g_list_length (parent_ids);
- if (parent_ids_len > 1 || !is_owner (self, GDATA_ENTRY (entry)))
+
+ /* The files in Shared with me folder doesn't have a parent and also we don't
+ * have enough permissions to physically delete them. But they can be removed
+ * by removal of our permissions... */
+ if (parent == self->shared_with_me_dir)
+ {
+ GDataFeed *acl_feed;
+
+ acl_feed = gdata_access_handler_get_rules (GDATA_ACCESS_HANDLER (entry),
+ GDATA_SERVICE (self->service),
+ cancellable,
+ NULL, NULL, &error);
+ if (error == NULL)
+ {
+ GDataGoaAuthorizer *goa_authorizer;
+ GoaAccount *account;
+ const gchar *account_identity;
+ GDataAuthorizationDomain *auth_domain;
+ GList *entries;
+
+ goa_authorizer = GDATA_GOA_AUTHORIZER (gdata_service_get_authorizer (GDATA_SERVICE (self->service)));
+ account = goa_object_peek_account (gdata_goa_authorizer_get_goa_object (goa_authorizer));
+ account_identity = goa_account_get_identity (account);
+ auth_domain = gdata_documents_service_get_primary_authorization_domain ();
+
+ for (entries = gdata_feed_get_entries (acl_feed); entries != NULL; entries = entries->next)
+ {
+ const gchar *scope_value = NULL;
+ GDataAccessRule *rule = GDATA_ACCESS_RULE (entries->data);
+
+ gdata_access_rule_get_scope (rule, NULL, &scope_value);
+ if (g_strcmp0 (scope_value, account_identity) == 0)
+ gdata_service_delete_entry (GDATA_SERVICE (self->service), auth_domain, GDATA_ENTRY (rule), NULL, &error);
+ }
+
+ g_object_unref (acl_feed);
+ }
+ }
+ else if (parent_ids_len > 1 || !is_owner (self, GDATA_ENTRY (entry)))
{
/* gdata_documents_service_remove_entry_from_folder () returns the
* updated entry variable provided as argument with an increased ref.
@@ -2405,10 +2528,12 @@ g_vfs_backend_google_enumerate (GVfsBackend *_self,
GVfsBackendGoogle *self = G_VFS_BACKEND_GOOGLE (_self);
GCancellable *cancellable = G_VFS_JOB (job)->cancellable;
GDataEntry *entry;
+ GDataEntry *child;
GError *error;
GHashTableIter iter;
char *parent_path;
char *id = NULL;
+ gboolean is_shared_with_me_dir;
g_rec_mutex_lock (&self->mutex);
g_debug ("+ enumerate: %s\n", filename);
@@ -2444,18 +2569,20 @@ g_vfs_backend_google_enumerate (GVfsBackend *_self,
/* g_strdup() is necessary to prevent segfault because gdata_entry_get_id() calls g_free() */
id = g_strdup (gdata_entry_get_id (entry));
+ is_shared_with_me_dir = (entry == self->shared_with_me_dir);
+
g_hash_table_iter_init (&iter, self->entries);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &entry))
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &child))
{
DirEntriesKey *k;
- GDataEntry *child;
gchar *child_id;
/* g_strdup() is necessary to prevent segfault because gdata_entry_get_id() calls g_free() */
- child_id = g_strdup (gdata_entry_get_id (entry));
+ child_id = g_strdup (gdata_entry_get_id (child));
k = dir_entries_key_new (child_id, id);
- if ((child = g_hash_table_lookup (self->dir_entries, k)) != NULL)
+ if ((is_shared_with_me_dir && is_shared_with_me (child)) ||
+ (!is_shared_with_me_dir && g_hash_table_lookup (self->dir_entries, k) != NULL))
{
GFileInfo *info;
gchar *entry_path;
@@ -2473,7 +2600,7 @@ g_vfs_backend_google_enumerate (GVfsBackend *_self,
info = g_file_info_new ();
entry_path = g_build_path ("/", parent_path, child_id, NULL);
child_filename = g_build_filename (filename, child_id, NULL);
- build_file_info (self, entry, flags, info, matcher, child_filename, entry_path, NULL);
+ build_file_info (self, child, flags, info, matcher, child_filename, entry_path, NULL);
g_vfs_job_enumerate_add_info (job, info);
g_object_unref (info);
g_free (entry_path);
@@ -2692,8 +2819,11 @@ g_vfs_backend_google_mount (GVfsBackend *_self,
auth_domain = gdata_documents_service_get_primary_authorization_domain ();
+ self->root = GDATA_ENTRY (gdata_documents_folder_new (ROOT_ID));
+ gdata_entry_set_title (self->root, self->account_identity);
+
error = NULL;
- self->root = gdata_service_query_single_entry (GDATA_SERVICE (self->service),
+ self->home = gdata_service_query_single_entry (GDATA_SERVICE (self->service),
auth_domain,
"root",
NULL,
@@ -2707,6 +2837,15 @@ g_vfs_backend_google_mount (GVfsBackend *_self,
g_error_free (error);
goto out;
}
+ insert_custom_entry (self, self->home, ROOT_ID);
+
+ self->shared_with_me_dir = GDATA_ENTRY (gdata_documents_folder_new (SHARED_WITH_ME_ID));
+ /* Translators: This is the "Shared with me" folder on https://drive.google.com. */
+ gdata_entry_set_title (self->shared_with_me_dir, _("Shared with me"));
+ insert_custom_entry (self, self->shared_with_me_dir, ROOT_ID);
+
+ /* TODO: Make it work with GOA volume monitor resp. shadow mounts. */
+ g_vfs_backend_set_default_location (_self, gdata_entry_get_id (self->home));
g_vfs_backend_set_mount_spec (_self, spec);
g_vfs_backend_set_display_name (_self, self->account_identity);
@@ -2826,6 +2965,12 @@ g_vfs_backend_google_push (GVfsBackend *_self,
goto out;
}
+ if (destination_parent == self->root || destination_parent == self->shared_with_me_dir)
+ {
+ g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Operation not supported"));
+ goto out;
+ }
+
existing_entry = resolve_child (self, destination_parent, destination_basename, cancellable, NULL);
if (existing_entry != NULL)
{
@@ -3471,7 +3616,7 @@ g_vfs_backend_google_set_display_name (GVfsBackend *_self,
g_debug (" entry path: %s\n", entry_path);
- if (entry == self->root)
+ if (entry == self->root || entry == self->home || entry == self->shared_with_me_dir)
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Operation not supported"));
goto out;
@@ -3549,6 +3694,12 @@ g_vfs_backend_google_create (GVfsBackend *_self,
g_debug (" parent path: %s\n", parent_path);
+ if (parent == self->root || parent == self->shared_with_me_dir)
+ {
+ g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Operation not supported"));
+ goto out;
+ }
+
existing_entry = resolve_child (self, parent, basename, cancellable, NULL);
if (existing_entry != NULL)
{
@@ -3658,6 +3809,12 @@ g_vfs_backend_google_replace (GVfsBackend *_self,
g_debug (" parent path: %s\n", parent_path);
+ if (parent == self->root || parent == self->shared_with_me_dir)
+ {
+ g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Operation not supported"));
+ goto out;
+ }
+
existing_entry = resolve_child (self, parent, basename, cancellable, NULL);
if (existing_entry != NULL)
{
@@ -3888,6 +4045,8 @@ g_vfs_backend_google_dispose (GObject *_self)
g_clear_object (&self->service);
g_clear_object (&self->root);
+ g_clear_object (&self->home);
+ g_clear_object (&self->shared_with_me_dir);
g_clear_object (&self->client);
g_clear_pointer (&self->entries, g_hash_table_unref);
g_clear_pointer (&self->dir_entries, g_hash_table_unref);
diff --git a/meson.build b/meson.build
index b646c0fc..14a18c16 100644
--- a/meson.build
+++ b/meson.build
@@ -419,7 +419,7 @@ enable_google = get_option('google')
if enable_google
assert(enable_goa, 'Google backend requested but GOA is required')
- libgdata_dep = dependency('libgdata', version: '>= 0.17.11')
+ libgdata_dep = dependency('libgdata', version: '>= 0.18.0')
endif
# *** Check for gphoto2 ***