All pastes #975730 Raw Edit

dav upload

public diff v1 · immutable
·published 2008-04-07 19:31 UTC
rendered paste body
diff --git a/daemon/gvfsbackenddav.c b/daemon/gvfsbackenddav.cindex 25fb24c..1a89a7e 100644--- a/daemon/gvfsbackenddav.c+++ b/daemon/gvfsbackenddav.c@@ -52,6 +52,7 @@ #include "gvfsjobopenforwrite.h" #include "gvfsjobwrite.h" #include "gvfsjobseekwrite.h"+#include "gvfsjobclosewrite.h" #include "gvfsjobsetdisplayname.h" #include "gvfsjobqueryinfo.h" #include "gvfsjobqueryfsinfo.h"@@ -186,6 +187,23 @@ message_add_redirect_header (SoupMessage         *msg,                                header_redirect); } +static void+message_add_if_match_header (SoupMessage *msg,+                             const char  *value,+                             gboolean     negative)+{+  const char *header;++  if (value == NULL)+    return;++  header = negative ? "If-None-Match" : "If-Match";++  soup_message_headers_append (msg->request_headers,+                               header, value);++}+ static inline gboolean str_equal (const char *a, const char *b, gboolean insensitive) {@@ -265,7 +283,14 @@ redirect_handler (SoupMessage *msg, gpointer user_data)     guint        status;     gboolean     redirect; +    g_print ("+ redirect_handler\n");+     status = msg->status_code;++    if (! SOUP_STATUS_IS_REDIRECTION (status)) {+      return;+    }+     new_loc = soup_message_headers_get (msg->response_headers, "Location");      /* If we don't have a location to redirect to, just fail */@@ -382,6 +407,36 @@ g_vfs_backend_dav_send_message (GVfsBackend *backend, SoupMessage *message)   return http_backend_send_message (backend, message); } +static SoupSession *+g_vfs_backend_dav_get_session (GVfsBackend *backend)+{+  GVfsBackendHttp *http_backend;++  http_backend = G_VFS_BACKEND_HTTP (backend);+  return http_backend->session;+}++static void+g_vfs_backend_dav_queue_message (GVfsBackend         *backend,+                                 SoupMessage         *message,+                                 SoupSessionCallback  callback,+                                 gpointer             user_data)+{+  GVfsBackendHttp *http_backend;+  SoupSession     *session;++  http_backend = G_VFS_BACKEND_HTTP (backend);+  session = http_backend->session;++  /* We have our own custom redirect handler */+  soup_message_set_flags (message, SOUP_MESSAGE_NO_REDIRECT);++  soup_message_add_header_handler (message, "got_body", "Location",+                                   G_CALLBACK (redirect_handler), session);++  soup_session_queue_message (session, message, callback, user_data);+}+ /* ************************************************************************* */ /* generic xml parsing functions */ @@ -1723,6 +1778,7 @@ do_enumerate (GVfsBackend           *backend, static gboolean dav_make_backup (GVfsBackend  *backend,                  SoupURI      *uri,+                 const char   *etag,                  GError      **error) {   SoupURI     *backup_uri;@@ -1735,6 +1791,7 @@ dav_make_backup (GVfsBackend  *backend,    message_add_destination_header (msg, backup_uri);   message_add_overwrite_header (msg, TRUE);+  message_add_if_match_header (msg, etag, FALSE);    status = g_vfs_backend_dav_send_message (backend, msg);   res = TRUE;@@ -1753,256 +1810,494 @@ dav_make_backup (GVfsBackend  *backend,   return res; } -/* *** create () *** */+typedef enum {++  PF_CANCLLED = -1,++  PF_OPEN = 0,+  PF_NEED_DATA,+  PF_HAVE_DATA,+  PF_DATA_WRITTEN,+  PF_CLOSE,+  PF_CLOSE_WAIT,+  PF_CLOSED++} PutFileState;++typedef struct {++  PutFileState state;++  GCancellable  *cancellable;+  GError        *error;++  SoupSession *session;++  GVfsJob     *job;++  GMutex      *lock;+  GCond       *cond;++  char        *data;+  gsize        data_len;++  char        *etag;++} PutFileHandle;++static PutFileHandle *+put_file_handle_new (GVfsJob *job)+{+  PutFileHandle *handle;++  handle = g_slice_new0 (PutFileHandle);++  handle->lock = g_mutex_new ();+  handle->cond = g_cond_new ();++  handle->state = PF_OPEN;+ +  handle->job = job;++  return handle;+}++static void+put_file_handle_free (PutFileHandle *handle)+{+  g_mutex_free (handle->lock);+  g_cond_free (handle->cond);++  if (handle->cancellable)+    g_object_unref (handle->cancellable);++  g_slice_free (PutFileHandle, handle);+}++#if 0 static void-try_create_tested_existence (SoupSession *session, SoupMessage *msg,-                             gpointer user_data)+put_file_handle_job_cancelled (GVfsJob       *job,+                               PutFileHandle *handle) {-  GVfsJob *job = G_VFS_JOB (user_data);-  GVfsBackendHttp *op_backend = job->backend_data;-  GOutputStream   *stream;-  SoupMessage     *put_msg;-  SoupURI         *uri;+  g_mutex_lock (handle->lock);+ -  if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))+  g_mutex_unlock (handle->lock);+}++static guint+put_file_handle_job_glue_cancellable (PutFileHandle *handle,+                                      GVfsJob       *job)+{+  guint id;++  id = g_signal_connect (job, "cancelled",+                         (GCallback) put_file_handle_job_cancelled,+                         PutFileHandle);+  return id;+}+#endif++static gboolean+put_file_handle_wirte (PutFileHandle  *handle,+                       char           *buffer,+                       gsize           buffer_size,+                       GCancellable   *cancellable,+                       GError        **error)+{+  gboolean res;+  gboolean have_written;++  have_written = FALSE;+  res = TRUE;++  g_print ("+ put_file_handle_wirte\n");+  g_mutex_lock (handle->lock);+  g_print ("  put_file_handle_wirte: have lock %d\n", handle->state);++restart:+  switch (handle->state)     {-      g_vfs_job_failed (job,-                        G_IO_ERROR,-                        G_IO_ERROR_EXISTS,-                        _("Target file already exists"));-      return;+    case PF_HAVE_DATA:+      /* Need to wait until data is written */+      g_cond_wait (handle->cond, handle->lock);+      goto restart;++    case PF_NEED_DATA:+      handle->data = buffer;+      handle->data_len = buffer_size;+      handle->state = PF_HAVE_DATA;+      have_written = TRUE;+      g_cond_signal (handle->cond);+      g_print ("  put_file_handle_wirte: does that work?!\n");+      g_cond_wait (handle->cond, handle->lock);+      goto restart;+      break;++    case PF_DATA_WRITTEN:++      if (have_written == FALSE)+        goto restart;++      handle->state = PF_NEED_DATA;++      if ((res = handle->error == NULL) != TRUE)+        g_propagate_error (error, handle->error);+      break;++    default:+      g_assert_not_reached ();     }-  /* TODO: other errors */ -  uri = soup_message_get_uri (msg);-  put_msg = soup_message_new_from_uri (SOUP_METHOD_PUT, uri);+  g_assert (res == FALSE || handle->state == PF_NEED_DATA);+  g_mutex_unlock (handle->lock);+  g_print ("- put_file_handle_wirte\n");+  return res;+} -  /* -   * Doesn't work with apache > 2.2.9-   * soup_message_headers_append (put_msg->request_headers, "If-None-Match", "*");-   */-  stream = soup_output_stream_new (op_backend->session, put_msg, -1);-  g_object_unref (put_msg);+static gboolean+put_file_handle_close (PutFileHandle  *handle,+                       GCancellable   *cancellable,+                       GError        **error)+{+  gboolean res; -  g_vfs_job_open_for_write_set_handle (G_VFS_JOB_OPEN_FOR_WRITE (job), stream);-  g_vfs_job_succeeded (job);-}  +  g_print ("+ put_file_handle_close\n");+  g_mutex_lock (handle->lock);+  g_print ("  put_file_handle_close: have_lock\n"); -static gboolean-try_create (GVfsBackend *backend,-            GVfsJobOpenForWrite *job,-            const char *filename,-            GFileCreateFlags flags)+restart:+  switch (handle->state)+    {+    case PF_CLOSED:+      if ((res = handle->error == NULL) == FALSE)+        g_propagate_error (error, handle->error);+      break;++    case PF_NEED_DATA:+      handle->state = PF_CLOSE;+      /* fallthrough */+      g_cond_signal (handle->cond);++    default:+      g_cond_wait (handle->cond, handle->lock);+      goto restart;+    }++  g_mutex_unlock (handle->lock);+  g_print ("- put_file_handle_close\n");+  return res;+}++static void+put_done (SoupSession *session,+          SoupMessage *msg,+          gpointer     user_data) {-  SoupMessage *msg;-  SoupURI     *uri;+  PutFileHandle *handle;+  handle = (PutFileHandle *) user_data; -  /* TODO: if SoupOutputStream supported chunked requests, we could-   * use a PUT with "If-None-Match: *" and "Expect: 100-continue"-   */-  uri = http_backend_uri_for_filename (backend, filename, FALSE);-  msg = soup_message_new_from_uri (SOUP_METHOD_HEAD, uri);-  soup_uri_free (uri);+  g_print ("+ put_done\n"); -  g_vfs_job_set_backend_data (G_VFS_JOB (job), backend, NULL);+  g_mutex_lock (handle->lock);+  g_print ("  put_done: have lock\n");++  if (! SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))+    g_set_error (&handle->error, G_IO_ERROR,+                 http_error_code_from_status (msg->status_code),+                 "%s", msg->reason_phrase);++  handle->state = PF_CLOSED;++  /* XXX Set the etag */++  g_cond_signal (handle->cond);+  g_mutex_unlock (handle->lock);+  +  if (handle->job)+    {+      /* This probably means we didn't even get far at all +       */+      g_vfs_job_failed (G_VFS_JOB (handle->job),+                        G_IO_ERROR,+                        http_error_code_from_status (msg->status_code),+                        "%s", msg->reason_phrase);+      put_file_handle_free (handle);+    }++  g_print ("- put_done\n"); -  http_backend_queue_message (backend, msg, try_create_tested_existence, job);-  return TRUE; } -/* *** replace () *** */ static void-open_for_replace_succeeded (GVfsBackendHttp *op_backend, GVfsJob *job,-                            SoupURI *uri, const char *etag)+put_wrote_chunk (SoupMessage *msg,+                 gpointer     user_data) {-  SoupMessage     *put_msg;-  GOutputStream   *stream;+  PutFileHandle *handle = (PutFileHandle *) user_data;++  g_print ("+ wrote chunk\n");+  g_mutex_lock (handle->lock);+  g_print ("  worte_chunk: have_lock %d\n", handle->state); -  put_msg = soup_message_new_from_uri (SOUP_METHOD_PUT, uri);+restart:+  switch (handle->state)+    { -  if (etag)-    soup_message_headers_append (put_msg->request_headers, "If-Match", etag);+    case PF_NEED_DATA:+      g_cond_wait (handle->cond, handle->lock);+      goto restart;++    case PF_HAVE_DATA:+      soup_message_body_append (msg->request_body,+                                SOUP_MEMORY_STATIC,+                                handle->data,+                                handle->data_len);+      handle->state = PF_DATA_WRITTEN;+      break;++    case PF_DATA_WRITTEN:+      g_cond_signal (handle->cond);+      /* Does that work ? */+      g_print ("  worte_chunk: Does taht work?\n");+      g_cond_wait (handle->cond, handle->lock);+      goto restart;++    case PF_CLOSE:+      soup_message_body_complete (msg->request_body);+      handle->state = PF_CLOSE_WAIT;+      /* Will signal the cond from put_done */+      break;++    default:+      g_assert_not_reached ();+    } -  stream = soup_output_stream_new (op_backend->session, put_msg, -1);-  g_object_unref (put_msg);+  g_assert (handle->state == PF_DATA_WRITTEN ||+            handle->state == PF_CLOSE_WAIT); -  g_vfs_job_open_for_write_set_handle (G_VFS_JOB_OPEN_FOR_WRITE (job), stream);-  g_vfs_job_succeeded (job);+  g_mutex_unlock (handle->lock);+  g_print ("- wrote chunk\n"); } + static void-try_replace_checked_etag (SoupSession *session, SoupMessage *msg,-                          gpointer user_data)+put_got_informational (SoupMessage *msg,+                       gpointer     user_data) {-  GVfsJob *job = G_VFS_JOB (user_data);-  GVfsBackendHttp *op_backend = job->backend_data;+  PutFileHandle *handle = (PutFileHandle *) user_data;++  g_print ("got informational %u\n", msg->status_code); -  if (msg->status_code == SOUP_STATUS_PRECONDITION_FAILED)+  if (SOUP_STATUS_IS_INFORMATIONAL (msg->status_code))     {-      g_vfs_job_failed (G_VFS_JOB (job),-                        G_IO_ERROR,-                        G_IO_ERROR_WRONG_ETAG,-                        _("The file was externally modified"));+      g_print ("Is informational \n");+      if (handle->job)+        {+          g_vfs_job_succeeded (handle->job);+          handle->job = NULL;+        }+    }+  else+    {+      /* Makr the body as done just to be sure that we don't end+       * up in some strange situations. In other words make sure+       * put_done () gets called soon+       * NB: job_done will find the handle->job and complete it+       * there+       */++      handle->state = PF_CLOSE_WAIT;+      soup_message_body_complete (msg->request_body);       return;     }-  /* TODO: other errors */ -  open_for_replace_succeeded (op_backend, job, soup_message_get_uri (msg),-                              soup_message_headers_get (msg->request_headers, "If-Match"));-}  +  handle->state = PF_NEED_DATA;+  /* Initiate the first chunk */+  put_wrote_chunk (msg, user_data);+} -static gboolean-try_replace (GVfsBackend *backend,-             GVfsJobOpenForWrite *job,-             const char *filename,-             const char *etag,-             gboolean make_backup,-             GFileCreateFlags flags)+static SoupMessage * +put_message_new (SoupURI       *uri,+                 PutFileHandle *handle) {-  GVfsBackendHttp *op_backend;-  SoupURI         *uri;+  SoupMessage *msg; -  /* TODO: if SoupOutputStream supported chunked requests, we could-   * use a PUT with "If-Match: ..." and "Expect: 100-continue"-   */+  msg = soup_message_new_from_uri (SOUP_METHOD_PUT, uri); -  op_backend = G_VFS_BACKEND_HTTP (backend); -  if (make_backup)-    {-      g_vfs_job_failed (G_VFS_JOB (job),-                        G_IO_ERROR,-                        G_IO_ERROR_CANT_CREATE_BACKUP,-                        _("Backup file creation failed"));-      return TRUE;-    }+  soup_message_headers_set_expectations (msg->request_headers,+                                         SOUP_EXPECTATION_CONTINUE);++  g_signal_connect (msg, "got-informational",+                    G_CALLBACK (put_got_informational), handle);++  g_signal_connect (msg, "wrote-chunk",+                    G_CALLBACK (put_wrote_chunk), handle); +  soup_message_body_set_accumulate (msg->request_body, FALSE);+  soup_message_headers_set_encoding (msg->request_headers,+                                     SOUP_ENCODING_CHUNKED);++  return msg;+} +/* *** create () *** */+static void+do_create (GVfsBackend         *backend,+           GVfsJobOpenForWrite *job,+           const char          *filename,+           GFileCreateFlags     flags)+{+  PutFileHandle *handle;+  SoupMessage   *msg;+  SoupURI       *uri;+  gboolean       can_conditional;    uri = http_backend_uri_for_filename (backend, filename, FALSE); -  if (etag)-    {-      SoupMessage *msg;+  can_conditional = FALSE; /* XXX: need a way to detect server caps */ +  if (can_conditional == FALSE)+    {+      guint status;       msg = soup_message_new_from_uri (SOUP_METHOD_HEAD, uri);-      soup_uri_free (uri);-      soup_message_headers_append (msg->request_headers, "If-Match", etag);+      status = g_vfs_backend_dav_send_message (backend, msg);++      g_object_unref (msg); -      g_vfs_job_set_backend_data (G_VFS_JOB (job), op_backend, NULL);-      soup_session_queue_message (op_backend->session, msg,-                                  try_replace_checked_etag, job);-      return TRUE;+      if (SOUP_STATUS_IS_SUCCESSFUL (status))+        {+          g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,+                            G_IO_ERROR_EXISTS,+                            _("Target file already exists"));+          soup_uri_free (uri);+          return;+        }     }+ +  handle = put_file_handle_new (G_VFS_JOB (job));+  handle->session = g_vfs_backend_dav_get_session (backend);+  g_vfs_job_open_for_write_set_handle (job, handle); -  open_for_replace_succeeded (op_backend, G_VFS_JOB (job), uri, NULL);-  soup_uri_free (uri);-  return TRUE;+  msg = put_message_new (uri, handle);++  if (can_conditional == TRUE) +    message_add_if_match_header (msg, "*", FALSE);++  g_vfs_backend_dav_queue_message (backend, msg, put_done, handle); }+  -/* *** write () *** */+/* *** replace () *** */ static void-write_ready (GObject      *source_object,-             GAsyncResult *result,-             gpointer      user_data)+do_replace (GVfsBackend         *backend,+            GVfsJobOpenForWrite *job,+            const char          *filename,+            const char          *etag,+            gboolean             make_backup,+            GFileCreateFlags     flags) {-  GOutputStream *stream;-  GVfsJob       *job;++  PutFileHandle *handle;+  SoupMessage   *msg;+  SoupURI       *uri;+  gboolean       res;   GError        *error;-  gssize         nwrote; -  stream = G_OUTPUT_STREAM (source_object); -  error  = NULL;-  job    = G_VFS_JOB (user_data);+  error = NULL;+  uri = http_backend_uri_for_filename (backend, filename, FALSE); -  nwrote = g_output_stream_write_finish (stream, result, &error);+  if (make_backup)+    {+      res = dav_make_backup (backend, uri, etag, &error); -  if (nwrote < 0)-   {-     g_vfs_job_failed (G_VFS_JOB (job),-                       error->domain,-                       error->code,-                       error->message);+      if (res == FALSE)+        {+          g_vfs_job_failed_from_error (G_VFS_JOB (job), error);+          g_error_free (error);+          return;+        }+    } -     g_error_free (error);-     return;-   } -  g_vfs_job_write_set_written_size (G_VFS_JOB_WRITE (job), nwrote);-  g_vfs_job_succeeded (job);-}+  handle = put_file_handle_new (G_VFS_JOB (job));+  handle->session = g_vfs_backend_dav_get_session (backend);+  g_vfs_job_open_for_write_set_handle (job, handle); -static gboolean-try_write (GVfsBackend *backend,-           GVfsJobWrite *job,-           GVfsBackendHandle handle,-           char *buffer,-           gsize buffer_size)-{-  GOutputStream   *stream;+  msg = put_message_new (uri, handle); -  stream = G_OUTPUT_STREAM (handle);+  message_add_if_match_header (msg, etag, FALSE); -  g_output_stream_write_async (stream,-                               buffer,-                               buffer_size,-                               G_PRIORITY_DEFAULT,-                               G_VFS_JOB (job)->cancellable,-                               write_ready,-                               job);-  return TRUE;+  g_vfs_backend_dav_queue_message (backend, msg, put_done, handle); } -/* *** close_write () *** */+/* *** write () *** */ static void-close_write_ready (GObject      *source_object,-                   GAsyncResult *result,-                   gpointer      user_data)+do_write (GVfsBackend       *backend,+          GVfsJobWrite      *job,+          GVfsBackendHandle  bhandle,+          char              *buffer,+          gsize              count) {-  GOutputStream *stream;-  GVfsJob       *job;-  GError        *error;+  PutFileHandle *handle;   gboolean       res;-+  GError        *error;+  +  handle = (PutFileHandle *) bhandle;   error = NULL;-  job = G_VFS_JOB (user_data);-  stream = G_OUTPUT_STREAM (source_object);-  res = g_output_stream_close_finish (stream,-                                      result,-                                      &error);++  res = put_file_handle_wirte (handle,+                               buffer,+                               count,+                               G_VFS_JOB (job)->cancellable,+                               &error);+   if (res == FALSE)     {-      g_vfs_job_failed (G_VFS_JOB (job),-                        error->domain,-                        error->code,-                        error->message);-+      g_vfs_job_failed_from_error (G_VFS_JOB (job), error);       g_error_free (error);     }   else-    g_vfs_job_succeeded (job);+    {+      g_vfs_job_write_set_written_size (G_VFS_JOB_WRITE (job), count);+      g_vfs_job_succeeded (G_VFS_JOB (job));+    } -  g_object_unref (stream);+  return; } -static gboolean-try_close_write (GVfsBackend *backend,-                 GVfsJobCloseWrite *job,-                 GVfsBackendHandle handle)++static void+do_close_write (GVfsBackend       *backend,+                GVfsJobCloseWrite *job,+                GVfsBackendHandle  _handle) {-  GOutputStream   *stream;+  PutFileHandle *handle;+  gboolean       res;+  GError        *error; -  stream = G_OUTPUT_STREAM (handle);+  handle = (PutFileHandle *) _handle; -  g_output_stream_close_async (stream,-                               G_PRIORITY_DEFAULT,+  res = put_file_handle_close (handle,                                G_VFS_JOB (job)->cancellable,-                               close_write_ready,-                               job);+                               &error); -  return TRUE;+  if (res == FALSE)+    {+      g_vfs_job_failed_from_error (G_VFS_JOB (job), error);+      g_error_free (error);+    }+  else+    {+      if (handle->etag)+        g_vfs_job_close_write_set_etag (job, handle->etag);+      g_vfs_job_succeeded (G_VFS_JOB (job));+    }++  put_file_handle_free (handle); } +/* *** make_directory () *** */ static void do_make_directory (GVfsBackend          *backend,                    GVfsJobMakeDirectory *job,@@ -2033,6 +2328,7 @@ do_make_directory (GVfsBackend          *backend,   g_object_unref (msg); } +/* *** delete () **** */ static void do_delete (GVfsBackend   *backend,            GVfsJobDelete *job,@@ -2171,10 +2467,10 @@ g_vfs_backend_dav_class_init (GVfsBackendDavClass *klass)   backend_class->try_query_info    = NULL;   backend_class->query_info        = do_query_info;   backend_class->enumerate         = do_enumerate;-  backend_class->try_create        = try_create;-  backend_class->try_replace       = try_replace;-  backend_class->try_write         = try_write;-  backend_class->try_close_write   = try_close_write;+  backend_class->create            = do_create;+  backend_class->replace           = do_replace;+  backend_class->write             = do_write;+  backend_class->close_write       = do_close_write;   backend_class->make_directory    = do_make_directory;   backend_class->delete            = do_delete;   backend_class->set_display_name  = do_set_display_name;