rendered paste bodydiff --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;