[gvfs] dav: Rewrite to libsoup async API to fix crashes



commit 9203fad575515fba715fcfc0cc8b08d01ef11737
Author: Daniel Kolesa <dkolesa igalia com>
Date:   Sun Mar 27 16:33:55 2022 +0200

    dav: Rewrite to libsoup async API to fix crashes
    
    Since libsoup3 cannot deal with threads, we cannot use the do_
    methods which execute in a thread pool. However, we can implement
    these in an async manner, which will bypass the thread pool and
    get rid of the issue.
    
    The write methods are left synchronous as they deal with a memory
    output stream and do not actually call libsoup. Therefore, we do
    not have to care whether they are threaded or not.
    
    Fixes: https://gitlab.gnome.org/GNOME/gvfs/-/issues/609

 daemon/gvfsbackenddav.c | 1958 +++++++++++++++++++++++++++++------------------
 1 file changed, 1211 insertions(+), 747 deletions(-)
---
diff --git a/daemon/gvfsbackenddav.c b/daemon/gvfsbackenddav.c
index 3ece9a5f..fd58b2c9 100644
--- a/daemon/gvfsbackenddav.c
+++ b/daemon/gvfsbackenddav.c
@@ -110,6 +110,7 @@ struct _GVfsBackendDav
   GVfsBackendHttp parent_instance;
 
   MountAuthData auth_info;
+  gchar *last_good_path;
 
   /* Used for user-verified secure connections. */
   GTlsCertificate *certificate;
@@ -378,13 +379,86 @@ g_vfs_backend_dav_stream_skip (GInputStream *stream, GError **error)
   return TRUE;
 }
 
