From beaaf78b72640f8231f49fdfc1b6dcfc3d14f149 Mon Sep 17 00:00:00 2001 From: Daniel Kolesa Date: Thu, 11 Feb 2021 22:43:53 +0100 Subject: dav, http: port to libsoup3 The dav and http backends of the daemon now use libsoup3. Things should work more or less the same as before. One caveat is that the soup session no longer defaults to ssl-strict, so it will no longer silently accept certificates. This does not affect the DAV backend (it has its own handling which is replicated in the soup3 port) but it does affect the http backend. Fixes: https://gitlab.gnome.org/GNOME/gvfs/-/issues/208 --- daemon/gvfsbackenddav.c | 1584 +++++++++++++++++++++++------------------- daemon/gvfsbackendhttp.c | 250 +++---- daemon/gvfsbackendhttp.h | 16 +- daemon/gvfshttpinputstream.c | 63 +- daemon/gvfshttpinputstream.h | 3 +- meson.build | 2 +- 6 files changed, 1052 insertions(+), 866 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 on :"; the ":" 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,20 +3393,14 @@ 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) { @@ -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); } diff --git a/daemon/gvfsbackendhttp.c b/daemon/gvfsbackendhttp.c index 1120d2f0..0f01d213 100644 --- a/daemon/gvfsbackendhttp.c +++ b/daemon/gvfsbackendhttp.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 @@ -51,7 +52,7 @@ #include "gvfsdaemonprotocol.h" #include "gvfsdaemonutils.h" -static SoupSession *the_session; +static SoupSession *the_session = NULL; G_DEFINE_TYPE (GVfsBackendHttp, g_vfs_backend_http, G_VFS_TYPE_BACKEND) @@ -63,7 +64,7 @@ g_vfs_backend_http_finalize (GObject *object) backend = G_VFS_BACKEND_HTTP (object); if (backend->mount_base) - soup_uri_free (backend->mount_base); + g_uri_unref (backend->mount_base); g_object_unref (backend->session); @@ -77,18 +78,89 @@ g_vfs_backend_http_init (GVfsBackendHttp *backend) { g_vfs_backend_set_user_visible (G_VFS_BACKEND (backend), FALSE); - backend->session = g_object_ref (the_session); + /* attempt to use libsoup's default values */ + backend->session = g_object_ref (http_try_init_session (-1, -1)); } /* ************************************************************************* */ /* public utility functions */ -SoupURI * +GUri * http_backend_get_mount_base (GVfsBackend *backend) { return G_VFS_BACKEND_HTTP (backend)->mount_base; } +#define DEBUG_MAX_BODY_SIZE (100 * 1024 * 1024) + +/* initializes the session singleton; if max_conns is lower than 0, the + * libsoup defaults are used for max-conns and max-conns-per-host, this + * is called in the instance constructor, so if they are to be overridden, + * all one has to do is make sure to call it with the desired values before + * any instance is created (most likely in the class constructor of the + * derived class, see dav backend) + */ +SoupSession * +http_try_init_session (gint max_conns, gint max_conns_per_host) +{ + const char *debug; + SoupSessionFeature *cookie_jar; + + if (the_session) + return the_session; + + /* Initialize the SoupSession, common to all backend instances */ + if (max_conns < 0) + the_session = soup_session_new_with_options ("user-agent", + "gvfs/" VERSION, NULL); + else + the_session = soup_session_new_with_options ("user-agent", + "gvfs/" VERSION, + "max-conns", + max_conns, + "max-conns-per-host", + max_conns_per_host, + NULL); + + /* Cookie handling - stored temporarlly in memory, mostly useful for + * authentication in WebDAV. */ + cookie_jar = g_object_new (SOUP_TYPE_COOKIE_JAR, NULL); + soup_session_add_feature (the_session, cookie_jar); + g_object_unref (cookie_jar); + + /* Send Accept-Language header (see bug 166795) */ + soup_session_set_accept_language_auto (the_session, TRUE); + + /* Prevent connection timeouts during long operations like COPY. */ + soup_session_set_timeout (the_session, 0); + + /* Logging */ + debug = g_getenv ("GVFS_HTTP_DEBUG"); + if (debug) + { + SoupLogger *logger; + SoupLoggerLogLevel level; + + if (g_ascii_strcasecmp (debug, "all") == 0 || + g_ascii_strcasecmp (debug, "body") == 0) + level = SOUP_LOGGER_LOG_BODY; + else if (g_ascii_strcasecmp (debug, "header") == 0) + level = SOUP_LOGGER_LOG_HEADERS; + else + level = SOUP_LOGGER_LOG_MINIMAL; + + logger = soup_logger_new (level); + g_object_set (G_OBJECT (logger), + "max-body-size", + DEBUG_MAX_BODY_SIZE, + NULL); + soup_session_add_feature (the_session, SOUP_SESSION_FEATURE (logger)); + g_object_unref (logger); + } + + return the_session; +} + char * http_path_get_basename (const char *path) { @@ -138,7 +210,7 @@ http_uri_get_basename (const char *uri_str) basename = http_path_get_basename (uri_str); - decoded = soup_uri_decode (basename); + decoded = g_uri_unescape_string (basename, NULL); g_free (basename); return decoded; @@ -149,13 +221,6 @@ http_error_code_from_status (guint status) { switch (status) { - case SOUP_STATUS_CANT_RESOLVE: - case SOUP_STATUS_CANT_RESOLVE_PROXY: - return G_IO_ERROR_HOST_NOT_FOUND; - - case SOUP_STATUS_CANCELLED: - return G_IO_ERROR_CANCELLED; - case SOUP_STATUS_UNAUTHORIZED: case SOUP_STATUS_PAYMENT_REQUIRED: case SOUP_STATUS_FORBIDDEN: @@ -184,45 +249,27 @@ http_error_code_from_status (guint status) void http_job_failed (GVfsJob *job, SoupMessage *msg) { - switch (msg->status_code) { + switch (soup_message_get_status(msg)) { case SOUP_STATUS_NOT_FOUND: g_vfs_job_failed_literal (job, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, - msg->reason_phrase); + soup_message_get_reason_phrase(msg)); break; case SOUP_STATUS_UNAUTHORIZED: case SOUP_STATUS_PAYMENT_REQUIRED: case SOUP_STATUS_FORBIDDEN: g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED, - _("HTTP Client Error: %s"), msg->reason_phrase); + _("HTTP Client Error: %s"), + soup_message_get_reason_phrase(msg)); break; default: g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED, - _("HTTP Error: %s"), msg->reason_phrase); + _("HTTP Error: %s"), + soup_message_get_reason_phrase(msg)); } } -guint -http_backend_send_message (GVfsBackend *backend, - SoupMessage *msg) -{ - GVfsBackendHttp *op_backend = G_VFS_BACKEND_HTTP (backend); - - return soup_session_send_message (op_backend->session, msg); -} - -void -http_backend_queue_message (GVfsBackend *backend, - SoupMessage *msg, - SoupSessionCallback callback, - gpointer user_data) -{ - GVfsBackendHttp *op_backend = G_VFS_BACKEND_HTTP (backend); - - soup_session_queue_message (op_backend->session, msg, - callback, user_data); -} /* ************************************************************************* */ /* virtual functions overrides */ @@ -235,8 +282,8 @@ try_mount (GVfsBackend *backend, { GVfsBackendHttp *op_backend; const char *uri_str; - char *path; - SoupURI *uri; + const char *path; + GUri *uri; GMountSpec *real_mount_spec; op_backend = G_VFS_BACKEND_HTTP (backend); @@ -245,7 +292,7 @@ try_mount (GVfsBackend *backend, uri_str = g_mount_spec_get (mount_spec, "uri"); if (uri_str) - uri = soup_uri_new (uri_str); + uri = g_uri_parse (uri_str, SOUP_HTTP_URI_FLAGS, NULL); g_debug ("+ try_mount: %s\n", uri_str ? uri_str : "(null)"); @@ -260,12 +307,11 @@ try_mount (GVfsBackend *backend, real_mount_spec = g_mount_spec_new ("http"); g_mount_spec_set (real_mount_spec, "uri", uri_str); - if (uri->path != NULL) + path = g_uri_get_path (uri); + if (path[0]) { - path = g_uri_unescape_string (uri->path, "/"); g_free (real_mount_spec->mount_prefix); real_mount_spec->mount_prefix = g_mount_spec_canonicalize_path (path); - g_free (path); } g_vfs_backend_set_mount_spec (backend, real_mount_spec); @@ -309,7 +355,7 @@ open_for_read_ready (GObject *source_object, } msg = g_vfs_http_input_stream_get_message (stream); - if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) + if (!SOUP_STATUS_IS_SUCCESSFUL (soup_message_get_status (msg))) { http_job_failed (G_VFS_JOB (job), msg); g_object_unref (msg); @@ -330,7 +376,7 @@ try_open_for_read (GVfsBackend *backend, GVfsJobOpenForRead *job, const char *filename) { - SoupURI *uri; + GUri *uri; uri = http_backend_get_mount_base (backend); http_backend_open_for_read (backend, G_VFS_JOB (job), uri); @@ -341,7 +387,7 @@ try_open_for_read (GVfsBackend *backend, void http_backend_open_for_read (GVfsBackend *backend, GVfsJob *job, - SoupURI *uri) + GUri *uri) { GVfsBackendHttp *op_backend; GInputStream *stream; @@ -507,7 +553,7 @@ file_info_from_message (SoupMessage *msg, /* prefer the filename from the Content-Disposition (rfc2183) header if one if present. See bug 551298. */ - if (soup_message_headers_get_content_disposition (msg->response_headers, + if (soup_message_headers_get_content_disposition (soup_message_get_response_headers (msg), NULL, ¶ms)) { const char *name = g_hash_table_lookup (params, "filename"); @@ -520,10 +566,10 @@ file_info_from_message (SoupMessage *msg, if (basename == NULL) { - const SoupURI *uri; + GUri *uri; uri = soup_message_get_uri (msg); - basename = http_uri_get_basename (uri->path); + basename = http_uri_get_basename (g_uri_get_path (uri)); } g_debug ("basename:%s\n", basename); @@ -540,12 +586,12 @@ file_info_from_message (SoupMessage *msg, g_free (basename); g_free (ed_name); - if (soup_message_headers_get_encoding (msg->response_headers) == SOUP_ENCODING_CONTENT_LENGTH) + if (soup_message_headers_get_encoding (soup_message_get_response_headers (msg)) == SOUP_ENCODING_CONTENT_LENGTH) { goffset start, end, length; gboolean ret; - ret = soup_message_headers_get_content_range (msg->response_headers, + ret = soup_message_headers_get_content_range (soup_message_get_response_headers (msg), &start, &end, &length); if (ret && length != -1) { @@ -553,14 +599,14 @@ file_info_from_message (SoupMessage *msg, } else if (!ret) { - length = soup_message_headers_get_content_length (msg->response_headers); + length = soup_message_headers_get_content_length (soup_message_get_response_headers (msg)); g_file_info_set_size (info, length); } } g_file_info_set_file_type (info, G_FILE_TYPE_REGULAR); - text = soup_message_headers_get_content_type (msg->response_headers, NULL); + text = soup_message_headers_get_content_type (soup_message_get_response_headers (msg), NULL); if (text) { GIcon *icon; @@ -578,23 +624,25 @@ file_info_from_message (SoupMessage *msg, } - text = soup_message_headers_get_one (msg->response_headers, + text = soup_message_headers_get_one (soup_message_get_response_headers (msg), "Last-Modified"); if (text) { - SoupDate *sd; + GDateTime *gd; - sd = soup_date_new_from_string(text); - if (sd) + gd = soup_date_time_new_from_http_string (text); + if (gd) { - g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED, soup_date_to_time_t (sd)); + 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); } } - text = soup_message_headers_get_one (msg->response_headers, + text = soup_message_headers_get_one (soup_message_get_response_headers (msg), "ETag"); if (text) { @@ -605,19 +653,25 @@ file_info_from_message (SoupMessage *msg, } static void -query_info_ready (SoupSession *session, - SoupMessage *msg, - gpointer user_data) +query_info_ready (GObject *object, + GAsyncResult *result, + gpointer user_data) { - GFileAttributeMatcher *matcher; - GVfsJobQueryInfo *job; - GFileInfo *info; - - job = G_VFS_JOB_QUERY_INFO (user_data); - info = job->file_info; - matcher = job->attribute_matcher; + GVfsJobQueryInfo *job = G_VFS_JOB_QUERY_INFO (user_data); + GFileAttributeMatcher *matcher = job->attribute_matcher; + GFileInfo *info = job->file_info; + GInputStream *res; + GError *error = NULL; + SoupMessage *msg = G_VFS_JOB (job)->backend_data; + + res = soup_session_send_finish (SOUP_SESSION (object), result, &error); + if (!res) + { + g_vfs_job_failed_from_error (G_VFS_JOB (job), error); + return; + } - if (! SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) + if (!SOUP_STATUS_IS_SUCCESSFUL (soup_message_get_status (msg))) { http_job_failed (G_VFS_JOB (job), msg); return; @@ -625,10 +679,11 @@ query_info_ready (SoupSession *session, file_info_from_message (msg, info, matcher); + g_object_unref (res); + g_vfs_job_succeeded (G_VFS_JOB (job)); } - static gboolean try_query_info (GVfsBackend *backend, GVfsJobQueryInfo *job, @@ -637,8 +692,9 @@ try_query_info (GVfsBackend *backend, GFileInfo *info, GFileAttributeMatcher *attribute_matcher) { + GVfsBackendHttp *op_backend = G_VFS_BACKEND_HTTP (backend); SoupMessage *msg; - SoupURI *uri; + GUri *uri; if (g_file_attribute_matcher_matches_only (attribute_matcher, G_FILE_ATTRIBUTE_THUMBNAIL_PATH)) @@ -650,7 +706,10 @@ try_query_info (GVfsBackend *backend, uri = http_backend_get_mount_base (backend); msg = soup_message_new_from_uri (SOUP_METHOD_HEAD, uri); - http_backend_queue_message (backend, msg, query_info_ready, job); + g_vfs_job_set_backend_data (G_VFS_JOB (job), msg, NULL); + + soup_session_send_async (op_backend->session, msg, G_PRIORITY_DEFAULT, + NULL, query_info_ready, job); return TRUE; } @@ -687,15 +746,9 @@ try_query_fs_info (GVfsBackend *backend, return TRUE; } - -#define DEBUG_MAX_BODY_SIZE (100 * 1024 * 1024) - static void g_vfs_backend_http_class_init (GVfsBackendHttpClass *klass) { - const char *debug; - SoupSessionFeature *cookie_jar; - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GVfsBackendClass *backend_class; @@ -711,43 +764,4 @@ g_vfs_backend_http_class_init (GVfsBackendHttpClass *klass) backend_class->try_query_info = try_query_info; backend_class->try_query_info_on_read = try_query_info_on_read; backend_class->try_query_fs_info = try_query_fs_info; - - /* Initialize the SoupSession, common to all backend instances */ - the_session = soup_session_new_with_options ("user-agent", - "gvfs/" VERSION, - NULL); - - g_object_set (the_session, "ssl-strict", FALSE, NULL); - - /* Cookie handling - stored temporarlly in memory, mostly useful for - * authentication in WebDAV. */ - cookie_jar = g_object_new (SOUP_TYPE_COOKIE_JAR, NULL); - soup_session_add_feature (the_session, cookie_jar); - g_object_unref (cookie_jar); - - /* Send Accept-Language header (see bug 166795) */ - g_object_set (the_session, "accept-language-auto", TRUE, NULL); - - /* Prevent connection timeouts during long operations like COPY. */ - g_object_set (the_session, "timeout", 0, NULL); - - /* Logging */ - debug = g_getenv ("GVFS_HTTP_DEBUG"); - if (debug) - { - SoupLogger *logger; - SoupLoggerLogLevel level; - - if (g_ascii_strcasecmp (debug, "all") == 0 || - g_ascii_strcasecmp (debug, "body") == 0) - level = SOUP_LOGGER_LOG_BODY; - else if (g_ascii_strcasecmp (debug, "header") == 0) - level = SOUP_LOGGER_LOG_HEADERS; - else - level = SOUP_LOGGER_LOG_MINIMAL; - - logger = soup_logger_new (level, DEBUG_MAX_BODY_SIZE); - soup_session_add_feature (the_session, SOUP_SESSION_FEATURE (logger)); - g_object_unref (logger); - } } diff --git a/daemon/gvfsbackendhttp.h b/daemon/gvfsbackendhttp.h index f132c568..f05e92a3 100644 --- a/daemon/gvfsbackendhttp.h +++ b/daemon/gvfsbackendhttp.h @@ -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 @@ -49,7 +50,7 @@ struct _GVfsBackendHttp { GVfsBackend parent_instance; - SoupURI *mount_base; + GUri *mount_base; SoupSession *session; }; @@ -61,19 +62,14 @@ char * http_path_get_basename (const char *path_str); int http_error_code_from_status (guint status); -SoupURI * http_backend_get_mount_base (GVfsBackend *backend); +SoupSession * http_try_init_session (gint max_conns, + gint max_conns_per_host); -guint http_backend_send_message (GVfsBackend *backend, - SoupMessage *msg); - -void http_backend_queue_message (GVfsBackend *backend, - SoupMessage *msg, - SoupSessionCallback callback, - gpointer user_data); +GUri * http_backend_get_mount_base (GVfsBackend *backend); void http_backend_open_for_read (GVfsBackend *backend, GVfsJob *job, - SoupURI *uri); + GUri *uri); void http_job_failed (GVfsJob *job, SoupMessage *msg); diff --git a/daemon/gvfshttpinputstream.c b/daemon/gvfshttpinputstream.c index 82f9164d..5a3f6d7c 100644 --- a/daemon/gvfshttpinputstream.c +++ b/daemon/gvfshttpinputstream.c @@ -1,6 +1,7 @@ /* gvfshttpinputstream.c: seekable wrapper around SoupRequestHTTP * * Copyright (C) 2006, 2007, 2012 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 @@ -29,13 +30,13 @@ #include #include "gvfshttpinputstream.h" +#include "gvfsbackendhttp.h" static void g_vfs_http_input_stream_seekable_iface_init (GSeekableIface *seekable_iface); struct GVfsHttpInputStreamPrivate { - SoupURI *uri; + GUri *uri; SoupSession *session; - SoupRequest *req; SoupMessage *msg; GInputStream *stream; @@ -61,9 +62,8 @@ g_vfs_http_input_stream_finalize (GObject *object) GVfsHttpInputStream *stream = G_VFS_HTTP_INPUT_STREAM (object); GVfsHttpInputStreamPrivate *priv = stream->priv; - g_clear_pointer (&priv->uri, soup_uri_free); + g_clear_pointer (&priv->uri, g_uri_unref); g_clear_object (&priv->session); - g_clear_object (&priv->req); g_clear_object (&priv->msg); g_clear_object (&priv->stream); g_free (priv->range); @@ -74,7 +74,7 @@ g_vfs_http_input_stream_finalize (GObject *object) /** * g_vfs_http_input_stream_new: * @session: a #SoupSession - * @uri: a #SoupURI + * @uri: a #GUri * * Prepares to send a GET request for @uri on @session, and returns a * #GInputStream that can be used to read the response. @@ -89,7 +89,7 @@ g_vfs_http_input_stream_finalize (GObject *object) **/ GInputStream * g_vfs_http_input_stream_new (SoupSession *session, - SoupURI *uri) + GUri *uri) { GVfsHttpInputStream *stream; GVfsHttpInputStreamPrivate *priv; @@ -98,30 +98,27 @@ g_vfs_http_input_stream_new (SoupSession *session, priv = stream->priv; priv->session = g_object_ref (session); - priv->uri = soup_uri_copy (uri); + priv->uri = g_uri_ref (uri); return G_INPUT_STREAM (stream); } -static SoupRequest * -g_vfs_http_input_stream_ensure_request (GInputStream *stream) +static SoupMessage * +g_vfs_http_input_stream_ensure_msg (GInputStream *stream) { GVfsHttpInputStreamPrivate *priv = G_VFS_HTTP_INPUT_STREAM (stream)->priv; - if (!priv->req) + if (!priv->msg) { - GError *error = NULL; - - priv->req = soup_session_request_uri (priv->session, priv->uri, &error); - g_assert_no_error (error); - priv->msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (priv->req)); + priv->msg = soup_message_new_from_uri (SOUP_METHOD_GET, priv->uri); priv->offset = 0; } if (priv->range) - soup_message_headers_replace (priv->msg->request_headers, "Range", priv->range); + soup_message_headers_replace (soup_message_get_request_headers (priv->msg), + "Range", priv->range); - return priv->req; + return priv->msg; } static void @@ -136,7 +133,7 @@ send_callback (GObject *object, g_input_stream_clear_pending (http_stream); - priv->stream = soup_request_send_finish (SOUP_REQUEST (object), result, &error); + priv->stream = soup_session_send_finish (SOUP_SESSION (object), result, &error); if (priv->stream) g_task_return_boolean (task, TRUE); else @@ -188,9 +185,9 @@ g_vfs_http_input_stream_send_async (GInputStream *stream, return; } - g_vfs_http_input_stream_ensure_request (stream); - soup_request_send_async (priv->req, cancellable, - send_callback, task); + g_vfs_http_input_stream_ensure_msg (stream); + soup_session_send_async (priv->session, priv->msg, G_PRIORITY_DEFAULT, + cancellable, send_callback, task); } /** @@ -253,16 +250,16 @@ read_send_callback (GObject *object, ReadAfterSendData *rasd = g_task_get_task_data (task); GError *error = NULL; - priv->stream = soup_request_send_finish (SOUP_REQUEST (object), result, &error); + priv->stream = soup_session_send_finish (SOUP_SESSION (object), result, &error); if (!priv->stream) { g_task_return_error (task, error); g_object_unref (task); return; } - if (!SOUP_STATUS_IS_SUCCESSFUL (priv->msg->status_code)) + if (!SOUP_STATUS_IS_SUCCESSFUL (soup_message_get_status (priv->msg))) { - if (priv->msg->status_code == SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE) + if (soup_message_get_status (priv->msg) == SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE) { g_input_stream_close (priv->stream, NULL, NULL); g_task_return_int (task, 0); @@ -271,9 +268,9 @@ read_send_callback (GObject *object, return; } g_task_return_new_error (task, - SOUP_HTTP_ERROR, - priv->msg->status_code, - "%s", priv->msg->reason_phrase); + G_IO_ERROR, + http_error_code_from_status (soup_message_get_status (priv->msg)), + _("HTTP Error: %s"), soup_message_get_reason_phrase (priv->msg)); g_object_unref (task); return; } @@ -282,7 +279,7 @@ read_send_callback (GObject *object, gboolean status; goffset start, end; - status = soup_message_headers_get_content_range (priv->msg->response_headers, + status = soup_message_headers_get_content_range (soup_message_get_response_headers (priv->msg), &start, &end, NULL); if (!status || start != priv->request_offset) { @@ -325,9 +322,9 @@ g_vfs_http_input_stream_read_async (GInputStream *stream, rasd->count = count; g_task_set_task_data (task, rasd, g_free); - g_vfs_http_input_stream_ensure_request (stream); - soup_request_send_async (priv->req, cancellable, - read_send_callback, task); + g_vfs_http_input_stream_ensure_msg (stream); + soup_session_send_async (priv->session, priv->msg, G_PRIORITY_DEFAULT, + cancellable, read_send_callback, task); return; } @@ -419,7 +416,7 @@ g_vfs_http_input_stream_seek (GSeekable *seekable, if (type == G_SEEK_END && priv->msg) { - goffset content_length = soup_message_headers_get_content_length (priv->msg->response_headers); + goffset content_length = soup_message_headers_get_content_length (soup_message_get_response_headers (priv->msg)); if (content_length) { @@ -498,7 +495,7 @@ g_vfs_http_input_stream_get_message (GInputStream *stream) { GVfsHttpInputStreamPrivate *priv = G_VFS_HTTP_INPUT_STREAM (stream)->priv; - g_vfs_http_input_stream_ensure_request (stream); + g_vfs_http_input_stream_ensure_msg (stream); return g_object_ref (priv->msg); } diff --git a/daemon/gvfshttpinputstream.h b/daemon/gvfshttpinputstream.h index 93f919ee..e5f9c45b 100644 --- a/daemon/gvfshttpinputstream.h +++ b/daemon/gvfshttpinputstream.h @@ -1,4 +1,5 @@ /* Copyright (C) 2006, 2007, 2012 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 @@ -57,7 +58,7 @@ struct GVfsHttpInputStreamClass GType g_vfs_http_input_stream_get_type (void) G_GNUC_CONST; GInputStream *g_vfs_http_input_stream_new (SoupSession *session, - SoupURI *uri); + GUri *uri); void g_vfs_http_input_stream_send_async (GInputStream *stream, int io_priority, diff --git a/meson.build b/meson.build index 557eef83..9b771ffa 100644 --- a/meson.build +++ b/meson.build @@ -310,7 +310,7 @@ endif enable_http = get_option('http') if enable_http assert(have_libxml, 'http required but libxml-2.0 not found') - libsoup_dep = dependency('libsoup-2.4', version: '>= 2.58.0') + libsoup_dep = dependency('libsoup-3.0', version: '>= 3.0.0') endif # *** Check if we should build with DNS-SD backend *** -- cgit v1.2.1