summaryrefslogtreecommitdiff
path: root/daemon/gvfsbackenddav.c
diff options
context:
space:
mode:
Diffstat (limited to 'daemon/gvfsbackenddav.c')
-rw-r--r--daemon/gvfsbackenddav.c1584
1 files changed, 881 insertions, 703 deletions
diff --git a/daemon/gvfsbackenddav.c b/daemon/gvfsbackenddav.c
index 12de62e5..559b8657 100644
--- a/daemon/gvfsbackenddav.c
+++ b/daemon/gvfsbackenddav.c
@@ -1,6 +1,7 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright (C) 2008 Red Hat, Inc.
+ * Copyright (C) 2021 Igalia S.L.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -66,10 +67,6 @@
#include "gvfsdnssdresolver.h"
#endif
-/* Overwrite maximal number of connections that libsoup can open in one time in
- order to prevent backend lockups when too many files is opened concurrently. */
-#define MAX_CONNS 32
-
typedef struct _MountAuthData MountAuthData;
static void mount_auth_info_free (MountAuthData *info);
@@ -79,6 +76,12 @@ static void mount_auth_info_free (MountAuthData *info);
static void dns_sd_resolver_changed (GVfsDnsSdResolver *resolver, GVfsBackendDav *dav_backend);
#endif
+static gboolean
+soup_authenticate (SoupMessage *msg,
+ SoupAuth *auth,
+ gboolean retrying,
+ gpointer user_data);
+
typedef struct _AuthInfo {
/* for server authentication */
@@ -95,6 +98,7 @@ struct _MountAuthData {
SoupSession *session;
GMountSource *mount_source;
gboolean retrying_after_403;
+ gboolean interactive;
AuthInfo server_auth;
AuthInfo proxy_auth;
@@ -164,7 +168,8 @@ string_to_uint64 (const char *str, guint64 *value)
static inline gboolean
sm_has_header (SoupMessage *msg, const char *header)
{
- return soup_message_headers_get_one (msg->response_headers, header) != NULL;
+ return soup_message_headers_get_one (soup_message_get_response_headers (msg),
+ header) != NULL;
}
static char *
@@ -193,12 +198,12 @@ path_get_parent_dir (const char *path)
static void
message_add_destination_header (SoupMessage *msg,
- SoupURI *uri)
+ GUri *uri)
{
char *string;
- string = soup_uri_to_string (uri, FALSE);
- soup_message_headers_append (msg->request_headers,
+ string = g_uri_to_string_partial (uri, G_URI_HIDE_PASSWORD);
+ soup_message_headers_append (soup_message_get_request_headers (msg),
"Destination",
string);
g_free (string);
@@ -207,7 +212,7 @@ static void
message_add_overwrite_header (SoupMessage *msg,
gboolean overwrite)
{
- soup_message_headers_append (msg->request_headers,
+ soup_message_headers_append (soup_message_get_request_headers (msg),
"Overwrite",
overwrite ? "T" : "F");
}
@@ -224,7 +229,7 @@ message_add_redirect_header (SoupMessage *msg,
else
header_redirect = "T";
- soup_message_headers_append (msg->request_headers,
+ soup_message_headers_append (soup_message_get_request_headers (msg),
"Apply-To-Redirect-Ref",
header_redirect);
}
@@ -267,21 +272,21 @@ path_equal (const char *a, const char *b, gboolean relax)
return res;
}
-/* Like soup_uri_equal */
static gboolean
-dav_uri_match (SoupURI *a, SoupURI *b, gboolean relax)
+dav_uri_match (GUri *a, GUri *b, gboolean relax)
{
gboolean diff;
char *ua, *ub;
- ua = g_uri_unescape_string (a->path, "/");
- ub = g_uri_unescape_string (b->path, "/");
+ ua = g_uri_unescape_string (g_uri_get_path (a), "/");
+ ub = g_uri_unescape_string (g_uri_get_path (b), "/");
- diff = a->scheme != b->scheme || a->port != b->port ||
- ! str_equal (a->host, b->host, TRUE) ||
- ! path_equal (ua, ub, relax) ||
- ! str_equal (a->query, b->query, FALSE) ||
- ! str_equal (a->fragment, b->fragment, FALSE);
+ diff = g_uri_get_port (a) != g_uri_get_port (b) ||
+ !str_equal (g_uri_get_scheme (a), g_uri_get_scheme (b), FALSE) ||
+ !str_equal (g_uri_get_host (a), g_uri_get_host (b), TRUE) ||
+ !path_equal (ua, ub, relax) ||
+ !str_equal (g_uri_get_query (a), g_uri_get_query (b), FALSE) ||
+ !str_equal (g_uri_get_fragment (a), g_uri_get_fragment (b), FALSE);
g_free (ua);
g_free (ub);
@@ -307,7 +312,7 @@ message_should_apply_redir_ref (SoupMessage *msg)
{
const char *header;
- header = soup_message_headers_get_one (msg->request_headers,
+ header = soup_message_headers_get_one (soup_message_get_request_headers (msg),
"Apply-To-Redirect-Ref");
if (header == NULL || g_ascii_strcasecmp (header, "T"))
@@ -316,23 +321,38 @@ message_should_apply_redir_ref (SoupMessage *msg)
return TRUE;
}
+static GUri *
+dav_uri_dup_with (GUri *uri, const char *path, const char *userinfo)
+{
+ if (!path && !userinfo)
+ return g_uri_ref (uri);
+
+ return g_uri_build (g_uri_get_flags (uri),
+ g_uri_get_scheme (uri),
+ userinfo ? userinfo : g_uri_get_userinfo (uri),
+ g_uri_get_host (uri),
+ g_uri_get_port (uri),
+ path ? path : g_uri_get_path (uri),
+ g_uri_get_query (uri),
+ g_uri_get_fragment (uri));
+}
+
-static SoupURI *
+static GUri *
g_vfs_backend_dav_uri_for_path (GVfsBackend *backend,
const char *path,
gboolean is_dir)
{
- SoupURI *mount_base;
- SoupURI *uri;
- char *fn_encoded;
- char *new_path;
+ GUri *mount_base;
+ GUri *uri;
+ char *fn_encoded;
+ char *new_path;
mount_base = http_backend_get_mount_base (backend);
- uri = soup_uri_copy (mount_base);
/* "/" means "whatever mount_base is" */
if (!strcmp (path, "/"))
- return uri;
+ return g_uri_ref (mount_base);
/* The mount_base path is escaped already so we need to
escape the new path as well */
@@ -345,139 +365,154 @@ g_vfs_backend_dav_uri_for_path (GVfsBackend *backend,
* to be a directory to avoid redirections
*/
if (is_dir == FALSE || g_str_has_suffix (path, "/"))
- new_path = g_build_path ("/", uri->path, fn_encoded, NULL);
+ new_path = g_build_path ("/", g_uri_get_path (mount_base), fn_encoded, NULL);
else
- new_path = g_build_path ("/", uri->path, fn_encoded, "/", NULL);
+ new_path = g_build_path ("/", g_uri_get_path (mount_base), fn_encoded, "/", NULL);
+
+ uri = dav_uri_dup_with (mount_base, new_path, NULL);
g_free (fn_encoded);
- g_free (uri->path);
- uri->path = new_path;
+ g_free (new_path);
return uri;
}
-/* redirection */
-static void
-redirect_handler (SoupMessage *msg, gpointer user_data)
+static gboolean
+g_vfs_backend_dav_stream_skip (GInputStream *stream, GError **error)
{
- SoupSession *session = user_data;
- const char *new_loc;
- SoupURI *new_uri;
- SoupURI *old_uri;
- guint status;
- gboolean redirect;
+ for (;;)
+ {
+ gssize skipped = g_input_stream_skip (stream, 4096, NULL, error);
+ if (skipped < 0)
+ {
+ return FALSE;
+ }
+ else if (!skipped)
+ break;
+ }
- status = msg->status_code;
- new_loc = soup_message_headers_get_one (msg->response_headers, "Location");
+ g_input_stream_close (stream, NULL, NULL);
+ return TRUE;
+}
- /* If we don't have a location to redirect to, just fail */
- if (new_loc == NULL)
- return;
+/* redirection */
+static GInputStream *
+g_vfs_backend_dav_redirect (SoupSession *session,
+ SoupMessage *msg,
+ GError **error)
+{
+ GInputStream *res;
+ const char *new_loc;
+ GUri *new_uri;
+ GUri *old_uri;
+ GUri *tmp;
+ guint status;
+ gboolean redirect;
- if (!SOUP_STATUS_IS_REDIRECTION(status))
- return;
+ res = soup_session_send (session, msg, NULL, error);
+ if (!res)
+ return NULL;
- new_uri = soup_uri_new_with_base (soup_message_get_uri (msg), new_loc);
- if (new_uri == NULL)
- {
- soup_message_set_status_full (msg,
- SOUP_STATUS_MALFORMED,
- "Invalid Redirect URL");
- return;
- }
-
- old_uri = soup_message_get_uri (msg);
-
- /* copy over username and password to new_uri */
- soup_uri_set_user(new_uri, old_uri->user);
- soup_uri_set_password(new_uri, old_uri->password);
-
- /* Check if this is a trailing slash redirect (i.e. /a/b to /a/b/),
- * redirect it right away
- */
- redirect = dav_uri_match (new_uri, old_uri, TRUE);
-
- if (redirect == TRUE)
- {
- const char *dest;
-
- dest = soup_message_headers_get_one (msg->request_headers,
- "Destination");
-
- if (dest && g_str_has_suffix (dest, "/") == FALSE)
- {
- char *new_dest = g_strconcat (dest, "/", NULL);
- soup_message_headers_replace (msg->request_headers,
- "Destination",
- new_dest);
- g_free (new_dest);
- }
- }
- else if (message_should_apply_redir_ref (msg))
- {
-
-
- if (status == SOUP_STATUS_MOVED_PERMANENTLY ||
- status == SOUP_STATUS_TEMPORARY_REDIRECT)
- {
-
- /* Only corss-site redirect safe methods */
- if (msg->method == SOUP_METHOD_GET &&
- msg->method == SOUP_METHOD_HEAD &&
- msg->method == SOUP_METHOD_OPTIONS &&
- msg->method == SOUP_METHOD_PROPFIND)
- redirect = TRUE;
- }
-
-#if 0
- else if (msg->status_code == SOUP_STATUS_SEE_OTHER ||
- msg->status_code == SOUP_STATUS_FOUND)
- {
- /* Redirect using a GET */
- g_object_set (msg,
- SOUP_MESSAGE_METHOD, SOUP_METHOD_GET,
- NULL);
- soup_message_set_request (msg, NULL,
- SOUP_MEMORY_STATIC, NULL, 0);
- soup_message_headers_set_encoding (msg->request_headers,
- SOUP_ENCODING_NONE);
- }
-#endif
- /* ELSE:
- *
- * Two possibilities:
- *
- * 1) It's a non-redirecty 3xx response (300, 304,
- * 305, 306)
- * 2) It's some newly-defined 3xx response (308+)
- *
- * We ignore both of these cases. In the first,
- * redirecting would be explicitly wrong, and in the
- * last case, we have no clue if the 3xx response is
- * supposed to be redirecty or non-redirecty. Plus,
- * 2616 says unrecognized status codes should be
- * treated as the equivalent to the x00 code, and we
- * don't redirect on 300, so therefore we shouldn't
- * redirect on 308+ either.
- */
- }
-
- if (redirect)
- {
- soup_message_set_uri (msg, new_uri);
- soup_session_requeue_message (session, msg);
- }
+ status = soup_message_get_status (msg);
+ if (!SOUP_STATUS_IS_REDIRECTION (status))
+ return res;
+
+ new_loc = soup_message_headers_get_one (soup_message_get_response_headers (msg),
+ "Location");
+ if (new_loc == NULL)
+ return res;
+
+ old_uri = soup_message_get_uri (msg);
+ new_uri = g_uri_parse_relative (old_uri, new_loc,
+ SOUP_HTTP_URI_FLAGS, error);
+ if (new_uri == NULL)
+ {
+ g_object_unref (res);
+ return NULL;
+ }
+
+ tmp = new_uri;
+ new_uri = dav_uri_dup_with (new_uri, NULL, g_uri_get_userinfo (old_uri));
+ g_uri_unref (tmp);
+
+ /* Check if this is a trailing slash redirect (i.e. /a/b to /a/b/),
+ * redirect it right away
+ */
+ redirect = dav_uri_match (new_uri, old_uri, TRUE);
+ if (redirect)
+ {
+ const char *dest;
+
+ dest = soup_message_headers_get_one (soup_message_get_request_headers (msg),
+ "Destination");
+ if (dest && g_str_has_suffix (dest, "/") == FALSE)
+ {
+ char *new_dest = g_strconcat (dest, "/", NULL);
+ soup_message_headers_replace (soup_message_get_request_headers (msg),
+ "Destination",
+ new_dest);
+ g_free (new_dest);
+ }
+ }
+ else if (message_should_apply_redir_ref (msg))
+ {
+ if (status == SOUP_STATUS_MOVED_PERMANENTLY ||
+ status == SOUP_STATUS_TEMPORARY_REDIRECT)
+ {
+ const char *method = soup_message_get_method (msg);
+
+ /* Only cross-site redirect safe methods */
+ if (method == SOUP_METHOD_GET &&
+ method == SOUP_METHOD_HEAD &&
+ method == SOUP_METHOD_OPTIONS &&
+ method == SOUP_METHOD_PROPFIND)
+ redirect = TRUE;
+ }
+
+ /* Two possibilities:
+ *
+ * 1) It's a non-redirecty 3xx response (300, 304,
+ * 305, 306)
+ * 2) It's some newly-defined 3xx response (308+)
+ *
+ * We ignore both of these cases. In the first,
+ * redirecting would be explicitly wrong, and in the
+ * last case, we have no clue if the 3xx response is
+ * supposed to be redirecty or non-redirecty. Plus,
+ * 2616 says unrecognized status codes should be
+ * treated as the equivalent to the x00 code, and we
+ * don't redirect on 300, so therefore we shouldn't
+ * redirect on 308+ either.
+ */
+ }
+
+ if (!redirect)
+ {
+ g_uri_unref (new_uri);
+ return res;
+ }
+
+ if (!g_vfs_backend_dav_stream_skip (res, error))
+ {
+ g_object_unref (res);
+ return NULL;
+ }
+
+ g_object_unref (res);
+
+ soup_message_set_uri (msg, new_uri);
- soup_uri_free (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;
- SoupURI *mount_base;
+ GUri *mount_base;
char *display_name;
char port[7] = {0, };
+ gint gport;
dav_backend = G_VFS_BACKEND_DAV (backend);
@@ -493,83 +528,86 @@ g_vfs_backend_dav_setup_display_name (GVfsBackend *backend)
mount_base = http_backend_get_mount_base (backend);
- if (! soup_uri_uses_default_port (mount_base))
- g_snprintf (port, sizeof (port), ":%u", mount_base->port);
+ 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 (mount_base->user != NULL)
+ 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"),
- mount_base->user,
- mount_base->host,
- port);
+ g_uri_get_user (mount_base),
+ g_uri_get_host (mount_base),
+ port);
else
display_name = g_strdup_printf ("%s%s",
- mount_base->host,
- port);
+ g_uri_get_host (mount_base),
+ port);
g_vfs_backend_set_display_name (backend, display_name);
g_free (display_name);
}
-static void
-certificate_error_handler (SoupMessage *msg,
- GParamSpec *pspec,
- gpointer user_data)
+static gboolean
+accept_certificate (SoupMessage *msg,
+ GTlsCertificate *certificate,
+ GTlsCertificateFlags errors,
+ gpointer user_data)
{
GVfsBackendDav *dav = G_VFS_BACKEND_DAV (user_data);
- GTlsCertificate *certificate;
- GTlsCertificateFlags errors;
- /* Fail the message if the certificate errors change or the certificate is
- * different. */
- if (soup_message_get_https_status (msg, &certificate, &errors))
- {
- if (errors != dav->certificate_errors ||
- !g_tls_certificate_is_same (certificate, dav->certificate))
- {
- soup_session_cancel_message (G_VFS_BACKEND_HTTP (dav)->session,
- msg,
- SOUP_STATUS_SSL_FAILED);
- }
- }
+ return (errors == dav->certificate_errors &&
+ g_tls_certificate_is_same (certificate, dav->certificate));
}
-static guint
-g_vfs_backend_dav_send_message (GVfsBackend *backend, SoupMessage *message)
+static void
+dav_message_connect_signals (SoupMessage *message, GVfsBackend *backend)
{
- GVfsBackendHttp *http_backend;
- SoupSession *session;
+ 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);
- http_backend = G_VFS_BACKEND_HTTP (backend);
- session = http_backend->session;
+ g_signal_connect (message, "authenticate",
+ G_CALLBACK (soup_authenticate),
+ &dav_backend->auth_info);
+}
+
+static GInputStream *
+g_vfs_backend_dav_send (GVfsBackend *backend,
+ SoupMessage *message,
+ gboolean cb_connect,
+ 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 (G_VFS_BACKEND_DAV (backend)->certificate_errors)
- g_signal_connect (message, "notify::tls-errors",
- G_CALLBACK (certificate_error_handler), backend);
+ if (cb_connect)
+ dav_message_connect_signals (message, backend);
- soup_message_add_header_handler (message, "got_body", "Location",
- G_CALLBACK (redirect_handler), session);
-
- return http_backend_send_message (backend, message);
+ return g_vfs_backend_dav_redirect (session, message, error);
}
static void
-g_vfs_backend_dav_queue_message (GVfsBackend *backend,
- SoupMessage *msg,
- SoupSessionCallback callback,
- gpointer user_data)
+g_vfs_backend_dav_send_async (GVfsBackend *backend,
+ SoupMessage *message,
+ gboolean cb_connect,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- if (G_VFS_BACKEND_DAV (backend)->certificate_errors)
- g_signal_connect (msg, "notify::tls-errors",
- G_CALLBACK (certificate_error_handler), backend);
+ SoupSession *session = G_VFS_BACKEND_HTTP (backend)->session;
+
+ if (cb_connect)
+ dav_message_connect_signals (message, backend);
- soup_session_queue_message (G_VFS_BACKEND_HTTP (backend)->session, msg,
- callback, user_data);
+ soup_session_send_async (session, message, G_PRIORITY_DEFAULT, NULL,
+ callback, user_data);
}
/* ************************************************************************* */
@@ -692,33 +730,38 @@ xml_node_iter_get_current (xmlNodeIter *iter)
return iter->cur_node;
}
+static int
+xml_read_cb (void *ctx, char *buf, int len)
+{
+ return g_input_stream_read (ctx, buf, len, NULL, NULL);
+}
+
static xmlDocPtr
parse_xml (SoupMessage *msg,
+ GInputStream *body,
xmlNodePtr *root,
const char *name,
GError **error)
{
- xmlDocPtr doc;
+ xmlDocPtr doc;
- if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
+ if (!SOUP_STATUS_IS_SUCCESSFUL (soup_message_get_status (msg)))
{
g_set_error (error,
G_IO_ERROR,
- http_error_code_from_status (msg->status_code),
- _("HTTP Error: %s"), msg->reason_phrase);
+ http_error_code_from_status (soup_message_get_status (msg)),
+ _("HTTP Error: %s"), soup_message_get_reason_phrase (msg));
return NULL;
}
- doc = xmlReadMemory (msg->response_body->data,
- msg->response_body->length,
- "response.xml",
- NULL,
- XML_PARSE_NONET |
- XML_PARSE_NOWARNING |
- XML_PARSE_NOBLANKS |
- XML_PARSE_NSCLEAN |
- XML_PARSE_NOCDATA |
- XML_PARSE_COMPACT);
+ doc = xmlReadIO (xml_read_cb, NULL, body, "response.xml", NULL,
+ XML_PARSE_NONET |
+ XML_PARSE_NOWARNING |
+ XML_PARSE_NOBLANKS |
+ XML_PARSE_NSCLEAN |
+ XML_PARSE_NOCDATA |
+ XML_PARSE_COMPACT);
+
if (doc == NULL)
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
@@ -759,7 +802,7 @@ struct _Multistatus {
xmlDocPtr doc;
xmlNodePtr root;
- SoupURI *target;
+ GUri *target;
char *path;
};
@@ -784,13 +827,16 @@ struct _MsPropstat {
static gboolean
-multistatus_parse (SoupMessage *msg, Multistatus *multistatus, GError **error)
+multistatus_parse (SoupMessage *msg,
+ GInputStream *body,
+ Multistatus *multistatus,
+ GError **error)
{
xmlDocPtr doc;
xmlNodePtr root;
- SoupURI *uri;
+ GUri *uri;
- doc = parse_xml (msg, &root, "multistatus", error);
+ doc = parse_xml (msg, body, &root, "multistatus", error);
if (doc == NULL)
return FALSE;
@@ -800,7 +846,7 @@ multistatus_parse (SoupMessage *msg, Multistatus *multistatus, GError **error)
multistatus->doc = doc;
multistatus->root = root;
multistatus->target = uri;
- multistatus->path = g_uri_unescape_string (uri->path, "/");
+ multistatus->path = g_uri_unescape_string (g_uri_get_path (uri), "/");
return TRUE;
}
@@ -830,7 +876,7 @@ multistatus_get_response (xmlNodeIter *resp_iter, MsResponse *response)
xmlNodePtr iter;
xmlNodePtr href;
xmlNodePtr propstat;
- SoupURI *uri;
+ GUri *uri;
const char *text;
char *path;
@@ -871,13 +917,12 @@ multistatus_get_response (xmlNodeIter *resp_iter, MsResponse *response)
if (text == NULL)
return FALSE;
- uri = soup_uri_new_with_base (multistatus->target, text);
-
+ uri = g_uri_parse_relative (multistatus->target, text, SOUP_HTTP_URI_FLAGS, NULL);
if (uri == NULL)
return FALSE;
- path = g_uri_unescape_string (uri->path, "/");
- soup_uri_free (uri);
+ path = g_uri_unescape_string (g_uri_get_path (uri), "/");
+ g_uri_unref (uri);
response->path = path;
response->is_target = path_equal (path, multistatus->path, TRUE);
@@ -1102,16 +1147,16 @@ ms_response_to_file_info (MsResponse *response,
}
else if (node_has_name (node, "getlastmodified"))
{
- SoupDate *sd;
+ GDateTime *gd;
- sd = soup_date_new_from_string(text);
- if (sd)
- {
- g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED, soup_date_to_time_t (sd));
+ gd = soup_date_time_new_from_http_string (text);
+ if (gd)
+ {
+ g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED, g_date_time_to_unix (gd));
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC, 0);
- soup_date_free (sd);
- }
- }
+ g_date_time_unref (gd);
+ }
+ }
}
}
@@ -1250,6 +1295,19 @@ typedef struct _PropName {
} PropName;
+static void
+propfind_stat_msg_starting_restarted (SoupMessage *msg, gpointer user_data)
+{
+ soup_message_set_request_body_from_bytes (msg, "application/xml", user_data);
+}
+
+static void
+propfind_stat_bytes_unref (gpointer data, GClosure *closure)
+{
+ (void)closure;
+ g_bytes_unref (data);
+}
+
static SoupMessage *
propfind_request_new (GVfsBackend *backend,
@@ -1258,13 +1316,14 @@ propfind_request_new (GVfsBackend *backend,
const PropName *properties)
{
SoupMessage *msg;
- SoupURI *uri;
+ GUri *uri;
const char *header_depth;
GString *body;
+ GBytes *bytes;
uri = g_vfs_backend_dav_uri_for_path (backend, filename, depth > 0);
msg = soup_message_new_from_uri (SOUP_METHOD_PROPFIND, uri);
- soup_uri_free (uri);
+ g_uri_unref (uri);
if (msg == NULL)
return NULL;
@@ -1276,7 +1335,8 @@ propfind_request_new (GVfsBackend *backend,
else
header_depth = "infinity";
- soup_message_headers_append (msg->request_headers, "Depth", header_depth);
+ soup_message_headers_append (soup_message_get_request_headers (msg),
+ "Depth", header_depth);
body = g_string_new (PROPSTAT_XML_BEGIN);
@@ -1302,22 +1362,29 @@ propfind_request_new (GVfsBackend *backend,
g_string_append (body, PROPSTAT_XML_END);
- soup_message_set_request (msg, "application/xml",
- SOUP_MEMORY_TAKE,
- body->str,
- body->len);
+ bytes = g_string_free_to_bytes (body);
+
+ g_signal_connect_data (msg, "starting",
+ G_CALLBACK (propfind_stat_msg_starting_restarted),
+ g_bytes_ref (bytes), propfind_stat_bytes_unref, 0);
- g_string_free (body, FALSE);
+ /* only called with implicit redirects enabled */
+ g_signal_connect_data (msg, "restarted",
+ G_CALLBACK (propfind_stat_msg_starting_restarted),
+ g_bytes_ref (bytes), propfind_stat_bytes_unref, 0);
+
+ g_bytes_unref (bytes);
return msg;
}
static SoupMessage *
-stat_location_begin (SoupURI *uri,
- gboolean count_children)
+stat_location_begin (GUri *uri,
+ gboolean count_children)
{
SoupMessage *msg;
const char *depth;
+ GBytes *bytes;
static const char *stat_profind_body =
PROPSTAT_XML_BEGIN
PROPSTAT_XML_PROP_BEGIN
@@ -1333,33 +1400,42 @@ stat_location_begin (SoupURI *uri,
else
depth = "0";
- soup_message_headers_append (msg->request_headers, "Depth", depth);
+ soup_message_headers_append (soup_message_get_request_headers (msg),
+ "Depth", depth);
+
+ bytes = g_bytes_new (stat_profind_body, strlen (stat_profind_body));
+
+ g_signal_connect_data (msg, "starting",
+ G_CALLBACK (propfind_stat_msg_starting_restarted),
+ g_bytes_ref (bytes), propfind_stat_bytes_unref, 0);
+
+ /* only called with implicit redirects enabled */
+ g_signal_connect_data (msg, "restarted",
+ G_CALLBACK (propfind_stat_msg_starting_restarted),
+ g_bytes_ref (bytes), propfind_stat_bytes_unref, 0);
+
+ g_bytes_unref (bytes);
- soup_message_set_request (msg, "application/xml",
- SOUP_MEMORY_STATIC,
- stat_profind_body,
- strlen (stat_profind_body));
return msg;
}
static gboolean
-stat_location_finish (SoupMessage *msg,
- GFileType *target_type,
- gint64 *target_size,
- guint *num_children)
+stat_location_finish (SoupMessage *msg,
+ GInputStream *body,
+ GFileType *target_type,
+ gint64 *target_size,
+ guint *num_children)
{
Multistatus ms;
xmlNodeIter iter;
gboolean res;
- GError *error;
guint child_count;
GFileInfo *file_info;
- if (msg->status_code != 207)
+ if (!body || soup_message_get_status (msg) != SOUP_STATUS_MULTI_STATUS)
return FALSE;
- res = multistatus_parse (msg, &ms, &error);
-
+ res = multistatus_parse (msg, body, &ms, NULL);
if (res == FALSE)
return FALSE;
@@ -1405,43 +1481,52 @@ stat_location_finish (SoupMessage *msg,
static gboolean
stat_location (GVfsBackend *backend,
- SoupURI *uri,
+ GUri *uri,
GFileType *target_type,
gint64 *target_size,
guint *num_children,
GError **error)
{
SoupMessage *msg;
- guint status;
- gboolean count_children;
- gboolean res;
+ GInputStream *body;
+ guint status;
+ gboolean count_children;
+ gboolean res;
count_children = num_children != NULL;
msg = stat_location_begin (uri, count_children);
-
if (msg == NULL)
return FALSE;
- status = g_vfs_backend_dav_send_message (backend, msg);
+ body = g_vfs_backend_dav_send (backend, msg, TRUE, error);
- if (status != 207)
+ if (!body)
+ {
+ g_object_unref (msg);
+ return FALSE;
+ }
+
+ status = soup_message_get_status (msg);
+ if (status != SOUP_STATUS_MULTI_STATUS)
{
g_set_error_literal (error,
- G_IO_ERROR,
- http_error_code_from_status (status),
- msg->reason_phrase);
+ G_IO_ERROR,
+ http_error_code_from_status (status),
+ soup_message_get_reason_phrase (msg));
g_object_unref (msg);
+ g_object_unref (body);
return FALSE;
}
- res = stat_location_finish (msg, target_type, target_size, num_children);
+ res = stat_location_finish (msg, body, target_type, target_size, 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"));
+ g_set_error_literal (error,
+ G_IO_ERROR, G_IO_ERROR_FAILED,
+ _("Response invalid"));
return res;
}
@@ -1464,38 +1549,11 @@ mount_auth_info_free (MountAuthData *data)
}
-static void
-soup_authenticate_from_data (SoupSession *session,
- SoupMessage *msg,
- SoupAuth *auth,
- gboolean retrying,
- gpointer user_data)
-{
- MountAuthData *data;
- AuthInfo *info;
-
- g_debug ("+ soup_authenticate_from_data (%s) \n",
- retrying ? "retrying" : "first auth");
-
- if (retrying)
- return;
-
- data = (MountAuthData *) user_data;
-
- if (soup_auth_is_for_proxy (auth))
- info = &data->proxy_auth;
- else
- info = &data->server_auth;
-
- soup_auth_authenticate (auth, info->username, info->password);
-}
-
-static void
-soup_authenticate_interactive (SoupSession *session,
- SoupMessage *msg,
- SoupAuth *auth,
- gboolean retrying,
- gpointer user_data)
+static gboolean
+soup_authenticate (SoupMessage *msg,
+ SoupAuth *auth,
+ gboolean retrying,
+ gpointer user_data)
{
MountAuthData *data;
AuthInfo *info;
@@ -1512,9 +1570,28 @@ soup_authenticate_interactive (SoupSession *session,
data = (MountAuthData *) user_data;
- retrying = (retrying || data->retrying_after_403);
+ is_proxy = soup_auth_is_for_proxy (auth);
+
+ if (is_proxy)
+ info = &(data->proxy_auth);
+ else
+ info = &(data->server_auth);
+
+ if (!data->interactive)
+ {
+ g_debug ("+ soup_authenticate (%s) \n",
+ retrying ? "retrying" : "first auth");
+
+ if (!retrying)
+ soup_auth_authenticate (auth, info->username, info->password);
+
+ return FALSE;
+ }
- g_debug ("+ soup_authenticate_interactive (%s) \n",
+ if (data->retrying_after_403)
+ retrying = TRUE;
+
+ g_debug ("+ soup_authenticate (interactive, %s) \n",
retrying ? "retrying" : "first auth");
new_username = NULL;
@@ -1522,43 +1599,53 @@ soup_authenticate_interactive (SoupSession *session,
realm = NULL;
pw_ask_flags = G_ASK_PASSWORD_NEED_PASSWORD;
- is_proxy = soup_auth_is_for_proxy (auth);
realm = soup_auth_get_realm (auth);
- if (is_proxy)
- info = &(data->proxy_auth);
- else
- info = &(data->server_auth);
-
if (realm && info->realm == NULL)
info->realm = g_strdup (realm);
else if (realm && info->realm && !g_str_equal (realm, info->realm))
- return;
+ return FALSE;
have_auth = info->username && info->password;
if (have_auth == FALSE && g_vfs_keyring_is_available ())
{
- SoupURI *uri;
- SoupURI *uri_free = NULL;
+ const char *host;
+ gchar *hostp = NULL;
+ gint port;
pw_ask_flags |= G_ASK_PASSWORD_SAVING_SUPPORTED;
if (is_proxy)
{
- g_object_get (session, SOUP_SESSION_PROXY_URI, &uri_free, NULL);
- uri = uri_free;
+ gboolean ret;
+ gchar *urip;
+
+ urip = g_strdup_printf ("http://%s", soup_auth_get_authority (auth));
+ ret = g_uri_split_network (urip, G_URI_FLAGS_NONE,
+ NULL, &hostp, &port, NULL);
+ g_free (urip);
+
+ if (!ret)
+ return FALSE;
+
+ host = hostp;
}
else
- uri = soup_message_get_uri (msg);
+ {
+ GUri *uri = soup_message_get_uri (msg);
+
+ host = g_uri_get_host (uri);
+ port = g_uri_get_port (uri);
+ }
res = g_vfs_keyring_lookup_password (info->username,
- uri->host,
+ host,
NULL,
"http",
realm,
is_proxy ? "proxy" : "basic",
- uri->port,
+ port,
&new_username,
NULL,
&new_password);
@@ -1572,14 +1659,14 @@ soup_authenticate_interactive (SoupSession *session,
info->password = new_password;
}
- if (uri_free)
- soup_uri_free (uri_free);
+ if (hostp)
+ g_free (hostp);
}
if (retrying == FALSE && have_auth)
{
soup_auth_authenticate (auth, info->username, info->password);
- return;
+ return FALSE;
}
if (is_proxy == FALSE)
@@ -1624,41 +1711,46 @@ soup_authenticate_interactive (SoupSession *session,
info->pw_save = pw_save;
}
else
- soup_session_cancel_message (session, msg, SOUP_STATUS_CANCELLED);
+ soup_auth_cancel (auth);
g_debug ("- soup_authenticate \n");
g_free (prompt);
+
+ return !res || aborted;
}
static void
keyring_save_authinfo (AuthInfo *info,
- SoupURI *uri,
+ GUri *uri,
gboolean is_proxy)
{
const char *type = is_proxy ? "proxy" : "basic";
g_vfs_keyring_save_password (info->username,
- uri->host,
+ g_uri_get_host (uri),
NULL,
"http",
info->realm,
type,
- uri->port,
+ g_uri_get_port (uri),
info->password,
info->pw_save);
}
/* ************************************************************************* */
-static SoupURI *
+static GUri *
g_mount_spec_to_dav_uri (GMountSpec *spec)
{
- SoupURI *uri;
+ GUri *uri;
const char *host;
const char *user;
const char *port;
const char *ssl;
const char *path;
+ const char *scheme;
+ char *host_str;
+ char *path_str;
gint port_num;
host = g_mount_spec_get (spec, "host");
@@ -1670,37 +1762,51 @@ g_mount_spec_to_dav_uri (GMountSpec *spec)
if (host == NULL || *host == 0)
return NULL;
- uri = soup_uri_new (NULL);
-
if (ssl != NULL && (strcmp (ssl, "true") == 0))
- soup_uri_set_scheme (uri, SOUP_URI_SCHEME_HTTPS);
+ scheme = "https";
else
- soup_uri_set_scheme (uri, SOUP_URI_SCHEME_HTTP);
+ scheme = "http";
- soup_uri_set_user (uri, user);
+ port_num = -1;
+
+ /* always a valid number if set */
+ if (port != NULL)
+ port_num = atoi (port);
/* IPv6 host does not include brackets in SoupURI, but GMountSpec host does */
if (gvfs_is_ipv6 (host))
- uri->host = g_strndup (host + 1, strlen (host) - 2);
+ host_str = g_strndup (host + 1, strlen (host) - 2);
else
- soup_uri_set_host (uri, host);
+ host_str = g_strdup (host);
- if (port && (port_num = atoi (port)))
- soup_uri_set_port (uri, port_num);
+ path_str = dav_uri_encode (path);
- g_free (uri->path);
- uri->path = dav_uri_encode (path);
+ uri = g_uri_build_with_user (SOUP_HTTP_URI_FLAGS,
+ scheme,
+ user,
+ NULL,
+ NULL,
+ host_str,
+ port_num,
+ path_str,
+ NULL,
+ NULL);
+
+ g_free (host_str);
+ g_free (path_str);
return uri;
}
static GMountSpec *
g_mount_spec_from_dav_uri (GVfsBackendDav *dav_backend,
- SoupURI *uri)
+ GUri *uri)
{
GMountSpec *spec;
- const char *ssl;
- char *local_path;
+ char *local_path;
+ gboolean ssl;
+ const gchar *user;
+ gint port_num;
#ifdef HAVE_AVAHI
if (dav_backend->resolver != NULL)
@@ -1724,36 +1830,35 @@ g_mount_spec_from_dav_uri (GVfsBackendDav *dav_backend,
spec = g_mount_spec_new ("dav");
- /* IPv6 host does not include brackets in SoupURI, but GMountSpec host does */
- if (strchr (uri->host, ':'))
+ /* IPv6 host does not include brackets in GUri, but GMountSpec host does */
+ if (strchr (g_uri_get_host (uri), ':'))
{
- char *host = g_strdup_printf ("[%s]", uri->host);
+ char *host = g_strdup_printf ("[%s]", g_uri_get_host (uri));
g_mount_spec_set (spec, "host", host);
g_free (host);
}
else
- g_mount_spec_set (spec, "host", uri->host);
+ g_mount_spec_set (spec, "host", g_uri_get_host (uri));
- if (uri->scheme == SOUP_URI_SCHEME_HTTPS)
- ssl = "true";
- else
- ssl = "false";
+ ssl = !strcmp (g_uri_get_scheme (uri), "https");
- g_mount_spec_set (spec, "ssl", ssl);
+ g_mount_spec_set (spec, "ssl", ssl ? "true" : "false");
- if (uri->user)
- g_mount_spec_set (spec, "user", uri->user);
+ user = g_uri_get_user (uri);
+ if (user != NULL)
+ g_mount_spec_set (spec, "user", user);
- if (! soup_uri_uses_default_port (uri))
+ port_num = g_uri_get_port (uri);
+ if (port_num > 0 && port_num != (ssl ? 443 : 80))
{
- char *port = g_strdup_printf ("%u", uri->port);
+ char *port = g_strdup_printf ("%d", port_num);
g_mount_spec_set (spec, "port", port);
g_free (port);
}
/* There must not be any illegal characters in the
URL at this point */
- local_path = g_uri_unescape_string (uri->path, "/");
+ local_path = g_uri_unescape_string (g_uri_get_path (uri), "/");
g_mount_spec_set_mount_prefix (spec, local_path);
g_free (local_path);
@@ -1761,15 +1866,17 @@ g_mount_spec_from_dav_uri (GVfsBackendDav *dav_backend,
}
#ifdef HAVE_AVAHI
-static SoupURI *
+static GUri *
dav_uri_from_dns_sd_resolver (GVfsBackendDav *dav_backend)
{
- SoupURI *uri;
+ GUri *uri;
char *user;
char *path;
char *address;
+ char *host;
gchar *interface;
const char *service_type;
+ const char *scheme;
guint port;
service_type = g_vfs_dns_sd_resolver_get_service_type (dav_backend->resolver);
@@ -1783,42 +1890,42 @@ dav_uri_from_dns_sd_resolver (GVfsBackendDav *dav_backend)
* there's also a TXT record "p" for password. Handle this.
*/
- uri = soup_uri_new (NULL);
-
if (strcmp (service_type, "_webdavs._tcp") == 0)
- soup_uri_set_scheme (uri, SOUP_URI_SCHEME_HTTPS);
+ scheme = "https";
else
- soup_uri_set_scheme (uri, SOUP_URI_SCHEME_HTTP);
-
- soup_uri_set_user (uri, user);
-
- soup_uri_set_port (uri, port);
+ scheme = "http";
- /* IPv6 host does not include brackets in SoupURI, but GVfsDnsSdResolver host does */
+ /* IPv6 host does not include brackets in GUri, but GVfsDnsSdResolver host does */
if (gvfs_is_ipv6 (address))
{
/* Link-local addresses require interface to be specified. */
if (g_str_has_prefix (address, "[fe80:") && interface != NULL)
{
- uri->host = g_strconcat (address + 1, interface, NULL);
- uri->host[strlen (address) - 2] = '%';
+ host = g_strconcat (address + 1, interface, NULL);
+ host[strlen (address) - 2] = '%';
}
else
- uri->host = g_strndup (address + 1, strlen (address) - 2);
+ host = g_strndup (address + 1, strlen (address) - 2);
}
else
- soup_uri_set_host (uri, address);
-
- if (path != NULL)
- soup_uri_set_path (uri, path);
- else
- soup_uri_set_path (uri, "/");
+ host = g_strdup (address);
+ uri = g_uri_build_with_user (SOUP_HTTP_URI_FLAGS,
+ scheme,
+ user,
+ NULL,
+ NULL,
+ host,
+ port,
+ path,
+ NULL,
+ NULL);
g_free (address);
g_free (interface);
g_free (user);
g_free (path);
+ g_free (host);
return uri;
}
@@ -1849,13 +1956,15 @@ do_mount (GVfsBackend *backend,
SoupSession *session;
SoupMessage *msg_opts;
SoupMessage *msg_stat;
- SoupURI *mount_base;
- gulong signal_id;
+ GUri *mount_base;
+ GUri *tmp;
+ GError *error = NULL;
guint status;
gboolean is_success;
gboolean is_webdav;
gboolean is_collection;
- gboolean auth_interactive;
+ gboolean sig_opts = TRUE;
+ gboolean sig_stat = TRUE;
gboolean res;
char *last_good_path;
const char *host;
@@ -1870,11 +1979,8 @@ do_mount (GVfsBackend *backend,
/* resolve DNS-SD style URIs */
if ((strcmp (type, "dav+sd") == 0 || strcmp (type, "davs+sd") == 0) && host != NULL)
{
- GError *error;
-
dav_backend->resolver = g_vfs_dns_sd_resolver_new_for_encoded_triple (host, "u");
- error = NULL;
if (!g_vfs_dns_sd_resolver_resolve_sync (dav_backend->resolver,
NULL,
&error))
@@ -1905,28 +2011,17 @@ do_mount (GVfsBackend *backend,
}
session = G_VFS_BACKEND_HTTP (backend)->session;
- G_VFS_BACKEND_HTTP (backend)->mount_base = mount_base;
+ G_VFS_BACKEND_HTTP (backend)->mount_base = mount_base;
soup_session_add_feature_by_type (session, SOUP_TYPE_AUTH_NEGOTIATE);
soup_session_add_feature_by_type (session, SOUP_TYPE_AUTH_NTLM);
- /* Override the HTTP backend's default. */
- g_object_set (session,
- "ssl-strict", TRUE,
- SOUP_SESSION_MAX_CONNS_PER_HOST, MAX_CONNS,
- SOUP_SESSION_MAX_CONNS, MAX_CONNS,
- NULL);
-
data = &(G_VFS_BACKEND_DAV (backend)->auth_info);
data->mount_source = g_object_ref (mount_source);
- data->server_auth.username = g_strdup (mount_base->user);
+ 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;
-
- signal_id = g_signal_connect (session, "authenticate",
- G_CALLBACK (soup_authenticate_interactive),
- data);
- auth_interactive = TRUE;
+ data->interactive = TRUE;
last_good_path = NULL;
msg_opts = soup_message_new_from_uri (SOUP_METHOD_OPTIONS, mount_base);
@@ -1936,12 +2031,16 @@ do_mount (GVfsBackend *backend,
msg_stat = stat_location_begin (mount_base, TRUE);
do {
+ GInputStream *body;
GFileType file_type;
- SoupURI *cur_uri;
+ GUri *cur_uri;
+ char *new_path;
res = TRUE;
- status = g_vfs_backend_dav_send_message (backend, msg_opts);
- is_success = SOUP_STATUS_IS_SUCCESSFUL (status);
+ 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
@@ -1956,7 +2055,7 @@ do_mount (GVfsBackend *backend,
data->retrying_after_403 = TRUE;
g_clear_pointer (&data->server_auth.username, g_free);
- data->server_auth.username = g_strdup (mount_base->user);
+ 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);
@@ -1975,23 +2074,26 @@ do_mount (GVfsBackend *backend,
* 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 (status == SOUP_STATUS_SSL_FAILED &&
+ if (g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE) &&
!dav_backend->certificate_errors)
{
GTlsCertificate *certificate;
GTlsCertificateFlags errors;
- soup_message_get_https_status (msg_opts, &certificate, &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_object_set (session, "ssl-strict", FALSE, NULL);
+ 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;
}
}
@@ -1999,46 +2101,61 @@ do_mount (GVfsBackend *backend,
if (!is_success || !is_webdav)
break;
- soup_message_headers_clear (msg_opts->response_headers);
- soup_message_body_truncate (msg_opts->response_body);
+ if (!g_vfs_backend_dav_stream_skip (body, &error))
+ {
+ g_object_unref (body);
+ break;
+ }
+
+ g_object_unref (body);
+
+ soup_message_headers_clear (soup_message_get_response_headers (msg_opts));
cur_uri = soup_message_get_uri (msg_opts);
soup_message_set_uri (msg_stat, cur_uri);
- g_vfs_backend_dav_send_message (backend, msg_stat);
- res = stat_location_finish (msg_stat, &file_type, NULL, NULL);
+ 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;
+ if (body && !g_vfs_backend_dav_stream_skip (body, &error))
+ {
+ g_object_unref (body);
+ break;
+ }
+
+ g_clear_object (&body);
+
+ soup_message_headers_clear (soup_message_get_response_headers (msg_stat));
+
g_debug (" [%s] webdav: %d, collection %d [res: %d]\n",
- mount_base->path, is_webdav, is_collection, res);
+ g_uri_get_path (mount_base), is_webdav, is_collection, res);
if (is_collection == FALSE)
break;
/* we have found a new good root, try the parent ... */
g_free (last_good_path);
- last_good_path = mount_base->path;
- mount_base->path = path_get_parent_dir (mount_base->path);
+ last_good_path = g_strdup (g_uri_get_path (mount_base));
+ new_path = path_get_parent_dir (last_good_path);
+
+ tmp = mount_base;
+ mount_base = dav_uri_dup_with (mount_base, new_path, NULL);
+ g_uri_unref (tmp);
+ G_VFS_BACKEND_HTTP (backend)->mount_base = mount_base;
+
+ g_free (new_path);
+
soup_message_set_uri (msg_opts, mount_base);
- if (auth_interactive)
- {
- /* 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 */
-
- g_signal_handler_disconnect (session, signal_id);
- g_signal_connect (session, "authenticate",
- G_CALLBACK (soup_authenticate_from_data),
- data);
- auth_interactive = FALSE;
- }
-
- soup_message_headers_clear (msg_stat->response_headers);
- soup_message_body_truncate (msg_stat->response_body);
+ /* 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;
} while (g_strcmp0 (last_good_path, "/") != 0);
@@ -2049,8 +2166,13 @@ do_mount (GVfsBackend *backend,
/* check if we at all have a good path */
if (last_good_path == NULL)
{
- if ((is_success && !is_webdav) ||
- msg_opts->status_code == SOUP_STATUS_METHOD_NOT_ALLOWED)
+ if (error)
+ {
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ }
+ 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
@@ -2068,14 +2190,12 @@ do_mount (GVfsBackend *backend,
/* 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 (target->status_code);
-
- if (error_code == G_IO_ERROR_CANCELLED)
- error_code = G_IO_ERROR_FAILED_HANDLED;
+ 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"), target->reason_phrase);
+ _("HTTP Error: %s"),
+ soup_message_get_reason_phrase (target));
}
else
{
@@ -2091,6 +2211,8 @@ do_mount (GVfsBackend *backend,
return;
}
+ else if (error)
+ g_error_free (error);
/* Success! We are mounted */
/* Save the auth info in the keyring */
@@ -2099,8 +2221,12 @@ do_mount (GVfsBackend *backend,
/* TODO: save proxy auth */
/* Set the working path in mount path */
- g_free (mount_base->path);
- mount_base->path = last_good_path;
+ tmp = mount_base;
+ mount_base = dav_uri_dup_with (mount_base, last_good_path, NULL);
+ g_uri_unref (tmp);
+ g_clear_pointer (&G_VFS_BACKEND_HTTP (backend)->mount_base, g_uri_unref);
+ G_VFS_BACKEND_HTTP (backend)->mount_base = mount_base;
+ g_free (last_good_path);
/* dup the mountspec, but only copy known fields */
mount_spec = g_mount_spec_from_dav_uri (dav_backend, mount_base);
@@ -2141,39 +2267,35 @@ do_query_info (GVfsBackend *backend,
GFileAttributeMatcher *matcher)
{
SoupMessage *msg;
- Multistatus ms;
- xmlNodeIter iter;
- gboolean res;
- GError *error;
-
- error = NULL;
+ GInputStream *body;
+ 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);
- g_vfs_backend_dav_send_message (backend, msg);
+ body = g_vfs_backend_dav_send (backend, msg, TRUE, &error);
+
+ if (!body)
+ goto error;
- res = multistatus_parse (msg, &ms, &error);
+ res = multistatus_parse (msg, body, &ms, &error);
+ g_object_unref (body);
if (res == FALSE)
- {
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- g_object_unref (msg);
- return;
- }
+ goto error;
res = FALSE;
multistatus_get_response_iter (&ms, &iter);
@@ -2203,7 +2325,12 @@ do_query_info (GVfsBackend *backend,
g_vfs_job_failed (G_VFS_JOB (job),
G_IO_ERROR, G_IO_ERROR_FAILED,
_("Response invalid"));
+ return;
+ error:
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ g_object_unref (msg);
}
static PropName fs_info_propnames[] = {
@@ -2220,10 +2347,11 @@ do_query_fs_info (GVfsBackend *backend,
GFileAttributeMatcher *attribute_matcher)
{
SoupMessage *msg;
- Multistatus ms;
- xmlNodeIter iter;
- gboolean res;
- GError *error;
+ GInputStream *body;
+ Multistatus ms;
+ xmlNodeIter iter;
+ gboolean res;
+ GError *error = NULL;
g_file_info_set_attribute_string (info,
G_FILE_ATTRIBUTE_FILESYSTEM_TYPE,
@@ -2247,7 +2375,6 @@ do_query_fs_info (GVfsBackend *backend,
}
msg = propfind_request_new (backend, filename, 0, fs_info_propnames);
-
if (msg == NULL)
{
g_vfs_job_failed (G_VFS_JOB (job),
@@ -2257,18 +2384,16 @@ do_query_fs_info (GVfsBackend *backend,
return;
}
- g_vfs_backend_dav_send_message (backend, msg);
+ body = g_vfs_backend_dav_send (backend, msg, TRUE, &error);
+
+ if (!body)
+ goto error;
- error = NULL;
- res = multistatus_parse (msg, &ms, &error);
+ res = multistatus_parse (msg, body, &ms, &error);
+ g_object_unref (body);
if (res == FALSE)
- {
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- g_object_unref (msg);
- return;
- }
+ goto error;
res = FALSE;
multistatus_get_response_iter (&ms, &iter);
@@ -2298,7 +2423,12 @@ do_query_fs_info (GVfsBackend *backend,
g_vfs_job_failed (G_VFS_JOB (job),
G_IO_ERROR, G_IO_ERROR_FAILED,
_("Response invalid"));
+ return;
+ error:
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ g_object_unref (msg);
}
/* *** enumerate *** */
@@ -2310,17 +2440,15 @@ do_enumerate (GVfsBackend *backend,
GFileQueryInfoFlags flags)
{
SoupMessage *msg;
- Multistatus ms;
- xmlNodeIter iter;
- gboolean res;
- GError *error;
-
- error = NULL;
+ GInputStream *body;
+ Multistatus ms;
+ xmlNodeIter iter;
+ gboolean res;
+ GError *error = NULL;
g_debug ("+ do_enumerate: %s\n", filename);
msg = propfind_request_new (backend, filename, 1, ls_propnames);
-
if (msg == NULL)
{
g_vfs_job_failed (G_VFS_JOB (job),
@@ -2332,17 +2460,17 @@ do_enumerate (GVfsBackend *backend,
message_add_redirect_header (msg, flags);
- g_vfs_backend_dav_send_message (backend, msg);
+ body = g_vfs_backend_dav_send (backend, msg, TRUE, &error);
+
+ if (!body)
+ goto error;
- res = multistatus_parse (msg, &ms, &error);
+ res = multistatus_parse (msg, body, &ms, &error);
+ g_object_unref (body);
if (res == FALSE)
- {
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- g_object_unref (msg);
- return;
- }
+ goto error;
+
g_vfs_job_succeeded (G_VFS_JOB (job));
multistatus_get_response_iter (&ms, &iter);
@@ -2370,6 +2498,12 @@ do_enumerate (GVfsBackend *backend,
g_object_unref (msg);
g_vfs_job_enumerate_done (G_VFS_JOB_ENUMERATE (job));
+ return;
+
+ error:
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ g_object_unref (msg);
}
/* ************************************************************************* */
@@ -2377,37 +2511,51 @@ do_enumerate (GVfsBackend *backend,
/* *** open () *** */
static void
-try_open_stat_done (SoupSession *session,
- SoupMessage *msg,
- gpointer user_data)
+try_open_stat_done (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
{
- GVfsJob *job = G_VFS_JOB (user_data);
- GVfsBackend *backend = job->backend_data;
- GFileType target_type;
- SoupURI *uri;
- gboolean res;
+ GInputStream *body;
+ SoupSession *session = SOUP_SESSION (source);
+ SoupMessage *msg;
+ GVfsJob *job = G_VFS_JOB (user_data);
+ GVfsBackend *backend = job->backend_data;
+ GError *error = NULL;
+ GUri *uri;
+ GFileType target_type;
+ gboolean res;
- if (msg->status_code != 207)
+ msg = soup_session_get_async_result_message (session, result);
+ if (soup_message_get_status (msg) != SOUP_STATUS_MULTI_STATUS)
{
http_job_failed (job, msg);
return;
}
- res = stat_location_finish (msg, &target_type, NULL, NULL);
+ body = soup_session_send_finish (session, result, &error);
+ if (!body)
+ {
+ g_vfs_job_failed_from_error (job, error);
+ g_error_free (error);
+ return;
+ }
+
+ res = stat_location_finish (msg, body, &target_type, NULL, NULL);
+ g_object_unref (body);
if (res == FALSE)
{
g_vfs_job_failed (job,
- G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Response invalid"));
+ G_IO_ERROR, G_IO_ERROR_FAILED,
+ _("Response invalid"));
return;
}
if (target_type == G_FILE_TYPE_DIRECTORY)
{
g_vfs_job_failed (job,
- G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
- _("File is directory"));
+ G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
+ _("File is directory"));
return;
}
@@ -2422,12 +2570,12 @@ try_open_for_read (GVfsBackend *backend,
GVfsJobOpenForRead *job,
const char *filename)
{
- SoupMessage *msg;
- SoupURI *uri;
+ SoupMessage *msg;
+ GUri *uri;
uri = g_vfs_backend_dav_uri_for_path (backend, filename, FALSE);
msg = stat_location_begin (uri, FALSE);
- soup_uri_free (uri);
+ g_uri_unref (uri);
if (msg == NULL)
{
@@ -2439,7 +2587,7 @@ try_open_for_read (GVfsBackend *backend,
}
g_vfs_job_set_backend_data (G_VFS_JOB (job), backend, NULL);
- g_vfs_backend_dav_queue_message (backend, msg, try_open_stat_done, job);
+ g_vfs_backend_dav_send_async (backend, msg, TRUE, try_open_stat_done, job);
return TRUE;
}
@@ -2449,16 +2597,29 @@ try_open_for_read (GVfsBackend *backend,
/* *** create () *** */
static void
-try_create_tested_existence (SoupSession *session, SoupMessage *msg,
+try_create_tested_existence (GObject *source,
+ GAsyncResult *result,
gpointer user_data)
{
GVfsJob *job = G_VFS_JOB (user_data);
- GOutputStream *stream;
- SoupMessage *put_msg;
- SoupURI *uri;
+ GOutputStream *stream;
+ GInputStream *body;
+ SoupMessage *msg = job->backend_data;
+ SoupMessage *put_msg;
+ GError *error = NULL;
+ GUri *uri;
- if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
+ body = soup_session_send_finish (SOUP_SESSION (source), result, &error);
+ if (!body)
{
+ g_vfs_job_failed_from_error (job, error);
+ g_error_free (error);
+ return;
+ }
+
+ if (SOUP_STATUS_IS_SUCCESSFUL (soup_message_get_status (msg)))
+ {
+ g_object_unref (body);
g_vfs_job_failed (job,
G_IO_ERROR,
G_IO_ERROR_EXISTS,
@@ -2467,12 +2628,14 @@ try_create_tested_existence (SoupSession *session, SoupMessage *msg,
}
/* TODO: other errors */
+ g_object_unref (body);
+
uri = soup_message_get_uri (msg);
put_msg = soup_message_new_from_uri (SOUP_METHOD_PUT, uri);
/*
* Doesn't work with apache > 2.2.9
- * soup_message_headers_append (put_msg->request_headers, "If-None-Match", "*");
+ * soup_message_headers_append (soup_message_get_request_headers (put_msg), "If-None-Match", "*");
*/
stream = g_memory_output_stream_new (NULL, 0, g_try_realloc, g_free);
g_object_set_data_full (G_OBJECT (stream), "-gvfs-stream-msg", put_msg, g_object_unref);
@@ -2492,25 +2655,28 @@ try_create (GVfsBackend *backend,
GFileCreateFlags flags)
{
SoupMessage *msg;
- SoupURI *uri;
+ GUri *uri;
/* TODO: if we supported chunked requests, we could
* use a PUT with "If-None-Match: *" and "Expect: 100-continue"
*/
uri = g_vfs_backend_dav_uri_for_path (backend, filename, FALSE);
msg = soup_message_new_from_uri (SOUP_METHOD_HEAD, uri);
- soup_uri_free (uri);
+ g_uri_unref (uri);
- g_vfs_job_set_backend_data (G_VFS_JOB (job), backend, NULL);
+ 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);
- g_vfs_backend_dav_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)
+open_for_replace_succeeded (GVfsBackendHttp *op_backend,
+ GVfsJob *job,
+ GUri *uri,
+ const char *etag)
{
SoupMessage *put_msg;
GOutputStream *stream;
@@ -2518,7 +2684,9 @@ open_for_replace_succeeded (GVfsBackendHttp *op_backend, GVfsJob *job,
put_msg = soup_message_new_from_uri (SOUP_METHOD_PUT, uri);
if (etag)
- soup_message_headers_append (put_msg->request_headers, "If-Match", etag);
+ soup_message_headers_append (soup_message_get_request_headers (put_msg),
+ "If-Match",
+ etag);
stream = g_memory_output_stream_new (NULL, 0, g_try_realloc, g_free);
g_object_set_data_full (G_OBJECT (stream), "-gvfs-stream-msg", put_msg, g_object_unref);
@@ -2532,14 +2700,29 @@ open_for_replace_succeeded (GVfsBackendHttp *op_backend, GVfsJob *job,
}
static void
-try_replace_checked_etag (SoupSession *session, SoupMessage *msg,
- gpointer user_data)
+try_replace_checked_etag (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
{
GVfsJob *job = G_VFS_JOB (user_data);
GVfsBackendHttp *op_backend = job->backend_data;
+ GInputStream *body;
+ SoupSession *session = SOUP_SESSION (source);
+ SoupMessage *msg;
+ GError *error;
- if (msg->status_code == SOUP_STATUS_PRECONDITION_FAILED)
+ msg = soup_session_get_async_result_message (session, result);
+ body = soup_session_send_finish (session, result, &error);
+ if (!body)
{
+ g_vfs_job_failed_from_error (job, error);
+ g_error_free (error);
+ return;
+ }
+
+ if (soup_message_get_status (msg) == SOUP_STATUS_PRECONDITION_FAILED)
+ {
+ g_object_unref (body);
g_vfs_job_failed (G_VFS_JOB (job),
G_IO_ERROR,
G_IO_ERROR_WRONG_ETAG,
@@ -2548,8 +2731,11 @@ try_replace_checked_etag (SoupSession *session, SoupMessage *msg,
}
/* TODO: other errors */
+ g_object_unref (body);
+
open_for_replace_succeeded (op_backend, job, soup_message_get_uri (msg),
- soup_message_headers_get_one (msg->request_headers, "If-Match"));
+ soup_message_headers_get_one (soup_message_get_request_headers (msg),
+ "If-Match"));
}
static gboolean
@@ -2561,7 +2747,7 @@ try_replace (GVfsBackend *backend,
GFileCreateFlags flags)
{
GVfsBackendHttp *op_backend;
- SoupURI *uri;
+ GUri *uri;
/* TODO: if SoupOutputStream supported chunked requests, we could
* use a PUT with "If-Match: ..." and "Expect: 100-continue"
@@ -2587,17 +2773,21 @@ try_replace (GVfsBackend *backend,
SoupMessage *msg;
msg = soup_message_new_from_uri (SOUP_METHOD_HEAD, uri);
- soup_uri_free (uri);
- soup_message_headers_append (msg->request_headers, "If-Match", etag);
+ g_uri_unref (uri);
+
+ 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_queue_message (backend, msg,
- try_replace_checked_etag, job);
+ g_vfs_backend_dav_send_async (backend, msg, TRUE,
+ try_replace_checked_etag, job);
+
return TRUE;
}
open_for_replace_succeeded (op_backend, G_VFS_JOB (job), uri, NULL);
- soup_uri_free (uri);
+ g_uri_unref (uri);
+
return TRUE;
}
@@ -2698,14 +2888,27 @@ do_truncate (GVfsBackend *backend,
/* *** close_write () *** */
static void
-try_close_write_sent (SoupSession *session,
- SoupMessage *msg,
- gpointer user_data)
+try_close_write_sent (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
{
- GVfsJob *job;
+ GInputStream *body;
+ GVfsJob *job = G_VFS_JOB (user_data);
+ SoupSession *session = SOUP_SESSION (source);
+ SoupMessage *msg = soup_session_get_async_result_message (session, result);
+ GError *error;
- job = G_VFS_JOB (user_data);
- if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
+ body = soup_session_send_finish (session, result, &error);
+ if (!body)
+ {
+ g_vfs_job_failed_from_error (job, error);
+ g_error_free (error);
+ return;
+ }
+
+ g_object_unref (body);
+
+ if (!SOUP_STATUS_IS_SUCCESSFUL (soup_message_get_status (msg)))
http_job_failed (job, msg);
else
g_vfs_job_succeeded (job);
@@ -2718,8 +2921,7 @@ try_close_write (GVfsBackend *backend,
{
GOutputStream *stream;
SoupMessage *msg;
- gsize length;
- gchar *data;
+ GBytes *bytes;
stream = G_OUTPUT_STREAM (handle);
@@ -2728,13 +2930,13 @@ try_close_write (GVfsBackend *backend,
g_object_set_data (G_OBJECT (stream), "-gvfs-stream-msg", NULL);
g_output_stream_close (stream, NULL, NULL);
- length = g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (stream));
- data = g_memory_output_stream_steal_data (G_MEMORY_OUTPUT_STREAM (stream));
+ bytes = g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (stream));
g_object_unref (stream);
- soup_message_body_append (msg->request_body, SOUP_MEMORY_TAKE, data, length);
- g_vfs_backend_dav_queue_message (backend, msg,
- try_close_write_sent, job);
+ soup_message_set_request_body_from_bytes (msg, NULL, bytes);
+ g_vfs_backend_dav_send_async (backend, msg, TRUE,
+ try_close_write_sent, job);
+ g_bytes_unref (bytes);
return TRUE;
}
@@ -2744,15 +2946,26 @@ do_make_directory (GVfsBackend *backend,
GVfsJobMakeDirectory *job,
const char *filename)
{
+ GInputStream *body;
SoupMessage *msg;
- SoupURI *uri;
- guint status;
+ 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);
- soup_uri_free (uri);
+ g_uri_unref (uri);
+
+ body = g_vfs_backend_dav_send (backend, msg, TRUE, &error);
+ if (!body)
+ {
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ g_object_unref (msg);
+ return;
+ }
- status = g_vfs_backend_dav_send_message (backend, msg);
+ status = soup_message_get_status (msg);
if (! SOUP_STATUS_IS_SUCCESSFUL (status))
if (status == SOUP_STATUS_METHOD_NOT_ALLOWED)
@@ -2764,6 +2977,7 @@ do_make_directory (GVfsBackend *backend,
else
g_vfs_job_succeeded (G_VFS_JOB (job));
+ g_object_unref (body);
g_object_unref (msg);
}
@@ -2772,24 +2986,22 @@ do_delete (GVfsBackend *backend,
GVfsJobDelete *job,
const char *filename)
{
+ GInputStream *body;
SoupMessage *msg;
- SoupURI *uri;
- GFileType file_type;
- gboolean res;
- guint num_children;
- guint status;
- GError *error;
-
- error = NULL;
+ GUri *uri;
+ GFileType file_type;
+ gboolean res;
+ guint num_children;
+ guint status;
+ GError *error = NULL;
uri = g_vfs_backend_dav_uri_for_path (backend, filename, FALSE);
res = stat_location (backend, uri, &file_type, NULL, &num_children, &error);
-
if (res == FALSE)
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
g_error_free (error);
- soup_uri_free (uri);
+ g_uri_unref (uri);
return;
}
@@ -2798,21 +3010,30 @@ do_delete (GVfsBackend *backend,
g_vfs_job_failed (G_VFS_JOB (job),
G_IO_ERROR, G_IO_ERROR_NOT_EMPTY,
_("Directory not empty"));
- soup_uri_free (uri);
+ 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);
+ 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;
+ }
- status = g_vfs_backend_dav_send_message (backend, msg);
-
+ 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));
- soup_uri_free (uri);
+ g_uri_unref (uri);
g_object_unref (msg);
+ g_object_unref (body);
}
static void
@@ -2821,12 +3042,14 @@ do_set_display_name (GVfsBackend *backend,
const char *filename,
const char *display_name)
{
+ GInputStream *body;
SoupMessage *msg;
- SoupURI *source;
- SoupURI *target;
- char *target_path;
- char *dirname;
- guint status;
+ 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);
@@ -2838,7 +3061,14 @@ do_set_display_name (GVfsBackend *backend,
message_add_destination_header (msg, target);
message_add_overwrite_header (msg, FALSE);
- status = g_vfs_backend_dav_send_message (backend, msg);
+ body = g_vfs_backend_dav_send (backend, msg, TRUE, &error);
+ if (!body)
+ {
+ http_job_failed (G_VFS_JOB (job), msg);
+ goto error;
+ }
+
+ status = soup_message_get_status (msg);
/*
* The precondition of SOUP_STATUS_PRECONDITION_FAILED (412) in
@@ -2870,11 +3100,14 @@ do_set_display_name (GVfsBackend *backend,
else
http_job_failed (G_VFS_JOB (job), msg);
+ g_object_unref (body);
+
+ error:
g_object_unref (msg);
g_free (dirname);
g_free (target_path);
- soup_uri_free (target);
- soup_uri_free (source);
+ g_uri_unref (target);
+ g_uri_unref (source);
}
static void
@@ -2886,9 +3119,10 @@ do_move (GVfsBackend *backend,
GFileProgressCallback progress_callback,
gpointer progress_callback_data)
{
+ GInputStream *body = NULL;
SoupMessage *msg;
- SoupURI *source_uri;
- SoupURI *target_uri;
+ GUri *source_uri;
+ GUri *target_uri;
guint status;
GFileType source_ft, target_ft;
GError *error = NULL;
@@ -2958,14 +3192,20 @@ do_move (GVfsBackend *backend,
msg = soup_message_new_from_uri (SOUP_METHOD_DELETE,
target_uri);
- status = g_vfs_backend_dav_send_message (backend, msg);
+ 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);
- g_object_unref (msg);
goto error;
}
+ g_object_unref (body);
g_object_unref (msg);
}
}
@@ -2988,11 +3228,16 @@ do_move (GVfsBackend *backend,
message_add_destination_header (msg, target_uri);
message_add_overwrite_header (msg, flags & G_FILE_COPY_OVERWRITE);
- status = g_vfs_backend_dav_send_message (backend, msg);
+ 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;
+ }
/* 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))
{
if (stat_res && progress_callback)
@@ -3007,11 +3252,12 @@ do_move (GVfsBackend *backend,
else
http_job_failed (G_VFS_JOB (job), msg);
-error:
+ error:
+ g_clear_object (&body);
g_object_unref (msg);
g_clear_error (&error);
- soup_uri_free (source_uri);
- soup_uri_free (target_uri);
+ g_uri_unref (source_uri);
+ g_uri_unref (target_uri);
}
static void
@@ -3023,9 +3269,10 @@ do_copy (GVfsBackend *backend,
GFileProgressCallback progress_callback,
gpointer progress_callback_data)
{
+ GInputStream *body;
SoupMessage *msg;
- SoupURI *source_uri;
- SoupURI *target_uri;
+ GUri *source_uri;
+ GUri *target_uri;
guint status;
GFileType source_ft, target_ft;
GError *error = NULL;
@@ -3102,11 +3349,17 @@ do_copy (GVfsBackend *backend,
message_add_destination_header (msg, target_uri);
message_add_overwrite_header (msg, flags & G_FILE_COPY_OVERWRITE);
- status = g_vfs_backend_dav_send_message (backend, msg);
+ body = g_vfs_backend_dav_send (backend, msg, TRUE, &error);
+ if (!body)
+ {
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_object_unref (msg);
+ goto error;
+ }
/* 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))
{
if (progress_callback)
@@ -3121,24 +3374,17 @@ do_copy (GVfsBackend *backend,
else
http_job_failed (G_VFS_JOB (job), msg);
+ g_object_unref (body);
g_object_unref (msg);
error:
g_clear_error (&error);
- soup_uri_free (source_uri);
- soup_uri_free (target_uri);
+ g_uri_unref (source_uri);
+ g_uri_unref (target_uri);
}
#define CHUNK_SIZE 65536
-/* Used to keep track of the state of reads in flight when the restarted signal
- * is received. */
-typedef enum {
- PUSH_READ_STATUS_NONE,
- PUSH_READ_STATUS_RESET,
- PUSH_READ_STATUS_DEFERRED,
-} PushReadStatus;
-
typedef struct {
/* Job details */
GVfsBackend *backend;
@@ -3147,21 +3393,15 @@ typedef struct {
/* Local file */
GInputStream *in;
- unsigned char *buf;
goffset size;
- goffset n_read;
- PushReadStatus read_status;
/* Remote file */
- SoupURI *uri;
+ GUri *uri;
SoupMessage *msg;
goffset n_written;
} PushHandle;
static void
-push_write_next_chunk (SoupMessage *msg, gpointer user_data);
-
-static void
push_handle_free (PushHandle *handle)
{
if (handle->in)
@@ -3171,121 +3411,20 @@ push_handle_free (PushHandle *handle)
}
g_object_unref (handle->backend);
g_object_unref (handle->job);
- soup_uri_free (handle->uri);
- g_slice_free (PushHandle, handle);
-}
+ g_object_unref (handle->msg);
+ g_uri_unref (handle->uri);
-static void
-push_read_cb (GObject *source, GAsyncResult *res, gpointer user_data)
-{
- PushHandle *handle = user_data;
- GError *error = NULL;
- gssize n;
-
- n = g_input_stream_read_finish (handle->in, res, &error);
-
- /* Ignore this read if we've subsequently been restarted. */
- if (handle->read_status != PUSH_READ_STATUS_NONE)
- {
- g_free (handle->buf);
- handle->buf = NULL;
-
- /* Queue another read if we've been subsequently restarted and
- * push_write_next_chunk () was called in the meantime. */
- if (handle->read_status == PUSH_READ_STATUS_DEFERRED)
- push_write_next_chunk (handle->msg, handle);
-
- return;
- }
-
- if (n > 0)
- {
- soup_message_body_append_take (handle->msg->request_body, handle->buf, n);
- handle->buf = NULL;
- handle->n_read += n;
- soup_session_unpause_message (G_VFS_BACKEND_HTTP (handle->backend)->session,
- handle->msg);
- }
- else if (n == 0)
- {
- g_free (handle->buf);
- handle->buf = NULL;
-
- if (handle->n_read != handle->size)
- {
- g_vfs_job_failed_literal (handle->job,
- G_IO_ERROR,
- G_IO_ERROR_FAILED,
- _("File length changed during transfer"));
-
- soup_session_cancel_message (G_VFS_BACKEND_HTTP (handle->backend)->session,
- handle->msg,
- SOUP_STATUS_CANCELLED);
- }
- }
- else
- {
- g_free (handle->buf);
- handle->buf = NULL;
- g_vfs_job_failed_from_error (handle->job, error);
- g_error_free (error);
- soup_session_cancel_message (G_VFS_BACKEND_HTTP (handle->backend)->session,
- handle->msg,
- SOUP_STATUS_CANCELLED);
- }
-}
-
-static void
-push_write_next_chunk (SoupMessage *msg, gpointer user_data)
-{
- PushHandle *handle = user_data;
-
- /* If we've been restarted, seek to the beginning of the file. */
- if (handle->read_status == PUSH_READ_STATUS_RESET)
- {
- GError *error = NULL;
-
- /* We've been restarted but there's still a read in flight, so defer. */
- if (handle->buf)
- {
- handle->read_status = PUSH_READ_STATUS_DEFERRED;
- return;
- }
-
- handle->n_read = 0;
- handle->n_written = 0;
- handle->read_status = PUSH_READ_STATUS_NONE;
-
- if (!g_seekable_seek (G_SEEKABLE (handle->in),
- 0, G_SEEK_SET,
- handle->job->cancellable, &error))
- {
- g_vfs_job_failed_from_error (handle->job, error);
- g_error_free (error);
- soup_session_cancel_message (G_VFS_BACKEND_HTTP (handle->backend)->session,
- handle->msg,
- SOUP_STATUS_CANCELLED);
- return;
- }
- }
-
- handle->buf = g_malloc (CHUNK_SIZE);
- g_input_stream_read_async (handle->in,
- handle->buf, CHUNK_SIZE,
- 0, handle->job->cancellable,
- push_read_cb, handle);
+ g_slice_free (PushHandle, handle);
}
static void
push_setup_message (PushHandle *handle)
{
- soup_message_set_flags (handle->msg, SOUP_MESSAGE_CAN_REBUILD);
- soup_message_body_set_accumulate (handle->msg->request_body, FALSE);
message_add_overwrite_header (handle->msg,
handle->op_job->flags & G_FILE_COPY_OVERWRITE);
- soup_message_headers_set_encoding (handle->msg->request_headers,
+ soup_message_headers_set_encoding (soup_message_get_request_headers (handle->msg),
SOUP_ENCODING_CONTENT_LENGTH);
- soup_message_headers_set_content_length (handle->msg->request_headers,
+ soup_message_headers_set_content_length (soup_message_get_request_headers (handle->msg),
handle->size);
}
@@ -3294,28 +3433,31 @@ push_restarted (SoupMessage *msg, gpointer user_data)
{
PushHandle *handle = user_data;
- handle->read_status = PUSH_READ_STATUS_RESET;
- msg->method = SOUP_METHOD_PUT;
+ handle->n_written = 0;
+
+ g_object_set (msg, "method", SOUP_METHOD_PUT, NULL);
push_setup_message (handle);
+
+ soup_message_set_request_body (handle->msg, NULL, handle->in, handle->size);
}
static void
-push_wrote_body_data (SoupMessage *msg, SoupBuffer *chunk, gpointer user_data)
+push_wrote_body_data (SoupMessage *msg, guint chunk_size, gpointer user_data)
{
PushHandle *handle = user_data;
- handle->n_written += chunk->length;
+ handle->n_written += chunk_size;
g_vfs_job_progress_callback (handle->n_written, handle->size, handle->job);
}
static void
-push_done (SoupSession *session, SoupMessage *msg, gpointer user_data)
+push_finished (SoupMessage *msg, gpointer user_data)
{
PushHandle *handle = user_data;
if (g_vfs_job_is_finished (handle->job))
; /* We got an error so we finished the job and cancelled msg. */
- else if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
+ else if (!SOUP_STATUS_IS_SUCCESSFUL (soup_message_get_status (msg)))
http_job_failed (handle->job, msg);
else
{
@@ -3329,15 +3471,43 @@ push_done (SoupSession *session, SoupMessage *msg, gpointer user_data)
}
static void
-push_stat_dest_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
+push_done (GObject *source, GAsyncResult *result, gpointer user_data)
+{
+ GInputStream *body;
+ GVfsJob *job = G_VFS_JOB (user_data);;
+ GError *error = NULL;
+
+ body = soup_session_send_finish (SOUP_SESSION (source), result, &error);
+ if (!body)
+ {
+ g_vfs_job_failed_from_error (job, error);
+ g_error_free (error);
+ }
+ else
+ g_object_unref (body);
+}
+
+static void
+push_stat_dest_cb (GObject *source, GAsyncResult *result, gpointer user_data)
{
+ GInputStream *body;
PushHandle *handle = user_data;
GFileType type;
+ GError *error = NULL;
- if (stat_location_finish (msg, &type, NULL, NULL))
+ body = soup_session_send_finish (SOUP_SESSION (source), result, &error);
+ if (!body)
+ {
+ g_vfs_job_failed_from_error (handle->job, error);
+ g_error_free (error);
+ return;
+ }
+
+ if (stat_location_finish (handle->msg, body, &type, NULL, NULL))
{
if (!(handle->op_job->flags & G_FILE_COPY_OVERWRITE))
{
+ g_object_unref (body);
g_vfs_job_failed (handle->job,
G_IO_ERROR,
G_IO_ERROR_EXISTS,
@@ -3347,6 +3517,7 @@ push_stat_dest_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
}
if (type == G_FILE_TYPE_DIRECTORY)
{
+ g_object_unref (body);
g_vfs_job_failed (handle->job,
G_IO_ERROR,
G_IO_ERROR_IS_DIRECTORY,
@@ -3356,20 +3527,23 @@ push_stat_dest_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
}
}
+ g_object_unref (body);
+ g_object_unref (handle->msg);
+
handle->msg = soup_message_new_from_uri (SOUP_METHOD_PUT, handle->uri);
push_setup_message (handle);
+ soup_message_set_request_body (handle->msg, NULL, handle->in, handle->size);
+
g_signal_connect (handle->msg, "restarted",
G_CALLBACK (push_restarted), handle);
- g_signal_connect (handle->msg, "wrote_headers",
- G_CALLBACK (push_write_next_chunk), handle);
- g_signal_connect (handle->msg, "wrote_chunk",
- G_CALLBACK (push_write_next_chunk), handle);
g_signal_connect (handle->msg, "wrote-body-data",
G_CALLBACK (push_wrote_body_data), handle);
+ g_signal_connect (handle->msg, "finished",
+ G_CALLBACK (push_finished), handle);
- g_vfs_backend_dav_queue_message (handle->backend, handle->msg,
- push_done, handle);
+ g_vfs_backend_dav_send_async (handle->backend, handle->msg, TRUE,
+ push_done, handle->job);
}
static void
@@ -3383,14 +3557,12 @@ push_source_fstat_cb (GObject *source, GAsyncResult *res, gpointer user_data)
info = g_file_input_stream_query_info_finish (fin, res, &error);
if (info)
{
- SoupMessage *msg;
-
handle->size = g_file_info_get_size (info);
g_object_unref (info);
- msg = stat_location_begin (handle->uri, FALSE);
- g_vfs_backend_dav_queue_message (handle->backend, msg,
- push_stat_dest_cb, handle);
+ handle->msg = stat_location_begin (handle->uri, FALSE);
+ g_vfs_backend_dav_send_async (handle->backend, handle->msg, TRUE,
+ push_stat_dest_cb, handle);
}
else
{
@@ -3538,4 +3710,10 @@ g_vfs_backend_dav_class_init (GVfsBackendDavClass *klass)
backend_class->move = do_move;
backend_class->copy = do_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
+ * a lot of files are opened at the same time
+ */
+ http_try_init_session (32, 32);
}