-/* redirection */
-static GInputStream *
-g_vfs_backend_dav_redirect (SoupSession  *session,
-                            SoupMessage  *msg,
-                            GError      **error)
+static void
+g_vfs_backend_dav_setup_display_name (GVfsBackend *backend)
+{
+  GVfsBackendDav *dav_backend;
+  GUri           *mount_base;
+  char           *display_name;
+  char            port[7] = {0, };
+  gint            gport;
+
+  dav_backend = G_VFS_BACKEND_DAV (backend);
+
+#ifdef HAVE_AVAHI
+  if (dav_backend->resolver != NULL)
+    {
+      const char *name;
+      name = g_vfs_dns_sd_resolver_get_service_name (dav_backend->resolver);
+      g_vfs_backend_set_display_name (backend, name);
+      return;
+    }
+#endif
+
+  mount_base = http_backend_get_mount_base (backend);
+
+  gport = g_uri_get_port (mount_base);
+  if ((gport > 0) && (gport != 80) && (gport != 443))
+    g_snprintf (port, sizeof (port), ":%u", g_uri_get_port (mount_base));
+
+  if (g_uri_get_user (mount_base) != NULL)
+    /* Translators: This is the name of the WebDAV share constructed as
+       "WebDAV as <username> on <hostname>:<port>"; the ":<port>" part is
+       the second %s and only shown if it is not the default http(s) port. */
+    display_name = g_strdup_printf (_("%s on %s%s"),
+                                    g_uri_get_user (mount_base),
+                                    g_uri_get_host (mount_base),
+                                    port);
+  else
+    display_name = g_strdup_printf ("%s%s",
+                                    g_uri_get_host (mount_base),
+                                    port);
+
+  g_vfs_backend_set_display_name (backend, display_name);
+  g_free (display_name);
+}
+
+static gboolean
+accept_certificate (SoupMessage *msg,
+                    GTlsCertificate *certificate,
+                    GTlsCertificateFlags errors,
+                    gpointer user_data)
+{
+  GVfsBackendDav *dav = G_VFS_BACKEND_DAV (user_data);
+
+  return (errors == dav->certificate_errors &&
+          g_tls_certificate_is_same (certificate, dav->certificate));
+}
+
+static void
+dav_message_connect_signals (SoupMessage *message, GVfsBackend *backend)
+{
+  GVfsBackendDav *dav_backend = G_VFS_BACKEND_DAV (backend);
+
+  /* we always have to connect this as the message can be
+   * re-sent with a differently set certificate_errors field
+   */
+  g_signal_connect (message, "accept-certificate",
+                    G_CALLBACK (accept_certificate), backend);
+
+  g_signal_connect (message, "authenticate",
+                    G_CALLBACK (soup_authenticate),
+                    &dav_backend->auth_info);
+}
+
+static void
+dav_send_async_with_redir_cb (GObject *source, GAsyncResult *ret, gpointer user_data)
 {
-  GInputStream *res;
+  SoupSession *session = SOUP_SESSION (source);
+  SoupMessage *msg = soup_session_get_async_result_message (session, ret);
+  GInputStream *body;
+  GError *error = NULL;
+  GTask *task = user_data;
   const char *new_loc;
   GUri *new_uri;
   GUri *old_uri;
@@ -392,26 +466,28 @@ g_vfs_backend_dav_redirect (SoupSession  *session,
   guint status;
   gboolean redirect;
 
-  res = soup_session_send (session, msg, NULL, error);
-  if (!res)
-    return NULL;
+  body = soup_session_send_finish (session, ret, &error);
+
+  if (!body)
+    goto return_error;
 
   status = soup_message_get_status (msg);
+
   if (!SOUP_STATUS_IS_REDIRECTION (status))
-    return res;
+    goto return_body;
 
   new_loc = soup_message_headers_get_one (soup_message_get_response_headers (msg),
                                           "Location");
   if (new_loc == NULL)
-    return res;
+    goto return_body;
 
   old_uri = soup_message_get_uri (msg);
   new_uri = g_uri_parse_relative (old_uri, new_loc,
-                                  SOUP_HTTP_URI_FLAGS, error);
+                                  SOUP_HTTP_URI_FLAGS, &error);
   if (new_uri == NULL)
     {
-      g_object_unref (res);
-      return NULL;
+      g_object_unref (body);
+      goto return_error;
     }
 
   tmp = new_uri;
@@ -475,125 +551,77 @@ g_vfs_backend_dav_redirect (SoupSession  *session,
   if (!redirect)
     {
       g_uri_unref (new_uri);
-      return res;
+      goto return_body;
     }
 
-  if (!g_vfs_backend_dav_stream_skip (res, error))
+  if (!g_vfs_backend_dav_stream_skip (body, &error))
     {
-      g_object_unref (res);
-      return NULL;
+      g_object_unref (body);
+      goto return_error;
     }
 
-  g_object_unref (res);
+  g_object_unref (body);
 
   soup_message_set_uri (msg, new_uri);
+  g_uri_unref (new_uri);
 
-  return g_vfs_backend_dav_redirect (session, msg, error);
-}
-
-static void
-g_vfs_backend_dav_setup_display_name (GVfsBackend *backend)
-{
-  GVfsBackendDav *dav_backend;
-  GUri           *mount_base;
-  char           *display_name;
-  char            port[7] = {0, };
-  gint            gport;
-
-  dav_backend = G_VFS_BACKEND_DAV (backend);
-
-#ifdef HAVE_AVAHI
-  if (dav_backend->resolver != NULL)
-    {
-      const char *name;
-      name = g_vfs_dns_sd_resolver_get_service_name (dav_backend->resolver);
-      g_vfs_backend_set_display_name (backend, name);
-      return;
-    }
-#endif
-
-  mount_base = http_backend_get_mount_base (backend);
-
-  gport = g_uri_get_port (mount_base);
-  if ((gport > 0) && (gport != 80) && (gport != 443))
-    g_snprintf (port, sizeof (port), ":%u", g_uri_get_port (mount_base));
-
-  if (g_uri_get_user (mount_base) != NULL)
-    /* Translators: This is the name of the WebDAV share constructed as
-       "WebDAV as <username> on <hostname>:<port>"; the ":<port>" part is
-       the second %s and only shown if it is not the default http(s) port. */
-    display_name = g_strdup_printf (_("%s on %s%s"),
-                                    g_uri_get_user (mount_base),
-                                    g_uri_get_host (mount_base),
-                                    port);
-  else
-    display_name = g_strdup_printf ("%s%s",
-                                    g_uri_get_host (mount_base),
-                                    port);
+  /* recurse */
+  soup_session_send_async (session, msg, G_PRIORITY_DEFAULT, NULL,
+                           dav_send_async_with_redir_cb, g_object_ref (task));
+  goto return_done;
 
-  g_vfs_backend_set_display_name (backend, display_name);
-  g_free (display_name);
-}
+return_body:
+  g_task_return_pointer (task, body, g_object_unref);
+  goto return_done;
 
-static gboolean
-accept_certificate (SoupMessage         *msg,
-                    GTlsCertificate     *certificate,
-                    GTlsCertificateFlags errors,
-                    gpointer             user_data)
-{
-  GVfsBackendDav *dav = G_VFS_BACKEND_DAV (user_data);
+return_error:
+  g_task_return_error (task, error);
 
-  return (errors == dav->certificate_errors &&
-          g_tls_certificate_is_same (certificate, dav->certificate));
+return_done:
+  g_object_unref (task);
 }
 
 static void
-dav_message_connect_signals (SoupMessage *message, GVfsBackend *backend)
+g_vfs_backend_dav_send_async (GVfsBackend *backend,
+                              SoupMessage *message,
+                              GAsyncReadyCallback callback,
+                              gpointer user_data)
 {
-  GVfsBackendDav *dav_backend = G_VFS_BACKEND_DAV (backend);
+  SoupSession *session = G_VFS_BACKEND_HTTP (backend)->session;
+  GTask *task = g_task_new (backend, NULL, callback, user_data);
 
-  /* we always have to connect this as the message can be
-   * re-sent with a differently set certificate_errors field
-   */
-  g_signal_connect (message, "accept-certificate",
-                    G_CALLBACK (accept_certificate), backend);
+  g_task_set_source_tag (task, g_vfs_backend_dav_send_async);
+  g_task_set_task_data (task, g_object_ref (message), g_object_unref);
 
-  g_signal_connect (message, "authenticate",
-                    G_CALLBACK (soup_authenticate),
-                    &dav_backend->auth_info);
+  soup_message_set_flags (message, SOUP_MESSAGE_NO_REDIRECT);
+
+  soup_session_send_async (session, message, G_PRIORITY_DEFAULT, NULL,
+                           dav_send_async_with_redir_cb, task);
 }
 
 static GInputStream *
-g_vfs_backend_dav_send (GVfsBackend *backend,
-                        SoupMessage *message,
-                        gboolean     cb_connect,
-                        GError     **error)
+g_vfs_backend_dav_send_finish (GVfsBackend   *backend,
+                               GAsyncResult  *result,
+                               GError       **error)
 {
-  SoupSession *session = G_VFS_BACKEND_HTTP (backend)->session;
-
-  /* We have our own custom redirect handler */
-  soup_message_set_flags (message, SOUP_MESSAGE_NO_REDIRECT);
-
-  if (cb_connect)
-    dav_message_connect_signals (message, backend);
+  g_return_val_if_fail (G_VFS_IS_BACKEND (backend), NULL);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
+  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+  g_return_val_if_fail (g_task_is_valid (result, backend), NULL);
+  g_return_val_if_fail (g_async_result_is_tagged (result, g_vfs_backend_dav_send_async), NULL);
 
-  return g_vfs_backend_dav_redirect (session, message, error);
+  return g_task_propagate_pointer (G_TASK (result), error);
 }
 
-static void
-g_vfs_backend_dav_send_async (GVfsBackend *backend,
-                              SoupMessage *message,
-                              gboolean     cb_connect,
-                              GAsyncReadyCallback callback,
-                              gpointer user_data)
+static SoupMessage *
+g_vfs_backend_dav_get_async_result_message (GVfsBackend  *backend,
+                                            GAsyncResult *result)
 {
-  SoupSession *session = G_VFS_BACKEND_HTTP (backend)->session;
-
-  if (cb_connect)
-    dav_message_connect_signals (message, backend);
+  g_return_val_if_fail (G_VFS_IS_BACKEND (backend), NULL);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
+  g_return_val_if_fail (g_task_is_valid (result, backend), NULL);
 
-  soup_session_send_async (session, message, G_PRIORITY_DEFAULT, NULL,
-                           callback, user_data);
+  return g_task_get_task_data (G_TASK (result));
 }
 
 /* ************************************************************************* */
@@ -1365,8 +1393,8 @@ propfind_request_new (GVfsBackend     *backend,
 }
 
 static SoupMessage *
-stat_location_begin (GUri      *uri,
-                     gboolean   count_children)
+stat_location_start (GUri *uri,
+                     gboolean count_children)
 {
   SoupMessage       *msg;
   const char        *depth;
@@ -1406,11 +1434,11 @@ stat_location_begin (GUri      *uri,
 }
 
 static gboolean
-stat_location_finish (SoupMessage  *msg,
-                      GInputStream *body,
-                      GFileType    *target_type,
-                      gint64       *target_size,
-                      guint        *num_children)
+stat_location_end (SoupMessage *msg,
+                   GInputStream *body,
+                   GFileType *target_type,
+                   gint64 *target_size,
+                   guint *num_children)
 {
   Multistatus  ms;
   xmlNodeIter  iter;
@@ -1465,56 +1493,143 @@ stat_location_finish (SoupMessage  *msg,
   return res;
 }
 
-static gboolean
-stat_location (GVfsBackend  *backend,
-               GUri         *uri,
-               GFileType    *target_type,
-               gint64       *target_size,
-               guint        *num_children,
-               GError      **error)
+typedef struct _StatLocationData {
+  GUri *uri;
+  GFileType target_type;
+  gint64 target_size;
+  guint num_children;
+} StatLocationData;
+
+static void
+stat_location_data_free (gpointer p)
 {
-  SoupMessage *msg;
+  g_uri_unref (((StatLocationData *)p)->uri);
+  g_slice_free (StatLocationData, p);
+}
+
+static void
+stat_location_cb (GObject *source, GAsyncResult *result, gpointer user_data)
+{
+  GVfsBackend *backend = G_VFS_BACKEND (source);
+  SoupMessage *msg = g_vfs_backend_dav_get_async_result_message (backend, result);
+  GTask *task = user_data;
+  StatLocationData *data = g_task_get_task_data (task);
   GInputStream *body;
+  GError *error = NULL;
   guint status;
-  gboolean count_children;
   gboolean res;
 
-  count_children = num_children != NULL;
-  msg = stat_location_begin (uri, count_children);
-  if (msg == NULL)
-    return FALSE;
-
-  body = g_vfs_backend_dav_send (backend, msg, TRUE, error);
+  body = g_vfs_backend_dav_send_finish (backend, result, &error);
 
   if (!body)
     {
       g_object_unref (msg);
-      return FALSE;
+      g_task_return_error (task, error);
+      g_object_unref (task);
+      return;
     }
 
   status = soup_message_get_status (msg);
   if (status != SOUP_STATUS_MULTI_STATUS)
     {
-      g_set_error_literal (error,
-                           G_IO_ERROR,
+      error = g_error_new (G_IO_ERROR,
                            http_error_code_from_status (status),
+                           _("HTTP Error: %s"),
                            soup_message_get_reason_phrase (msg));
 
       g_object_unref (msg);
+      g_task_return_error (task, error);
       g_object_unref (body);
-      return FALSE;
+      g_object_unref (task);
+      return;
     }
 
-  res = stat_location_finish (msg, body, target_type, target_size, num_children);
+  res = stat_location_end (msg, body, &data->target_type,
+                           &data->target_size, &data->num_children);
   g_object_unref (msg);
   g_object_unref (body);
 
   if (res == FALSE)
-    g_set_error_literal (error,
-                         G_IO_ERROR, G_IO_ERROR_FAILED,
-                         _("Response invalid"));
+    {
+      error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED,
+                           _("Response invalid"));
+      g_task_return_error (task, error);
+    }
+  else
+    g_task_return_boolean (task, TRUE);
+
+  g_object_unref (task);
+}
+
+static void
+stat_location_async (GVfsBackend *backend,
+                     GUri *uri,
+                     gboolean count_children,
+                     GAsyncReadyCallback callback,
+                     gpointer user_data)
+{
+  GTask *task;
+  SoupMessage *msg;
+  StatLocationData *data;
+
+  task = g_task_new (backend, NULL, callback, user_data);
+  data = g_slice_new (StatLocationData);
+  data->uri = g_uri_ref (uri);
+
+  g_task_set_source_tag (task, stat_location_async);
+  g_task_set_task_data (task, data, stat_location_data_free);
+
+  msg = stat_location_start (uri, count_children);
+  if (msg == NULL)
+    {
+      g_task_return_boolean (task, FALSE);
+      g_object_unref (task);
+      return;
+    }
+
+  dav_message_connect_signals (msg, backend);
+
+  g_vfs_backend_dav_send_async (backend, msg, stat_location_cb, task);
+}
+
+static gboolean
+stat_location_finish (GVfsBackend *backend,
+                      GFileType *target_type,
+                      gint64 *target_size,
+                      guint *num_children,
+                      GAsyncResult *result,
+                      GError **error)
+{
+  g_return_val_if_fail (G_VFS_IS_BACKEND (backend), FALSE);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+  g_return_val_if_fail (g_task_is_valid (result, backend), FALSE);
+  g_return_val_if_fail (g_async_result_is_tagged (result, stat_location_async), FALSE);
+
+   if (g_task_propagate_boolean (G_TASK (result), error))
+     {
+       StatLocationData *data = g_task_get_task_data (G_TASK (result));
+       if (target_type) *target_type = data->target_type;
+       if (target_size) *target_size = data->target_size;
+       if (num_children) *num_children = data->num_children;
+       return TRUE;
+     }
+
+  return FALSE;
+}
+
+static GUri *
+stat_location_async_get_uri (GVfsBackend *backend, GAsyncResult *result)
+{
+  StatLocationData *data;
+
+  g_return_val_if_fail (G_VFS_IS_BACKEND (backend), NULL);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
+  g_return_val_if_fail (g_task_is_valid (result, backend), NULL);
+
+  data = g_task_get_task_data (G_TASK (result));
+  return data->uri;
 
-  return res;
 }
 
 /* ************************************************************************* */
@@ -1930,305 +2045,374 @@ dns_sd_resolver_changed (GVfsDnsSdResolver *resolver,
 
 /* ************************************************************************* */
 /* Backend Functions */
+
 static void
-do_mount (GVfsBackend  *backend,
-          GVfsJobMount *job,
-          GMountSpec   *mount_spec,
-          GMountSource *mount_source,
-          gboolean      is_automount)
+mount_success (GVfsBackend *backend, GVfsJob *job)
 {
   GVfsBackendDav *dav_backend = G_VFS_BACKEND_DAV (backend);
-  MountAuthData  *data;
-  SoupSession    *session;
-  SoupMessage    *msg_opts;
-  SoupMessage    *msg_stat;
-  GUri           *mount_base;
-  GUri           *tmp;
-  GError         *error = NULL;
-  guint           status;
-  gboolean        is_success;
-  gboolean        is_webdav;
-  gboolean        is_collection;
-  gboolean        sig_opts = TRUE;
-  gboolean        sig_stat = TRUE;
-  gboolean        res;
-  char           *last_good_path;
-  const char     *host;
-  const char     *type;
+  GVfsBackendHttp *http_backend = G_VFS_BACKEND_HTTP (backend);
+  GMountSpec *mount_spec;
+  GUri *tmp;
 
-  g_debug ("+ mount\n");
+  /* Save the auth info in the keyring */
+  keyring_save_authinfo (&(dav_backend->auth_info.server_auth), http_backend->mount_base, FALSE);
+  /* TODO: save proxy auth */
 
-  host = g_mount_spec_get (mount_spec, "host");
-  type = g_mount_spec_get (mount_spec, "type");
+  /* Set the working path in mount path */
+  tmp = http_backend->mount_base;
+  http_backend->mount_base = soup_uri_copy (tmp, SOUP_URI_PATH,
+                                            dav_backend->last_good_path,
+                                            SOUP_URI_NONE);
+  g_uri_unref (tmp);
+  g_clear_pointer (&dav_backend->last_good_path, g_free);
 
-#ifdef HAVE_AVAHI
-  /* resolve DNS-SD style URIs */
-  if ((strcmp (type, "dav+sd") == 0 || strcmp (type, "davs+sd") == 0) && host != NULL)
-    {
-      dav_backend->resolver = g_vfs_dns_sd_resolver_new_for_encoded_triple (host, "u");
+  /* dup the mountspec, but only copy known fields */
+  mount_spec = g_mount_spec_from_dav_uri (dav_backend, http_backend->mount_base);
 
-      if (!g_vfs_dns_sd_resolver_resolve_sync (dav_backend->resolver,
-                                               NULL,
-                                               &error))
-        {
-          g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
-          g_error_free (error);
-          return;
-        }
-      g_signal_connect (dav_backend->resolver,
-                        "changed",
-                        (GCallback) dns_sd_resolver_changed,
-                        dav_backend);
+  g_vfs_backend_set_mount_spec (backend, mount_spec);
+  g_vfs_backend_set_icon_name (backend, "folder-remote");
+  g_vfs_backend_set_symbolic_icon_name (backend, "folder-remote-symbolic");
+  
+  g_vfs_backend_dav_setup_display_name (backend);
+  
+  /* cleanup */
+  g_mount_spec_unref (mount_spec);
 
-      mount_base = dav_uri_from_dns_sd_resolver (dav_backend);
-    }
-  else
-#endif
+  g_vfs_job_succeeded (G_VFS_JOB (job));
+  g_debug ("- mount\n");
+}
+
+static void try_mount_opts_cb (GObject *source, GAsyncResult *result, gpointer user_data);
+
+static void
+try_mount_stat_cb (GObject *source, GAsyncResult *result, gpointer user_data)
+{
+  GVfsBackend *backend = G_VFS_BACKEND (source);
+  GVfsBackendDav *dav_backend = G_VFS_BACKEND_DAV (backend);
+  GVfsBackendHttp *http_backend = G_VFS_BACKEND_HTTP (backend);
+  GVfsJobMount *job = user_data;
+  SoupMessage *msg_stat = g_vfs_backend_dav_get_async_result_message (backend, result);
+  SoupMessage *msg_opts;
+  GInputStream *body;
+  GError *error = NULL;
+  GFileType file_type;
+  gboolean res;
+  gboolean is_collection;
+  GUri *tmp;
+  char *new_path;
+
+  body = g_vfs_backend_dav_send_finish (backend, result, &error);
+
+  if (body == NULL)
     {
-      mount_base = g_mount_spec_to_dav_uri (mount_spec);
+      g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+      g_error_free (error);
+      goto clear_msg;
     }
 
-  if (mount_base == NULL)
+  res = stat_location_end (msg_stat, body, &file_type, NULL, NULL);
+  is_collection = res && file_type == G_FILE_TYPE_DIRECTORY;
+
+  if (!g_vfs_backend_dav_stream_skip (body, &error))
     {
-      g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
-                        G_IO_ERROR_INVALID_ARGUMENT,
-                        _("Invalid mount spec"));
-      return;
+      g_object_unref (body);
+      if (dav_backend->last_good_path == NULL)
+        g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+      else
+        mount_success (backend, G_VFS_JOB (job));
+      g_error_free (error);
+      goto clear_msg;
     }
 
-  session = G_VFS_BACKEND_HTTP (backend)->session;
-  G_VFS_BACKEND_HTTP (backend)->mount_base = mount_base;
+  g_object_unref (body);
 
-  soup_session_add_feature_by_type (session, SOUP_TYPE_AUTH_NEGOTIATE);
-  soup_session_add_feature_by_type (session, SOUP_TYPE_AUTH_NTLM);
+  soup_message_headers_clear (soup_message_get_response_headers (msg_stat));
 
-  data = &(G_VFS_BACKEND_DAV (backend)->auth_info); 
-  data->mount_source = g_object_ref (mount_source);
-  data->server_auth.username = g_strdup (g_uri_get_user (mount_base));
-  data->server_auth.pw_save = G_PASSWORD_SAVE_NEVER;
-  data->proxy_auth.pw_save = G_PASSWORD_SAVE_NEVER;
-  data->interactive = TRUE;
+  g_debug (" [%s] webdav: %d, collection %d [res: %d]\n",
+           g_uri_get_path (http_backend->mount_base), TRUE, is_collection, res);
 
-  last_good_path = NULL;
-  msg_opts = soup_message_new_from_uri (SOUP_METHOD_OPTIONS, mount_base);
+  if ((is_collection == FALSE) && (dav_backend->last_good_path != NULL))
+    {
+      mount_success (backend, G_VFS_JOB (job));
+      goto clear_msg;
+    }
+  else if (res == FALSE)
+    {
+      int error_code = http_error_code_from_status (soup_message_get_status (msg_stat));
+      g_vfs_job_failed (G_VFS_JOB (job),
+                        G_IO_ERROR, error_code,
+                        _("HTTP Error: %s"),
+                        soup_message_get_reason_phrase (msg_stat));
+      goto clear_msg;
+    }
+  else if (is_collection == FALSE)
+    {
+      g_vfs_job_failed (G_VFS_JOB (job),
+                        G_IO_ERROR, G_IO_ERROR_FAILED,
+                        _("Could not find an enclosing directory"));
+      goto clear_msg;
+    }
 
-  /* The count_children parameter is intentionally set to TRUE to be sure that
-     enumeration is possible: https://gitlab.gnome.org/GNOME/gvfs/-/issues/468 */
-  msg_stat = stat_location_begin (mount_base, TRUE);
-
-  do {
-    GInputStream *body;
-    GFileType file_type;
-    GUri *cur_uri;
-    char *new_path;
-
-    res = TRUE;
-    body = g_vfs_backend_dav_send (backend, msg_opts, sig_opts, &error);
-    sig_opts = FALSE;
-    status = body ? soup_message_get_status (msg_opts) : SOUP_STATUS_NONE;
-    is_success = body && SOUP_STATUS_IS_SUCCESSFUL (status);
-    is_webdav = sm_has_header (msg_opts, "DAV");
-
-    /* Workaround for servers which response with 403 instead of 401 in case of
-     * wrong credentials to let the user specify its credentials again. */
-    if (status == SOUP_STATUS_FORBIDDEN &&
-        last_good_path == NULL &&
-        (data->server_auth.password != NULL ||
-         data->proxy_auth.password != NULL))
-      {
-        SoupSessionFeature *auth_manager;
+  /* we have found a new good root, try the parent ... */
+  g_free (dav_backend->last_good_path);
+  dav_backend->last_good_path = g_strdup (g_uri_get_path (http_backend->mount_base));
+  new_path = path_get_parent_dir (dav_backend->last_good_path);
 
-        data->retrying_after_403 = TRUE;
+  tmp = http_backend->mount_base;
+  http_backend->mount_base = soup_uri_copy (tmp, SOUP_URI_PATH, new_path, SOUP_URI_NONE);
+  g_uri_unref (tmp);
 
-        g_clear_pointer (&data->server_auth.username, g_free);
-        data->server_auth.username = g_strdup (g_uri_get_user (mount_base));
-        g_clear_pointer (&data->server_auth.password, g_free);
-        g_clear_pointer (&data->proxy_auth.password, g_free);
+  g_free (new_path);
 
-        auth_manager = soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER);
-        soup_auth_manager_clear_cached_credentials (SOUP_AUTH_MANAGER (auth_manager));
+  /* if we have found a root that is good then we assume
+     that we also have obtained to correct credentials
+     and we switch the auth handler. This will prevent us
+     from asking for *different* credentials *again* if the
+     server should response with 401 for some of the parent
+     collections. See also bug #677753 */
+  dav_backend->auth_info.interactive = FALSE;
 
-        g_object_unref (msg_opts);
-        msg_opts = soup_message_new_from_uri (SOUP_METHOD_OPTIONS, mount_base);
+  /* break out */
+  if (g_strcmp0 (dav_backend->last_good_path, "/") == 0)
+    {
+      mount_success (backend, G_VFS_JOB (job));
+      goto clear_msg;
+    }
 
-        continue;
-      }
+  /* else loop the whole thing from options */
 
-    /* If SSL is used and the certificate verifies OK, then ssl-strict remains
-     * on for all further connections.
-     * If SSL is used and the certificate does not verify OK, then the user
-     * gets a chance to override it. If they do, ssl-strict is disabled but
-     * the certificate is stored, and checked on each subsequent connection to
-     * ensure that it hasn't changed. */
-    if (g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE) &&
-        !dav_backend->certificate_errors)
-      {
-        GTlsCertificate *certificate;
-        GTlsCertificateFlags errors;
-
-        certificate = soup_message_get_tls_peer_certificate (msg_opts);
-        errors = soup_message_get_tls_peer_certificate_errors (msg_opts);
-        if (gvfs_accept_certificate (mount_source, certificate, errors))
-          {
-            g_clear_error (&error);
-            dav_backend->certificate = g_object_ref (certificate);
-            dav_backend->certificate_errors = errors;
-            continue;
-          }
-        else
-          {
-            /* break the loop, if last_good_path is NULL then the error is
-             * propagated, otherwise it is cleared later by the else branch
-             */
-            break;
-          }
-      }
+  msg_opts = soup_message_new_from_uri (SOUP_METHOD_OPTIONS,
+                                        http_backend->mount_base);
 
-    if (!is_success || !is_webdav)
-      break;
+  dav_message_connect_signals (msg_opts, backend);
 
-    if (!g_vfs_backend_dav_stream_skip (body, &error))
-      {
-        g_object_unref (body);
-        break;
-      }
+  g_vfs_backend_dav_send_async (backend, msg_opts, try_mount_opts_cb, job);
 
-    g_object_unref (body);
+clear_msg:
+  g_object_unref (msg_stat);
+}
 
-    soup_message_headers_clear (soup_message_get_response_headers (msg_opts));
+static void
+try_mount_opts_cb (GObject *source, GAsyncResult *result, gpointer user_data)
+{
+  GVfsBackend *backend = G_VFS_BACKEND (source);
+  GVfsBackendDav *dav_backend = G_VFS_BACKEND_DAV (backend);
+  GVfsBackendHttp *http_backend = G_VFS_BACKEND_HTTP (backend);
+  GVfsJobMount *job = user_data;
+  SoupMessage *msg_opts = g_vfs_backend_dav_get_async_result_message (backend, result);
+  SoupMessage *msg_stat;
+  GInputStream *body;
+  GError *error = NULL;
+  GUri *cur_uri;
+  guint status;
+  gboolean is_success, is_webdav;
+
+  body = g_vfs_backend_dav_send_finish (backend, result, &error);
+
+  /* If SSL is used and the certificate verifies OK, then ssl-strict remains
+   * on for all further connections.
+   * If SSL is used and the certificate does not verify OK, then the user
+   * gets a chance to override it. If they do, ssl-strict is disabled but
+   * the certificate is stored, and checked on each subsequent connection to
+   * ensure that it hasn't changed. */
+  if (g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE) &&
+      !dav_backend->certificate_errors)
+    {
+      GTlsCertificate *certificate;
+      GTlsCertificateFlags errors;
 
-    cur_uri = soup_message_get_uri (msg_opts);
-    soup_message_set_uri (msg_stat, cur_uri);
+      certificate = soup_message_get_tls_peer_certificate (msg_opts);
+      errors = soup_message_get_tls_peer_certificate_errors (msg_opts);
+      if (gvfs_accept_certificate (dav_backend->auth_info.mount_source, certificate, errors))
+        {
+          g_clear_error (&error);
+          dav_backend->certificate = g_object_ref (certificate);
+          dav_backend->certificate_errors = errors;
 
-    body = g_vfs_backend_dav_send (backend, msg_stat, sig_stat, NULL);
-    sig_stat = FALSE;
-    res = stat_location_finish (msg_stat, body, &file_type, NULL, NULL);
-    is_collection = res && file_type == G_FILE_TYPE_DIRECTORY;
+          /* re-send the opts message; re-create since we're still in its cb */
+          g_object_unref (msg_opts);
+          msg_opts = soup_message_new_from_uri (SOUP_METHOD_OPTIONS,
+                                                http_backend->mount_base);
 
-    if (body && !g_vfs_backend_dav_stream_skip (body, &error))
-      {
-        g_object_unref (body);
-        break;
-      }
+          dav_message_connect_signals (msg_opts, backend);
 
-    g_clear_object (&body);
+          g_vfs_backend_dav_send_async (backend, msg_opts,
+                                        try_mount_opts_cb, job);
+          return;
+        }
+    }
 
-    soup_message_headers_clear (soup_message_get_response_headers (msg_stat));
+  /* message failed, propagate the error */
+  if (!body)
+    {
+      if (dav_backend->last_good_path == NULL)
+        g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+      else
+        mount_success (backend, G_VFS_JOB (job));
+      g_error_free (error);
+      goto clear_msgs;
+    }
 
-    g_debug (" [%s] webdav: %d, collection %d [res: %d]\n",
-             g_uri_get_path (mount_base), is_webdav, is_collection, res);
+  status = soup_message_get_status (msg_opts);
 
-    if (is_collection == FALSE)
-      break;
+  /* Workaround for servers which response with 403 instead of 401 in case of
+   * wrong credentials to let the user specify its credentials again. */
+  if (status == SOUP_STATUS_FORBIDDEN &&
+      dav_backend->last_good_path == NULL &&
+      (dav_backend->auth_info.server_auth.password != NULL ||
+       dav_backend->auth_info.proxy_auth.password != NULL))
+    {
+      SoupSessionFeature *auth_manager;
 
-    /* we have found a new good root, try the parent ... */
-    g_free (last_good_path);
-    last_good_path = g_strdup (g_uri_get_path (mount_base));
-    new_path = path_get_parent_dir (last_good_path);
+      dav_backend->auth_info.retrying_after_403 = TRUE;
 
-    tmp = mount_base;
-    mount_base = soup_uri_copy (mount_base, SOUP_URI_PATH, new_path, SOUP_URI_NONE);
-    g_uri_unref (tmp);
-    G_VFS_BACKEND_HTTP (backend)->mount_base = mount_base;
+      g_clear_pointer (&dav_backend->auth_info.server_auth.username, g_free);
+      dav_backend->auth_info.server_auth.username = g_strdup (g_uri_get_user (http_backend->mount_base));
+      g_clear_pointer (&dav_backend->auth_info.server_auth.password, g_free);
+      g_clear_pointer (&dav_backend->auth_info.proxy_auth.password, g_free);
 
-    g_free (new_path);
+      auth_manager = soup_session_get_feature (http_backend->session, SOUP_TYPE_AUTH_MANAGER);
+      soup_auth_manager_clear_cached_credentials (SOUP_AUTH_MANAGER (auth_manager));
 
-    soup_message_set_uri (msg_opts, mount_base);
+      /* re-send the opts message; re-create since we're still in its cb */
+      g_object_unref (msg_opts);
+      msg_opts = soup_message_new_from_uri (SOUP_METHOD_OPTIONS,
+                                            http_backend->mount_base);
 
-    /* if we have found a root that is good then we assume
-       that we also have obtained to correct credentials
-       and we switch the auth handler. This will prevent us
-       from asking for *different* credentials *again* if the
-       server should response with 401 for some of the parent
-       collections. See also bug #677753 */
-    data->interactive = FALSE;
+      dav_message_connect_signals (msg_opts, backend);
 
-  } while (g_strcmp0 (last_good_path, "/") != 0);
+      g_vfs_backend_dav_send_async (backend, msg_opts, try_mount_opts_cb, job);
+      return;
+    }
 
-  /* we either encountered an error or we have
-     reached the end of paths we are allowed to
-     chdir up to (or couldn't chdir up at all) */
+  is_success = SOUP_STATUS_IS_SUCCESSFUL (status);
+  is_webdav = sm_has_header (msg_opts, "DAV");
 
-  /* check if we at all have a good path */
-  if (last_good_path == NULL)
+  if ((is_success && !is_webdav) || (status == SOUP_STATUS_METHOD_NOT_ALLOWED))
+    {
+      if (dav_backend->last_good_path == NULL)
+        g_vfs_job_failed (G_VFS_JOB (job),
+                          G_IO_ERROR, G_IO_ERROR_FAILED,
+                          _("Not a WebDAV enabled share"));
+      else
+        mount_success (backend, G_VFS_JOB (job));
+      goto clear_msgs;
+    }
+  else if (!is_success)
     {
-      if (error)
+      int error_code = http_error_code_from_status (soup_message_get_status (msg_opts));
+
+      if (dav_backend->last_good_path == NULL)
+        g_vfs_job_failed (G_VFS_JOB (job),
+                          G_IO_ERROR, error_code,
+                          _("HTTP Error: %s"),
+                          soup_message_get_reason_phrase (msg_opts));
+      else
+        mount_success (backend, G_VFS_JOB (job));
+      goto clear_msgs;
+    }
+
+  if (!g_vfs_backend_dav_stream_skip (body, &error))
+    {
+      if (dav_backend->last_good_path == NULL)
+        g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+      else
+        mount_success (backend, G_VFS_JOB (job));
+      g_error_free (error);
+      goto clear_msgs;
+    }
+
+  g_object_unref (body);
+
+  cur_uri = soup_message_get_uri (msg_opts);
+
+  /* The count_children parameter is intentionally set to TRUE to be sure that
+     enumeration is possible: https://gitlab.gnome.org/GNOME/gvfs/-/issues/468 */
+  msg_stat = stat_location_start (cur_uri, TRUE);
+
+  dav_message_connect_signals (msg_stat, backend);
+
+  g_vfs_backend_dav_send_async (backend, msg_stat, try_mount_stat_cb, job);
+
+clear_msgs:
+  g_object_unref (msg_opts);
+}
+
+static gboolean
+try_mount (GVfsBackend  *backend,
+           GVfsJobMount *job,
+           GMountSpec   *mount_spec,
+           GMountSource *mount_source,
+           gboolean      is_automount)
+{
+  GVfsBackendDav  *dav_backend = G_VFS_BACKEND_DAV (backend);
+  GVfsBackendHttp *http_backend = G_VFS_BACKEND_HTTP (backend);
+  SoupMessage     *msg_opts;
+  GUri            *mount_base;
+  const char      *host;
+  const char      *type;
+
+  g_debug ("+ mount\n");
+
+  host = g_mount_spec_get (mount_spec, "host");
+  type = g_mount_spec_get (mount_spec, "type");
+
+#ifdef HAVE_AVAHI
+  /* resolve DNS-SD style URIs */
+  if ((strcmp (type, "dav+sd") == 0 || strcmp (type, "davs+sd") == 0) && host != NULL)
+    {
+      GError *error = NULL;
+      dav_backend->resolver = g_vfs_dns_sd_resolver_new_for_encoded_triple (host, "u");
+
+      if (!g_vfs_dns_sd_resolver_resolve_sync (dav_backend->resolver,
+                                               NULL,
+                                               &error))
         {
           g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
           g_error_free (error);
+          return TRUE;
         }
-      else if ((is_success && !is_webdav) ||
-               soup_message_get_status (msg_opts) == SOUP_STATUS_METHOD_NOT_ALLOWED)
-        {
-          /* This means the either: a) OPTIONS request succeeded
-             (which should be the case even for non-existent
-             resources on a webdav enabled share) but we did not
-             get the DAV header. Or b) the OPTIONS request was a
-             METHOD_NOT_ALLOWED (405).
-             Prioritize this error messages, because it seems most
-             useful to the user. */
-          g_vfs_job_failed (G_VFS_JOB (job),
-                            G_IO_ERROR, G_IO_ERROR_FAILED,
-                            _("Not a WebDAV enabled share"));
-        }
-      else if (!is_success || !res)
-        {
-          /* Either the OPTIONS request (is_success) or the PROPFIND
-             request (res) failed. */
-          SoupMessage *target = !is_success ? msg_opts : msg_stat;
-          int error_code = http_error_code_from_status (soup_message_get_status (target));
-
-          g_vfs_job_failed (G_VFS_JOB (job),
-                            G_IO_ERROR, error_code,
-                            _("HTTP Error: %s"),
-                            soup_message_get_reason_phrase (target));
-        }
-      else
-        {
-          /* This means, we have a valid DAV header, PROPFIND worked,
-             but it is not a collection!  */
-          g_vfs_job_failed (G_VFS_JOB (job),
-                            G_IO_ERROR, G_IO_ERROR_FAILED,
-                            _("Could not find an enclosing directory"));
-        }
+      g_signal_connect (dav_backend->resolver,
+                        "changed",
+                        (GCallback) dns_sd_resolver_changed,
+                        dav_backend);
 
-      g_object_unref (msg_opts);
-      g_object_unref (msg_stat);
+      mount_base = dav_uri_from_dns_sd_resolver (dav_backend);
+    }
+  else
+#endif
+    {
+      mount_base = g_mount_spec_to_dav_uri (mount_spec);
+    }
 
-      return;
+  if (mount_base == NULL)
+    {
+      g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
+                        G_IO_ERROR_INVALID_ARGUMENT,
+                        _("Invalid mount spec"));
+      return TRUE;
     }
