[gvfs] dav: Add support for server-side copying



commit 2f2057e0f86335ea918f3dd0ed2a7bcbe0f3a6d2
Author: Ross Lagerwall <rosslagerwall gmail com>
Date:   Thu Jan 22 22:21:37 2015 +0000

    dav: Add support for server-side copying
    
    Use webdav's COPY method to implement support for server-side copying.
    This improves performance considerably, especially over slow
    connections.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=743379

 daemon/gvfsbackenddav.c  |  111 ++++++++++++++++++++++++++++++++++++++++++++++
 daemon/gvfsbackendhttp.c |    3 +
 2 files changed, 114 insertions(+), 0 deletions(-)
---
diff --git a/daemon/gvfsbackenddav.c b/daemon/gvfsbackenddav.c
index 8fc824d..d4f50a6 100644
--- a/daemon/gvfsbackenddav.c
+++ b/daemon/gvfsbackenddav.c
@@ -2851,6 +2851,116 @@ do_move (GVfsBackend *backend,
   soup_uri_free (target_uri);
 }
 
+static void
+do_copy (GVfsBackend *backend,
+         GVfsJobCopy *job,
+         const char *source,
+         const char *destination,
+         GFileCopyFlags flags,
+         GFileProgressCallback progress_callback,
+         gpointer progress_callback_data)
+{
+  SoupMessage *msg;
+  SoupURI *source_uri;
+  SoupURI *target_uri;
+  guint status;
+  GFileType source_ft, target_ft;
+  GError *error = NULL;
+  gboolean res;
+
+  if (flags & G_FILE_COPY_BACKUP)
+    {
+      /* Return G_IO_ERROR_NOT_SUPPORTED instead of
+       * G_IO_ERROR_CANT_CREATE_BACKUP to proceed with the GIO fallback
+       * copy. */
+      g_vfs_job_failed_literal (G_VFS_JOB (job),
+                                G_IO_ERROR,
+                                G_IO_ERROR_NOT_SUPPORTED,
+                                "Operation not supported");
+      return;
+    }
+
+  source_uri = g_vfs_backend_dav_uri_for_path (backend, source, FALSE);
+  target_uri = g_vfs_backend_dav_uri_for_path (backend, destination, FALSE);
+
+  res = stat_location (backend, source_uri, &source_ft, NULL, &error);
+  if (!res)
+    {
+      g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+      goto error;
+    }
+
+  res = stat_location (backend, target_uri, &target_ft, NULL, &error);
+  if (res)
+    {
+      if (flags & G_FILE_COPY_OVERWRITE)
+        {
+          if (target_ft == G_FILE_TYPE_DIRECTORY)
+            {
+              if (source_ft == G_FILE_TYPE_DIRECTORY)
+                g_vfs_job_failed_literal (G_VFS_JOB(job),
+                                          G_IO_ERROR,
+                                          G_IO_ERROR_WOULD_MERGE,
+                                          _("Can't copy directory over directory"));
+              else
+                g_vfs_job_failed_literal (G_VFS_JOB(job),
+                                          G_IO_ERROR,
+                                          G_IO_ERROR_IS_DIRECTORY,
+                                          _("File is directory"));
+              goto error;
+            }
+        }
+      else
+        {
+          g_vfs_job_failed_literal (G_VFS_JOB (job),
+                                    G_IO_ERROR,
+                                    G_IO_ERROR_EXISTS,
+                                    _("Target file already exists"));
+          goto error;
+        }
+    }
+  else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+    {
+      g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+      goto error;
+    }
+
+  if (source_ft == G_FILE_TYPE_DIRECTORY)
+    {
+      g_vfs_job_failed_literal (G_VFS_JOB (job),
+                                G_IO_ERROR,
+                                G_IO_ERROR_WOULD_RECURSE,
+                                _("Can't recursively copy directory"));
+      goto error;
+    }
+
+  msg = soup_message_new_from_uri (SOUP_METHOD_COPY, source_uri);
+  message_add_destination_header (msg, target_uri);
+  message_add_overwrite_header (msg, flags & G_FILE_COPY_OVERWRITE);
+
+  status = g_vfs_backend_dav_send_message (backend, msg);
+
+  /* See do_set_display_name () for the explanation of the PRECONDITION_FAILED
+   * and IS_REDIRECTION handling below. */
+
+  if (SOUP_STATUS_IS_SUCCESSFUL (status))
+    g_vfs_job_succeeded (G_VFS_JOB (job));
+  else if (status == SOUP_STATUS_PRECONDITION_FAILED ||
+           SOUP_STATUS_IS_REDIRECTION (status))
+    g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
+                      G_IO_ERROR_EXISTS,
+                      _("Target file already exists"));
+  else
+    http_job_failed (G_VFS_JOB (job), msg);
+
+  g_object_unref (msg);
+
+error:
+  g_clear_error (&error);
+  soup_uri_free (source_uri);
+  soup_uri_free (target_uri);
+}
+
 #define CHUNK_SIZE 65536
 
 /* Used to keep track of the state of reads in flight when the restarted signal
@@ -3255,5 +3365,6 @@ g_vfs_backend_dav_class_init (GVfsBackendDavClass *klass)
   backend_class->delete            = do_delete;
   backend_class->set_display_name  = do_set_display_name;
   backend_class->move              = do_move;
+  backend_class->copy              = do_copy;
   backend_class->try_push          = try_push;
 }
diff --git a/daemon/gvfsbackendhttp.c b/daemon/gvfsbackendhttp.c
index 7d6f32c..46f950a 100644
--- a/daemon/gvfsbackendhttp.c
+++ b/daemon/gvfsbackendhttp.c
@@ -714,6 +714,9 @@ g_vfs_backend_http_class_init (GVfsBackendHttpClass *klass)
   /* Send Accept-Language header (see bug 166795) */
   g_object_set (the_session, "accept-language-auto", TRUE, NULL);
 
+  /* Prevent connection timeouts during long operations like COPY. */
+  g_object_set (the_session, "timeout", 0, NULL);
+
   /* Logging */
   debug = g_getenv ("GVFS_HTTP_DEBUG");
   if (debug)


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