-  else if (error)
-    g_error_free (error);
 
-  /* Success! We are mounted */        
-  /* Save the auth info in the keyring */
+  http_backend->mount_base = mount_base;
 
-  keyring_save_authinfo (&(data->server_auth), mount_base, FALSE);
-  /* TODO: save proxy auth */
+  soup_session_add_feature_by_type (http_backend->session,
+                                    SOUP_TYPE_AUTH_NEGOTIATE);
+  soup_session_add_feature_by_type (http_backend->session,
+                                    SOUP_TYPE_AUTH_NTLM);
 
-  /* Set the working path in mount path */
-  tmp = mount_base;
-  mount_base = soup_uri_copy (mount_base, SOUP_URI_PATH, last_good_path, SOUP_URI_NONE);
-  g_uri_unref (tmp);
-  G_VFS_BACKEND_HTTP (backend)->mount_base = mount_base;
-  g_free (last_good_path);
+  dav_backend->auth_info.mount_source = g_object_ref (mount_source);
+  dav_backend->auth_info.server_auth.username = g_strdup (g_uri_get_user (mount_base));
+  dav_backend->auth_info.server_auth.pw_save = G_PASSWORD_SAVE_NEVER;
+  dav_backend->auth_info.proxy_auth.pw_save = G_PASSWORD_SAVE_NEVER;
+  dav_backend->auth_info.interactive = TRUE;
 
-  /* dup the mountspec, but only copy known fields */
-  mount_spec = g_mount_spec_from_dav_uri (dav_backend, mount_base);
+  dav_backend->last_good_path = NULL;
 
-  g_vfs_backend_set_mount_spec (backend, mount_spec);
-  g_vfs_backend_set_icon_name (backend, "folder-remote");
-  g_vfs_backend_set_symbolic_icon_name (backend, "folder-remote-symbolic");
-  
-  g_vfs_backend_dav_setup_display_name (backend);
-  
-  /* cleanup */
-  g_mount_spec_unref (mount_spec);
-  g_object_unref (msg_opts);
-  g_object_unref (msg_stat);
+  msg_opts = soup_message_new_from_uri (SOUP_METHOD_OPTIONS, mount_base);
+  dav_message_connect_signals (msg_opts, backend);
 
-  g_vfs_job_succeeded (G_VFS_JOB (job));
-  g_debug ("- mount\n");
+  g_vfs_backend_dav_send_async (backend, msg_opts, try_mount_opts_cb, job);
+  return TRUE;
 }
 
 static PropName ls_propnames[] = {
@@ -2244,34 +2428,18 @@ static PropName ls_propnames[] = {
 
 /* *** query_info () *** */
 static void
-do_query_info (GVfsBackend           *backend,
-               GVfsJobQueryInfo      *job,
-               const char            *filename,
-               GFileQueryInfoFlags    flags,
-               GFileInfo             *info,
-               GFileAttributeMatcher *matcher)
+try_query_info_cb (GObject *source, GAsyncResult *result, gpointer user_data)
 {
-  SoupMessage *msg;
+  GVfsBackend *backend = G_VFS_BACKEND (source);
+  SoupMessage *msg = g_vfs_backend_dav_get_async_result_message (backend, result);
+  GVfsJobQueryInfo *job = user_data;
   GInputStream *body;
+  GError *error = NULL;
   Multistatus ms;
   xmlNodeIter iter;
   gboolean res;
-  GError *error = NULL;
 
-  g_debug ("Query info %s\n", filename);
-
-  msg = propfind_request_new (backend, filename, 0, ls_propnames);
-  if (msg == NULL)
-    {
-      g_vfs_job_failed (G_VFS_JOB (job),
-                        G_IO_ERROR, G_IO_ERROR_FAILED,
-                        _("Could not create request"));
-      return;
-    }
-
-  message_add_redirect_header (msg, flags);
-
-  body = g_vfs_backend_dav_send (backend, msg, TRUE, &error);
+  body = g_vfs_backend_dav_send_finish (backend, result, &error);
 
   if (!body)
     goto error;
@@ -2318,6 +2486,36 @@ do_query_info (GVfsBackend           *backend,
   g_object_unref (msg);
 }
 
+static gboolean
+try_query_info (GVfsBackend *backend,
+                GVfsJobQueryInfo *job,
+                const char *filename,
+                GFileQueryInfoFlags flags,
+                GFileInfo *info,
+                GFileAttributeMatcher *matcher)
+{
+  SoupMessage *msg;
+
+  g_debug ("Query info %s\n", filename);
+
+  msg = propfind_request_new (backend, filename, 0, ls_propnames);
+  if (msg == NULL)
+    {
+      g_vfs_job_failed (G_VFS_JOB (job),
+                        G_IO_ERROR, G_IO_ERROR_FAILED,
+                        _("Could not create request"));
+      return TRUE;
+    }
+
+  message_add_redirect_header (msg, flags);
+
+  dav_message_connect_signals (msg, backend);
+
+  g_vfs_backend_dav_send_async (backend, msg, try_query_info_cb, job);
+
+  return TRUE;
+}
+
 static PropName fs_info_propnames[] = {
   {"quota-available-bytes", NULL},
   {"quota-used-bytes",      NULL},
@@ -2325,51 +2523,18 @@ static PropName fs_info_propnames[] = {
 };
 
 static void
-do_query_fs_info (GVfsBackend           *backend,
-                  GVfsJobQueryFsInfo    *job,
-                  const char            *filename,
-                  GFileInfo             *info,
-                  GFileAttributeMatcher *attribute_matcher)
+try_query_fs_info_cb (GObject *source, GAsyncResult *result, gpointer user_data)
 {
-  SoupMessage *msg;
+  GVfsBackend *backend = G_VFS_BACKEND (source);
+  SoupMessage *msg = g_vfs_backend_dav_get_async_result_message (backend, result);
+  GVfsJobQueryFsInfo *job = user_data;
   GInputStream *body;
+  GError *error = NULL;
   Multistatus ms;
   xmlNodeIter iter;
   gboolean res;
-  GError *error = NULL;
-
-  g_file_info_set_attribute_string (info,
-                                    G_FILE_ATTRIBUTE_FILESYSTEM_TYPE,
-                                    "webdav");
-  g_file_info_set_attribute_boolean (info,
-                                     G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE,
-                                     TRUE);
-  g_file_info_set_attribute_uint32 (info,
-                                    G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW,
-                                    G_FILESYSTEM_PREVIEW_TYPE_IF_ALWAYS);
 
-  if (! (g_file_attribute_matcher_matches (attribute_matcher,
-                                           G_FILE_ATTRIBUTE_FILESYSTEM_SIZE) ||
-         g_file_attribute_matcher_matches (attribute_matcher,
-                                           G_FILE_ATTRIBUTE_FILESYSTEM_USED) ||
-         g_file_attribute_matcher_matches (attribute_matcher,
-                                           G_FILE_ATTRIBUTE_FILESYSTEM_FREE)))
-    {
-      g_vfs_job_succeeded (G_VFS_JOB (job));
-      return;
-    }
-
-  msg = propfind_request_new (backend, filename, 0, fs_info_propnames);
-  if (msg == NULL)
-    {
-      g_vfs_job_failed (G_VFS_JOB (job),
-                        G_IO_ERROR, G_IO_ERROR_FAILED,
-                        _("Could not create request"));
-
-      return;
-    }
-
-  body = g_vfs_backend_dav_send (backend, msg, TRUE, &error);
+  body = g_vfs_backend_dav_send_finish (backend, result, &error);
 
   if (!body)
     goto error;
@@ -2392,7 +2557,7 @@ do_query_fs_info (GVfsBackend           *backend,
 
       if (response.is_target)
         {
-          ms_response_to_fs_info (&response, info);
+          ms_response_to_fs_info (&response, job->file_info);
           res = TRUE;
         }
 
@@ -2416,36 +2581,67 @@ do_query_fs_info (GVfsBackend           *backend,
   g_object_unref (msg);
 }
 
-/* *** enumerate *** */
-static void
-do_enumerate (GVfsBackend           *backend,
-              GVfsJobEnumerate      *job,
-              const char            *filename,
-              GFileAttributeMatcher *matcher,
-              GFileQueryInfoFlags    flags)
+static gboolean
+try_query_fs_info (GVfsBackend *backend,
+                   GVfsJobQueryFsInfo *job,
+                   const char *filename,
+                   GFileInfo *info,
+                   GFileAttributeMatcher *attribute_matcher)
 {
   SoupMessage *msg;
-  GInputStream *body;
-  Multistatus ms;
-  xmlNodeIter iter;
-  gboolean res;
-  GError *error = NULL;
 
-  g_debug ("+ do_enumerate: %s\n", filename);
+  g_file_info_set_attribute_string (info,
+                                    G_FILE_ATTRIBUTE_FILESYSTEM_TYPE,
+                                    "webdav");
+  g_file_info_set_attribute_boolean (info,
+                                     G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE,
+                                     TRUE);
+  g_file_info_set_attribute_uint32 (info,
+                                    G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW,
+                                    G_FILESYSTEM_PREVIEW_TYPE_IF_ALWAYS);
 
-  msg = propfind_request_new (backend, filename, 1, ls_propnames);
+  if (! (g_file_attribute_matcher_matches (attribute_matcher,
+                                           G_FILE_ATTRIBUTE_FILESYSTEM_SIZE) ||
+         g_file_attribute_matcher_matches (attribute_matcher,
+                                           G_FILE_ATTRIBUTE_FILESYSTEM_USED) ||
+         g_file_attribute_matcher_matches (attribute_matcher,
+                                           G_FILE_ATTRIBUTE_FILESYSTEM_FREE)))
+    {
+      g_vfs_job_succeeded (G_VFS_JOB (job));
+      return TRUE;
+    }
+
+  msg = propfind_request_new (backend, filename, 0, fs_info_propnames);
   if (msg == NULL)
     {
       g_vfs_job_failed (G_VFS_JOB (job),
                         G_IO_ERROR, G_IO_ERROR_FAILED,
                         _("Could not create request"));
-      
-      return;
+      return TRUE;
     }
 
-  message_add_redirect_header (msg, flags);
+  dav_message_connect_signals (msg, backend);
+
+  g_vfs_backend_dav_send_async (backend, msg, try_query_fs_info_cb, job);
+
+  return TRUE;
+}
+
+/* *** enumerate *** */
+
+static void
+try_enumerate_cb (GObject *source, GAsyncResult *result, gpointer user_data)
+{
+  GVfsBackend *backend = G_VFS_BACKEND (source);
+  SoupMessage *msg = g_vfs_backend_dav_get_async_result_message (backend, result);
+  GVfsJobEnumerate *job = user_data;
+  GInputStream *body;
+  GError *error = NULL;
+  Multistatus ms;
+  xmlNodeIter iter;
+  gboolean res;
 
-  body = g_vfs_backend_dav_send (backend, msg, TRUE, &error);
+  body = g_vfs_backend_dav_send_finish (backend, result, &error);
 
   if (!body)
     goto error;
@@ -2491,6 +2687,34 @@ do_enumerate (GVfsBackend           *backend,
   g_object_unref (msg);
 }
 
+static gboolean
+try_enumerate (GVfsBackend *backend,
+               GVfsJobEnumerate *job,
+               const char *filename,
+               GFileAttributeMatcher *matcher,
+               GFileQueryInfoFlags flags)
+{
+  SoupMessage *msg;
+
+  g_debug ("+ try_enumerate: %s\n", filename);
+
+  msg = propfind_request_new (backend, filename, 1, ls_propnames);
+  if (msg == NULL)
+    {
+      g_vfs_job_failed (G_VFS_JOB (job),
+                        G_IO_ERROR, G_IO_ERROR_FAILED,
+                        _("Could not create request"));
+      return TRUE;
+    }
+
+  message_add_redirect_header (msg, flags);
+  dav_message_connect_signals (msg, backend);
+
+  g_vfs_backend_dav_send_async (backend, msg, try_enumerate_cb, job);
+
+  return TRUE;
+}
+
 /* ************************************************************************* */
 /*  */
 
@@ -2525,7 +2749,7 @@ try_open_stat_done (GObject      *source,
       return;
     }
 
-  res = stat_location_finish (msg, body, &target_type, NULL, NULL);
+  res = stat_location_end (msg, body, &target_type, NULL, NULL);
   g_object_unref (body);
 
   if (res == FALSE)
@@ -2559,7 +2783,7 @@ try_open_for_read (GVfsBackend        *backend,
   GUri *uri;
 
   uri = g_vfs_backend_dav_uri_for_path (backend, filename, FALSE);
-  msg = stat_location_begin (uri, FALSE);
+  msg = stat_location_start (uri, FALSE);
   g_uri_unref (uri);
 
   if (msg == NULL)
@@ -2571,8 +2795,11 @@ try_open_for_read (GVfsBackend        *backend,
       return FALSE;
     }
 
+  dav_message_connect_signals (msg, backend);
+
   g_vfs_job_set_backend_data (G_VFS_JOB (job), backend, NULL);
-  g_vfs_backend_dav_send_async (backend, msg, TRUE, try_open_stat_done, job);
+  soup_session_send_async (G_VFS_BACKEND_HTTP (backend)->session, msg,
+                           G_PRIORITY_DEFAULT, NULL, try_open_stat_done, job);
 
   return TRUE;
 }
@@ -2651,7 +2878,11 @@ try_create (GVfsBackend *backend,
 
   g_vfs_job_set_backend_data (G_VFS_JOB (job), msg, NULL);
 
-  g_vfs_backend_dav_send_async (backend, msg, TRUE, try_create_tested_existence, job);
+  dav_message_connect_signals (msg, backend);
+
+  soup_session_send_async (G_VFS_BACKEND_HTTP (backend)->session, msg,
+                           G_PRIORITY_DEFAULT, NULL,
+                           try_create_tested_existence, job);
 
   return TRUE;
 }
@@ -2760,12 +2991,14 @@ try_replace (GVfsBackend *backend,
       msg = soup_message_new_from_uri (SOUP_METHOD_HEAD, uri);
       g_uri_unref (uri);
 
+      dav_message_connect_signals (msg, backend);
+
       soup_message_headers_append (soup_message_get_request_headers (msg),
                                    "If-Match", etag);
 
       g_vfs_job_set_backend_data (G_VFS_JOB (job), op_backend, NULL);
-      g_vfs_backend_dav_send_async (backend, msg, TRUE,
-                                    try_replace_checked_etag, job);
+      soup_session_send_async (op_backend->session, msg, G_PRIORITY_DEFAULT,
+                               NULL, try_replace_checked_etag, job);
 
       return TRUE;
     }
@@ -2829,6 +3062,7 @@ try_write (GVfsBackend *backend,
   return TRUE;
 }
 
+/* this does not invoke libsoup API, so it can be synchronous/threaded */
 static void
 do_seek_on_write (GVfsBackend *backend,
                   GVfsJobSeekWrite *job,
@@ -2851,6 +3085,7 @@ do_seek_on_write (GVfsBackend *backend,
     }
 }
 
+/* this does not invoke libsoup API, so it can be synchronous/threaded */
 static void
 do_truncate (GVfsBackend *backend,
              GVfsJobTruncate *job,
@@ -2914,34 +3149,32 @@ try_close_write (GVfsBackend *backend,
   g_object_ref (msg);
   g_object_set_data (G_OBJECT (stream), "-gvfs-stream-msg", NULL);
 
+  dav_message_connect_signals (msg, backend);
+
   g_output_stream_close (stream, NULL, NULL);
   bytes = g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (stream));
   g_object_unref (stream);
 
   soup_message_set_request_body_from_bytes (msg, NULL, bytes);
-  g_vfs_backend_dav_send_async (backend, msg, TRUE,
-                                try_close_write_sent, job);
+  soup_session_send_async (G_VFS_BACKEND_HTTP (backend)->session, msg,
+                           G_PRIORITY_DEFAULT, NULL, try_close_write_sent, job);
   g_bytes_unref (bytes);
 
   return TRUE;
 }
 
 static void
-do_make_directory (GVfsBackend          *backend,
-                   GVfsJobMakeDirectory *job,
-                   const char           *filename)
+make_directory_cb (GObject *source, GAsyncResult *result, gpointer user_data)
 {
+  GVfsBackend *backend = G_VFS_BACKEND (source);
+  GVfsJobMakeDirectory *job = user_data;
+  SoupMessage *msg = g_vfs_backend_dav_get_async_result_message (backend, result);
   GInputStream *body;
-  SoupMessage *msg;
-  GUri *uri;
   GError *error = NULL;
   guint status;
 
-  uri = g_vfs_backend_dav_uri_for_path (backend, filename, TRUE);
-  msg = soup_message_new_from_uri (SOUP_METHOD_MKCOL, uri);
-  g_uri_unref (uri);
+  body = g_vfs_backend_dav_send_finish (backend, result, &error);
 
-  body = g_vfs_backend_dav_send (backend, msg, TRUE, &error);
   if (!body)
     {
       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
@@ -2966,27 +3199,72 @@ do_make_directory (GVfsBackend          *backend,
   g_object_unref (msg);
 }
 
-static void
-do_delete (GVfsBackend   *backend,
-           GVfsJobDelete *job,
-           const char    *filename)
+static gboolean
+try_make_directory (GVfsBackend *backend,
+                    GVfsJobMakeDirectory *job,
+                    const char *filename)
 {
-  GInputStream *body;
   SoupMessage *msg;
   GUri *uri;
+
+  uri = g_vfs_backend_dav_uri_for_path (backend, filename, TRUE);
+  msg = soup_message_new_from_uri (SOUP_METHOD_MKCOL, uri);
+  g_uri_unref (uri);
+
+  dav_message_connect_signals (msg, backend);
+
+  g_vfs_backend_dav_send_async (backend, msg, make_directory_cb, job);
+  return TRUE;
+}
+
+static void
+try_delete_send_cb (GObject *source, GAsyncResult *result, gpointer user_data)
+{
+  GVfsBackend *backend = G_VFS_BACKEND (source);
+  GVfsJobDelete *job = user_data;
+  SoupMessage *msg = g_vfs_backend_dav_get_async_result_message (backend, result);
+  GInputStream *body;
+  GError *error = NULL;
+  guint status;
+
+  body = g_vfs_backend_dav_send_finish (backend, result, &error);
+
+  if (!body)
+    {
+      g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+      g_error_free (error);
+      g_object_unref (msg);
+      return;
+    }
+
+  status = soup_message_get_status (msg);
+  if (!SOUP_STATUS_IS_SUCCESSFUL (status))
+    http_job_failed (G_VFS_JOB (job), msg);
+  else
+    g_vfs_job_succeeded (G_VFS_JOB (job));
+
+  g_object_unref (msg);
+  g_object_unref (body);
+}
+
+static void
+try_delete_cb (GObject *source, GAsyncResult *result, gpointer user_data)
+{
+  GVfsBackend *backend = G_VFS_BACKEND (source);
+  GVfsJobDelete *job = user_data;
+  SoupMessage *msg = NULL;
   GFileType file_type;
-  gboolean res;
   guint num_children;
-  guint status;
   GError *error = NULL;
+  gboolean res;
+  GUri *uri = stat_location_async_get_uri (backend, result);
 
-  uri = g_vfs_backend_dav_uri_for_path (backend, filename, FALSE);
-  res = stat_location (backend, uri, &file_type, NULL, &num_children, &error);
+  res = stat_location_finish (backend, &file_type, NULL,
+                              &num_children, result, &error);
   if (res == FALSE)
     {
       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
-      g_error_free (error);
-      g_uri_unref (uri);
+      g_clear_error (&error);
       return;
     }
 
@@ -2995,377 +3273,559 @@ do_delete (GVfsBackend   *backend,
       g_vfs_job_failed (G_VFS_JOB (job),
                         G_IO_ERROR, G_IO_ERROR_NOT_EMPTY,
                         _("Directory not empty"));
-      g_uri_unref (uri);
       return;
     }
 
   msg = soup_message_new_from_uri (SOUP_METHOD_DELETE, uri);
-  body = g_vfs_backend_dav_send (backend, msg, TRUE, &error);
+
+  dav_message_connect_signals (msg, backend);
+
+  g_vfs_backend_dav_send_async (backend, msg, try_delete_send_cb, job);
+}
+
+static gboolean
+try_delete (GVfsBackend *backend, GVfsJobDelete *job, const char *filename)
+{
+  GUri *uri;
+
+  uri = g_vfs_backend_dav_uri_for_path (backend, filename, FALSE);
+  stat_location_async (backend, uri, TRUE, try_delete_cb, job);
+  return TRUE;
+}
+
+typedef struct _TrySetDisplayNameData {
+  GVfsJobSetDisplayName *job;
+  char *target_path;
+} TrySetDisplayNameData;
+
+static void
+try_set_display_name_cb (GObject *source, GAsyncResult *result,
+                         gpointer user_data)
+{
+  GVfsBackend *backend = G_VFS_BACKEND (source);
+  TrySetDisplayNameData *data = user_data;
+  SoupMessage *msg = g_vfs_backend_dav_get_async_result_message (backend, result);
+  GInputStream *body;
+  GError *error = NULL;
+  guint status;
+
+  body = g_vfs_backend_dav_send_finish (backend, result, &error);
+
   if (!body)
     {
-      g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
-      g_error_free (error);
-      g_object_unref (msg);
-      g_uri_unref (uri);
-      return;
+      http_job_failed (G_VFS_JOB (data->job), msg);
+      goto error;
     }
 
   status = soup_message_get_status (msg);
-  if (!SOUP_STATUS_IS_SUCCESSFUL (status))
-    http_job_failed (G_VFS_JOB (job), msg);
+
+  /*
+   * The precondition of SOUP_STATUS_PRECONDITION_FAILED (412) in
+   * this case was triggered by the "Overwrite: F" header which
+   * means that the target already exists.
+   * Also if we get a REDIRECTION it means that there was no
+   * "Location" header, since otherwise that would have triggered
+   * our redirection handler. This probably means we are dealing
+   * with an web dav implementation (like mod_dav) that also sends
+   * redirects for the destionaion (i.e. "Destination: /foo" header)
+   * which very likely means that the target also exists (and is a
+   * directory). That or the webdav server is broken.
+   * We could find out by doing another stat and but I think this is
+   * such a corner case that we are totally fine with returning
+   * G_IO_ERROR_EXISTS.
+   * */
+
+  if (SOUP_STATUS_IS_SUCCESSFUL (status))
+    {
+      g_debug ("new target_path: %s\n", data->target_path);
+      g_vfs_job_set_display_name_set_new_path (data->job, data->target_path);
+      g_vfs_job_succeeded (G_VFS_JOB (data->job));
+    }
+  else if (status == SOUP_STATUS_PRECONDITION_FAILED ||
+           SOUP_STATUS_IS_REDIRECTION (status))
+    g_vfs_job_failed (G_VFS_JOB (data->job), G_IO_ERROR,
+                      G_IO_ERROR_EXISTS,
+                      _("Target file already exists"));
   else
-    g_vfs_job_succeeded (G_VFS_JOB (job));
+    http_job_failed (G_VFS_JOB (data->job), msg);
 
-  g_uri_unref (uri);
-  g_object_unref (msg);
   g_object_unref (body);
+
+error:
+  g_clear_error (&error);
+  g_object_unref (msg);
+  g_free (data->target_path);
+  g_slice_free (TrySetDisplayNameData, data);
+}
+
+static gboolean
+try_set_display_name (GVfsBackend *backend,
+                      GVfsJobSetDisplayName *job,
+                      const char *filename,
+                      const char *display_name)
+{
+  SoupMessage *msg;
+  GUri *source;
+  GUri *target;
+  TrySetDisplayNameData *data = g_slice_new (TrySetDisplayNameData);
+  char *dirname;
+
+  source = g_vfs_backend_dav_uri_for_path (backend, filename, FALSE);
+  msg = soup_message_new_from_uri (SOUP_METHOD_MOVE, source);
+
+  dirname = g_path_get_dirname (filename);
+  data->target_path = g_build_filename (dirname, display_name, NULL);
+  target = g_vfs_backend_dav_uri_for_path (backend, data->target_path, FALSE);
+
+  message_add_destination_header (msg, target);
+  message_add_overwrite_header (msg, FALSE);
+
+  data->job = job;
+  g_free (dirname);
+  g_uri_unref (target);
+  g_uri_unref (source);
+
+  dav_message_connect_signals (msg, backend);
+
+  g_vfs_backend_dav_send_async (backend, msg, try_set_display_name_cb, data);
+
+  return TRUE;
+}
+
+typedef struct _CopyData {
+  GVfsJob *job;
+  SoupMessage *msg;
+  GUri *source_uri;
+  GUri *target_uri;
+  GFileProgressCallback progress_callback;
+  gpointer progress_callback_data;
+  gint64 file_size;
+  GFileType source_ft;
+  GFileType target_ft;
+  GFileCopyFlags flags;
+  gboolean source_res;
+  gboolean target_res;
+} CopyData;
+
+static void
+copy_data_free (gpointer data)
+{
+  CopyData *p = data;
+  g_clear_pointer (&p->source_uri, g_uri_unref);
+  g_clear_pointer (&p->target_uri, g_uri_unref);
+  g_clear_object (&p->msg);
+  g_slice_free (CopyData, p);
 }
 
 static void
-do_set_display_name (GVfsBackend           *backend,
-                     GVfsJobSetDisplayName *job,
-                     const char            *filename,
-                     const char            *display_name)
+try_move_do_cb (GObject *source, GAsyncResult *result, gpointer user_data)
 {
+  GVfsBackend *backend = G_VFS_BACKEND (source);
+  CopyData *data = user_data;
   GInputStream *body;
-  SoupMessage *msg;
-  GUri *source;
-  GUri *target;
-  char *target_path;
-  char *dirname;
   GError *error = NULL;
   guint status;
 
-  source = g_vfs_backend_dav_uri_for_path (backend, filename, FALSE);
-  msg = soup_message_new_from_uri (SOUP_METHOD_MOVE, source);
-
-  dirname = g_path_get_dirname (filename);
-  target_path = g_build_filename (dirname, display_name, NULL);
-  target = g_vfs_backend_dav_uri_for_path (backend, target_path, FALSE);
-
-  message_add_destination_header (msg, target);
-  message_add_overwrite_header (msg, FALSE);
+  body = g_vfs_backend_dav_send_finish (backend, result, &error);
 
-  body = g_vfs_backend_dav_send (backend, msg, TRUE, &error);
   if (!body)
     {
-      http_job_failed (G_VFS_JOB (job), msg);
-      goto error;
+      g_vfs_job_failed_from_error (data->job, error);
+      copy_data_free (data);
+      g_clear_error (&error);
+      return;
     }
 
-  status = soup_message_get_status (msg);
-
-  /*
-   * The precondition of SOUP_STATUS_PRECONDITION_FAILED (412) in
-   * this case was triggered by the "Overwrite: F" header which
-   * means that the target already exists.
-   * Also if we get a REDIRECTION it means that there was no
-   * "Location" header, since otherwise that would have triggered
-   * our redirection handler. This probably means we are dealing
-   * with an web dav implementation (like mod_dav) that also sends
-   * redirects for the destionaion (i.e. "Destination: /foo" header)
-   * which very likely means that the target also exists (and is a
-   * directory). That or the webdav server is broken.
-   * We could find out by doing another stat and but I think this is
-   * such a corner case that we are totally fine with returning
-   * G_IO_ERROR_EXISTS.
-   * */
+  /* See try_set_display_name () for the explanation of the PRECONDITION_FAILED
+   * and IS_REDIRECTION handling below. */
+  status = soup_message_get_status (data->msg);
 
   if (SOUP_STATUS_IS_SUCCESSFUL (status))
     {
-      g_debug ("new target_path: %s\n", target_path);
-      g_vfs_job_set_display_name_set_new_path (job, target_path);
-      g_vfs_job_succeeded (G_VFS_JOB (job));
+      if (data->source_res && data->progress_callback)
+        data->progress_callback (data->file_size, data->file_size,
+                                 data->progress_callback_data);
+      g_vfs_job_succeeded (data->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_vfs_job_failed (data->job, G_IO_ERROR,
                       G_IO_ERROR_EXISTS,
                       _("Target file already exists"));
   else
-    http_job_failed (G_VFS_JOB (job), msg);
+    http_job_failed (data->job, data->msg);
 
-  g_object_unref (body);
+  copy_data_free (data);
+}
 
- error:
-  g_object_unref (msg);
-  g_free (dirname);
-  g_free (target_path);
-  g_uri_unref (target);
-  g_uri_unref (source);
+static void
+try_move_do (GVfsBackend *backend, CopyData *data)
+{
+  message_add_destination_header (data->msg, data->target_uri);
+  message_add_overwrite_header (data->msg, data->flags & G_FILE_COPY_OVERWRITE);
+  g_vfs_backend_dav_send_async (backend, data->msg, try_move_do_cb, data);
 }
 
 static void
-do_move (GVfsBackend *backend,
-         GVfsJobMove *job,
-         const char *source,
-         const char *destination,
-         GFileCopyFlags flags,
-         GFileProgressCallback progress_callback,
-         gpointer progress_callback_data)
-{
-  GInputStream *body = NULL;
-  SoupMessage *msg;
-  GUri *source_uri;
-  GUri *target_uri;
-  guint status;
-  GFileType source_ft, target_ft;
+try_move_target_delete_cb (GObject *source, GAsyncResult *result,
+                           gpointer user_data)
+{
+  GVfsBackend *backend = G_VFS_BACKEND (source);
+  SoupMessage *msg = g_vfs_backend_dav_get_async_result_message (backend, result);
+  CopyData *data = user_data;
+  GInputStream *body;
   GError *error = NULL;
-  gboolean res, stat_res;
-  gint64 file_size;
+  guint status;
 
-  if (flags & G_FILE_COPY_BACKUP)
-    {
-      if (flags & G_FILE_COPY_NO_FALLBACK_FOR_MOVE)
-        {
-          g_vfs_job_failed_literal (G_VFS_JOB (job),
-                                    G_IO_ERROR,
-                                    G_IO_ERROR_CANT_CREATE_BACKUP,
-                                    _("Backups not supported"));
-        }
-      else
-        {
-          /* Return G_IO_ERROR_NOT_SUPPORTED instead of G_IO_ERROR_CANT_CREATE_BACKUP
-           * to be proceeded with copy and delete fallback (see g_file_move). */
-          g_vfs_job_failed_literal (G_VFS_JOB (job),
-                                    G_IO_ERROR,
-                                    G_IO_ERROR_NOT_SUPPORTED,
-                                    "Operation not supported");
-        }
+  body = g_vfs_backend_dav_send_finish (backend, result, &error);
 
+  if (!body)
+    {
+      g_vfs_job_failed_from_error (data->job, error);
+      copy_data_free (data);
+      g_object_unref (msg);
+      g_clear_error (&error);
       return;
     }
 
-  source_uri = g_vfs_backend_dav_uri_for_path (backend, source, FALSE);
-  msg = soup_message_new_from_uri (SOUP_METHOD_MOVE, source_uri);
-  target_uri = g_vfs_backend_dav_uri_for_path (backend, destination, FALSE);
-
-  res = stat_location (backend, target_uri, &target_ft, NULL, NULL, &error);
-  if (!res && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+  status = soup_message_get_status (msg);
+  if (!SOUP_STATUS_IS_SUCCESSFUL (status))
     {
-      g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
-      goto error;
+      http_job_failed (data->job, msg);
+      g_object_unref (msg);
+      copy_data_free (data);
+      return;
     }
-  g_clear_error (&error);
 
-  stat_res = stat_location (backend, source_uri, &source_ft, &file_size, NULL, &error);
-  if (res)
+  g_object_unref (body);
+  g_object_unref (msg);
+
+  try_move_do (backend, data);
+}
+
+static void
+try_move_source_stat_cb (GObject *source, GAsyncResult *result,
+                         gpointer user_data)
+{
+  GVfsBackend *backend = G_VFS_BACKEND (source);
+  CopyData *data = user_data;
+  GError *error = NULL;
+
+  data->source_res = stat_location_finish (backend, &data->source_ft,
+                                           &data->file_size, NULL,
+                                           result, &error);
+
+  if (data->target_res)
     {
-      if (flags & G_FILE_COPY_OVERWRITE)
+      if (data->flags & G_FILE_COPY_OVERWRITE)
         {
-          if (stat_res)
+          if (data->source_res)
             {
-              if (target_ft == G_FILE_TYPE_DIRECTORY)
+              if (data->target_ft == G_FILE_TYPE_DIRECTORY)
                 {
-                  if (source_ft == G_FILE_TYPE_DIRECTORY)
-                    g_vfs_job_failed_literal (G_VFS_JOB(job),
+                  if (data->source_ft == G_FILE_TYPE_DIRECTORY)
+                    g_vfs_job_failed_literal (G_VFS_JOB(data->job),
                                               G_IO_ERROR,
                                               G_IO_ERROR_WOULD_MERGE,
                                               _("Can’t move directory over directory"));
                   else
-                    g_vfs_job_failed_literal (G_VFS_JOB(job),
+                    g_vfs_job_failed_literal (G_VFS_JOB(data->job),
                                               G_IO_ERROR,
                                               G_IO_ERROR_IS_DIRECTORY,
                                               _("Can’t move over directory"));
-                  goto error;
+                  copy_data_free (data);
+                  return;
                 }
-              else if (source_ft == G_FILE_TYPE_DIRECTORY)
+              else if (data->source_ft == G_FILE_TYPE_DIRECTORY)
                 {
                   /* Overwriting a file with a directory, first remove the
                    * file */
                   SoupMessage *msg;
 
                   msg = soup_message_new_from_uri (SOUP_METHOD_DELETE,
-                                                   target_uri);
-                  body = g_vfs_backend_dav_send (backend, msg, TRUE, &error);
-                  if (!body)
-                    {
-                      g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
-                      goto error;
-                    }
-
-                  status = soup_message_get_status (msg);
-                  if (!SOUP_STATUS_IS_SUCCESSFUL (status))
-                    {
-                      http_job_failed (G_VFS_JOB (job), msg);
-                      goto error;
-                    }
-                  g_object_unref (body);
-                  g_object_unref (msg);
+                                                   data->target_uri);
+
+                  dav_message_connect_signals (msg, backend);
+
+                  g_vfs_backend_dav_send_async (backend, msg,
+                                                try_move_target_delete_cb,
+                                                data);
+                  return;
                 }
             }
           else
             {
-              g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
-              goto error;
+              g_vfs_job_failed_from_error (data->job, error);
+              copy_data_free (data);
+              g_clear_error (&error);
+              return;
             }
         }
       else
         {
-          g_vfs_job_failed_literal (G_VFS_JOB(job),
+          g_vfs_job_failed_literal (G_VFS_JOB(data->job),
                                     G_IO_ERROR,
                                     G_IO_ERROR_EXISTS,
                                     _("Target file exists"));
-          goto error;
+          copy_data_free (data);
+          g_clear_error (&error);
+          return;
+        }
+    }
+
+  try_move_do (backend, data);
+}
+
+static void
+try_move_target_stat_cb (GObject *source, GAsyncResult *result,
+                         gpointer user_data)
+{
+  GVfsBackend *backend = G_VFS_BACKEND (source);
+  CopyData *data = user_data;
+  gboolean res;
+  GError *error = NULL;
+
+  res = stat_location_finish (backend, &data->target_ft, NULL, NULL,
+                              result, &error);
+
+  if (!res && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+    {
+      g_vfs_job_failed_from_error (data->job, error);
+      g_clear_error (&error);
+      copy_data_free (data);
+      return;
+    }
+
+  g_clear_error (&error);
+
+  data->target_res = res;
+
+  stat_location_async (backend, data->source_uri, FALSE,
+                       try_move_source_stat_cb, data);
+}
+
+static gboolean
+try_move (GVfsBackend *backend,
+          GVfsJobMove *job,
+          const char *source,
+          const char *destination,
+          GFileCopyFlags flags,
+          GFileProgressCallback progress_callback,
+          gpointer progress_callback_data)
+{
+  CopyData *data = g_slice_new0 (CopyData);
+  data->job = G_VFS_JOB (job);
+  data->flags = flags;
+  data->progress_callback = progress_callback;
+  data->progress_callback_data = progress_callback_data;
+
+  if (flags & G_FILE_COPY_BACKUP)
+    {
+      if (flags & G_FILE_COPY_NO_FALLBACK_FOR_MOVE)
+        {
+          g_vfs_job_failed_literal (G_VFS_JOB (job),
+                                    G_IO_ERROR,
+                                    G_IO_ERROR_CANT_CREATE_BACKUP,
+                                    _("Backups not supported"));
+        }
+      else
+        {
+          /* Return G_IO_ERROR_NOT_SUPPORTED instead of G_IO_ERROR_CANT_CREATE_BACKUP
+           * to be proceeded with copy and delete fallback (see g_file_move). */
+          g_vfs_job_failed_literal (G_VFS_JOB (job),
+                                    G_IO_ERROR,
+                                    G_IO_ERROR_NOT_SUPPORTED,
+                                    "Operation not supported");
         }
+
+      copy_data_free (data);
+      return TRUE;
     }
 
-  message_add_destination_header (msg, target_uri);
-  message_add_overwrite_header (msg, flags & G_FILE_COPY_OVERWRITE);
+  data->source_uri = g_vfs_backend_dav_uri_for_path (backend, source, FALSE);
+  data->msg = soup_message_new_from_uri (SOUP_METHOD_MOVE, data->source_uri);
+  data->target_uri = g_vfs_backend_dav_uri_for_path (backend, destination, FALSE);
+
+  dav_message_connect_signals (data->msg, backend);
+
+  stat_location_async (backend, data->target_uri, FALSE,
+                       try_move_target_stat_cb, data);
+  return TRUE;
+}
+
+static void
+try_copy_do_cb (GObject *source, GAsyncResult *result, gpointer user_data)
+{
+  GVfsBackend *backend = G_VFS_BACKEND (source);
+  CopyData *data = user_data;
+  GInputStream *body;
+  GError *error = NULL;
+  guint status;
+
+  body = g_vfs_backend_dav_send_finish (backend, result, &error);
 
-  body = g_vfs_backend_dav_send (backend, msg, TRUE, &error);
   if (!body)
     {
-      g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
-      goto error;
+      g_vfs_job_failed_from_error (data->job, error);
+      copy_data_free (data);
+      return;
     }
 
-  /* See do_set_display_name () for the explanation of the PRECONDITION_FAILED
+  /* See try_set_display_name () for the explanation of the PRECONDITION_FAILED
    * and IS_REDIRECTION handling below. */
-  status = soup_message_get_status (msg);
+  status = soup_message_get_status (data->msg);
   if (SOUP_STATUS_IS_SUCCESSFUL (status))
     {
-      if (stat_res && progress_callback)
-        progress_callback (file_size, file_size, progress_callback_data);
-      g_vfs_job_succeeded (G_VFS_JOB (job));
+      if (data->progress_callback)
+        data->progress_callback (data->file_size, data->file_size,
+                                 data->progress_callback_data);
+      g_vfs_job_succeeded (data->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_vfs_job_failed (data->job, G_IO_ERROR,
                       G_IO_ERROR_EXISTS,
                       _("Target file already exists"));
   else
-    http_job_failed (G_VFS_JOB (job), msg);
+    http_job_failed (data->job, data->msg);
 
- error:
-  g_clear_object (&body);
-  g_object_unref (msg);
-  g_clear_error (&error);
-  g_uri_unref (source_uri);
-  g_uri_unref (target_uri);
+  g_object_unref (body);
+  copy_data_free (data);
 }
 
 static void
-do_copy (GVfsBackend *backend,
-         GVfsJobCopy *job,
-         const char *source,
-         const char *destination,
-         GFileCopyFlags flags,
-         GFileProgressCallback progress_callback,
-         gpointer progress_callback_data)
+try_copy_target_stat_cb (GObject *source, GAsyncResult *result,
+                         gpointer user_data)
 {
-  GInputStream *body;
-  SoupMessage *msg;
-  GUri *source_uri;
-  GUri *target_uri;
-  guint status;
-  GFileType source_ft, target_ft;
+  GVfsBackend *backend = G_VFS_BACKEND (source);
+  CopyData *data = user_data;
   GError *error = NULL;
-  gboolean res;
-  gint64 file_size;
-
-  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, &file_size, NULL, &error);
-  if (!res)
-    {
-      g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
-      goto error;
-    }
+  data->target_res = stat_location_finish (backend, &data->target_ft,
+                                           NULL, NULL, result, &error);
 
-  res = stat_location (backend, target_uri, &target_ft, NULL, NULL, &error);
-  if (res)
+  if (data->target_res)
     {
-      if (flags & G_FILE_COPY_OVERWRITE)
+      if (data->flags & G_FILE_COPY_OVERWRITE)
         {
-          if (target_ft == G_FILE_TYPE_DIRECTORY)
+          if (data->target_ft == G_FILE_TYPE_DIRECTORY)
             {
-              if (source_ft == G_FILE_TYPE_DIRECTORY)
-                g_vfs_job_failed_literal (G_VFS_JOB(job),
+              if (data->source_ft == G_FILE_TYPE_DIRECTORY)
+                g_vfs_job_failed_literal (G_VFS_JOB(data->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_vfs_job_failed_literal (G_VFS_JOB(data->job),
                                           G_IO_ERROR,
                                           G_IO_ERROR_IS_DIRECTORY,
                                           _("File is directory"));
-              goto error;
+              copy_data_free (data);
+              return;
             }
         }
       else
         {
-          g_vfs_job_failed_literal (G_VFS_JOB (job),
+          g_vfs_job_failed_literal (data->job,
                                     G_IO_ERROR,
                                     G_IO_ERROR_EXISTS,
                                     _("Target file already exists"));
-          goto error;
+          copy_data_free (data);
+          return;
         }
     }
   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;
+      g_vfs_job_failed_from_error (data->job, error);
+      g_clear_error (&error);
+      copy_data_free (data);
+      return;
     }
 
-  if (source_ft == G_FILE_TYPE_DIRECTORY)
+  g_clear_error (&error);
+
+  if (data->source_ft == G_FILE_TYPE_DIRECTORY)
     {
-      g_vfs_job_failed_literal (G_VFS_JOB (job),
+      g_vfs_job_failed_literal (data->job,
                                 G_IO_ERROR,
                                 G_IO_ERROR_WOULD_RECURSE,
                                 _("Can’t recursively copy directory"));
-      goto error;
+      copy_data_free (data);
+      return;
     }
 
-  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);
+  data->msg = soup_message_new_from_uri (SOUP_METHOD_COPY, data->source_uri);
+  message_add_destination_header (data->msg, data->target_uri);
+  message_add_overwrite_header (data->msg, data->flags & G_FILE_COPY_OVERWRITE);
 
-  body = g_vfs_backend_dav_send (backend, msg, TRUE, &error);
-  if (!body)
+  dav_message_connect_signals (data->msg, backend);
+
+  g_vfs_backend_dav_send_async (backend, data->msg, try_copy_do_cb, data);
+}
+
+static void
+try_copy_source_stat_cb (GObject *source, GAsyncResult *result,
+                         gpointer user_data)
+{
+  GVfsBackend *backend = G_VFS_BACKEND (source);
+  CopyData *data = user_data;
+  gboolean res;
+  GError *error = NULL;
+
+  res = stat_location_finish (backend, &data->source_ft, &data->file_size,
+                              NULL, result, &error);
+
+  if (!res)
     {
-      g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
-      g_object_unref (msg);
-      goto error;
+      g_vfs_job_failed_from_error (data->job, error);
+      g_clear_error (&error);
+      copy_data_free (data);
+      return;
     }
 
-  /* See do_set_display_name () for the explanation of the PRECONDITION_FAILED
-   * and IS_REDIRECTION handling below. */
-  status = soup_message_get_status (msg);
-  if (SOUP_STATUS_IS_SUCCESSFUL (status))
+  g_clear_error (&error);
+
+  data->source_res = res;
+
+  stat_location_async (backend, data->target_uri, FALSE,
+                       try_copy_target_stat_cb, data);
+}
+
+static gboolean
+try_copy (GVfsBackend *backend,
+          GVfsJobCopy *job,
+          const char *source,
+          const char *destination,
+          GFileCopyFlags flags,
+          GFileProgressCallback progress_callback,
+          gpointer progress_callback_data)
+{
+  CopyData *data = g_slice_new0 (CopyData);
+  data->job = G_VFS_JOB (job);
+  data->flags = flags;
+  data->progress_callback = progress_callback;
+  data->progress_callback_data = progress_callback_data;
+
+  if (flags & G_FILE_COPY_BACKUP)
     {
-      if (progress_callback)
-        progress_callback (file_size, file_size, progress_callback_data);
-      g_vfs_job_succeeded (G_VFS_JOB (job));
+      /* 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");
+
+      copy_data_free (data);
+      return TRUE;
     }
-  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 (body);
-  g_object_unref (msg);
+  data->source_uri = g_vfs_backend_dav_uri_for_path (backend, source, FALSE);
+  data->target_uri = g_vfs_backend_dav_uri_for_path (backend, destination, FALSE);
 
-error:
-  g_clear_error (&error);
-  g_uri_unref (source_uri);
-  g_uri_unref (target_uri);
+  stat_location_async (backend, data->source_uri, FALSE,
+                       try_copy_source_stat_cb, data);
+  return TRUE;
 }
 
 #define CHUNK_SIZE 65536
@@ -3459,7 +3919,7 @@ static void
 push_done (GObject *source, GAsyncResult *result, gpointer user_data)
 {
   GInputStream *body;
-  GVfsJob *job = G_VFS_JOB (user_data);;
+  GVfsJob *job = G_VFS_JOB (user_data);
   GError *error = NULL;
 
   body = soup_session_send_finish (SOUP_SESSION (source), result, &error);
@@ -3488,7 +3948,7 @@ push_stat_dest_cb (GObject *source, GAsyncResult *result, gpointer user_data)
       return;
     }
 
-  if (stat_location_finish (handle->msg, body, &type, NULL, NULL))
+  if (stat_location_end (handle->msg, body, &type, NULL, NULL))
     {
       if (!(handle->op_job->flags & G_FILE_COPY_OVERWRITE))
         {
@@ -3527,8 +3987,11 @@ push_stat_dest_cb (GObject *source, GAsyncResult *result, gpointer user_data)
   g_signal_connect (handle->msg, "finished",
                     G_CALLBACK (push_finished), handle);
 
-  g_vfs_backend_dav_send_async (handle->backend, handle->msg, TRUE,
-                                push_done, handle->job);
+  dav_message_connect_signals (handle->msg, handle->backend);
+
+  soup_session_send_async (G_VFS_BACKEND_HTTP (handle->backend)->session,
+                           handle->msg, G_PRIORITY_DEFAULT,
+                           NULL, push_done, handle->job);
 }
 
 static void
@@ -3545,9 +4008,13 @@ push_source_fstat_cb (GObject *source, GAsyncResult *res, gpointer user_data)
       handle->size = g_file_info_get_size (info);
       g_object_unref (info);
 
-      handle->msg = stat_location_begin (handle->uri, FALSE);
-      g_vfs_backend_dav_send_async (handle->backend, handle->msg, TRUE,
-                                    push_stat_dest_cb, handle);
+      handle->msg = stat_location_start (handle->uri, FALSE);
+
+      dav_message_connect_signals (handle->msg, handle->backend);
+
+      soup_session_send_async (G_VFS_BACKEND_HTTP (handle->backend)->session,
+                               handle->msg, G_PRIORITY_DEFAULT, NULL,
+                               push_stat_dest_cb, handle);
     }
   else
     {
@@ -3675,26 +4142,23 @@ g_vfs_backend_dav_class_init (GVfsBackendDavClass *klass)
 
   backend_class = G_VFS_BACKEND_CLASS (klass); 
 
-  backend_class->try_mount         = NULL;
-  backend_class->mount             = do_mount;
-  backend_class->try_query_info    = NULL;
-  backend_class->query_info        = do_query_info;
-  backend_class->try_query_fs_info = NULL;
-  backend_class->query_fs_info     = do_query_fs_info;
-  backend_class->enumerate         = do_enumerate;
-  backend_class->try_open_for_read = try_open_for_read;
-  backend_class->try_create        = try_create;
-  backend_class->try_replace       = try_replace;
-  backend_class->try_write         = try_write;
-  backend_class->seek_on_write     = do_seek_on_write;
-  backend_class->truncate          = do_truncate;
-  backend_class->try_close_write   = try_close_write;
-  backend_class->make_directory    = do_make_directory;
-  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;
+  backend_class->try_mount            = try_mount;
+  backend_class->try_query_info       = try_query_info;
+  backend_class->try_query_fs_info    = try_query_fs_info;
+  backend_class->try_enumerate        = try_enumerate;
+  backend_class->try_open_for_read    = try_open_for_read;
+  backend_class->try_create           = try_create;
+  backend_class->try_replace          = try_replace;
+  backend_class->try_write            = try_write;
+  backend_class->seek_on_write        = do_seek_on_write;
+  backend_class->truncate             = do_truncate;
+  backend_class->try_close_write      = try_close_write;
+  backend_class->try_make_directory   = try_make_directory;
+  backend_class->try_delete           = try_delete;
+  backend_class->try_set_display_name = try_set_display_name;
+  backend_class->try_move             = try_move;
+  backend_class->try_copy             = try_copy;
+  backend_class->try_push             = try_push;
 
   /* override the maximum number of connections, since the libsoup defaults
    * of 10 and 2 respectively are too low and may cause backend lockups when


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