diff options
-rw-r--r-- | daemon/gvfsbackenddav.c | 1584 | ||||
-rw-r--r-- | daemon/gvfsbackendhttp.c | 250 | ||||
-rw-r--r-- | daemon/gvfsbackendhttp.h | 16 | ||||
-rw-r--r-- | daemon/gvfshttpinputstream.c | 63 | ||||
-rw-r--r-- | daemon/gvfshttpinputstream.h | 3 | ||||
-rw-r--r-- | 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 <username> on <hostname>:<port>"; the ":<port>" part is the second %s and only shown if it is not the default http(s) port. */ display_name = g_strdup_printf (_("%s on %s%s"), - mount_base->user, - mount_base->host, - port); + g_uri_get_user (mount_base), + g_uri_get_host (mount_base), + port); else display_name = g_strdup_printf ("%s%s", - mount_base->host, - port); + g_uri_get_host (mount_base), + port); g_vfs_backend_set_display_name (backend, display_name); g_free (display_name); } -static void -certificate_error_handler (SoupMessage *msg, - GParamSpec *pspec, - gpointer user_data) +static gboolean +accept_certificate (SoupMessage *msg, + GTlsCertificate *certificate, + GTlsCertificateFlags errors, + gpointer user_data) { GVfsBackendDav *dav = G_VFS_BACKEND_DAV (user_data); - GTlsCertificate *certificate; - GTlsCertificateFlags errors; - /* Fail the message if the certificate errors change or the certificate is - * different. */ - if (soup_message_get_https_status (msg, &certificate, &errors)) - { - if (errors != dav->certificate_errors || - !g_tls_certificate_is_same (certificate, dav->certificate)) - { - soup_session_cancel_message (G_VFS_BACKEND_HTTP (dav)->session, - msg, - SOUP_STATUS_SSL_FAILED); - } - } + return (errors == dav->certificate_errors && + g_tls_certificate_is_same (certificate, dav->certificate)); } -static guint -g_vfs_backend_dav_send_message (GVfsBackend *backend, SoupMessage *message) +static void +dav_message_connect_signals (SoupMessage *message, GVfsBackend *backend) { - GVfsBackendHttp *http_backend; - SoupSession *session; + GVfsBackendDav *dav_backend = G_VFS_BACKEND_DAV (backend); + + /* we always have to connect this as the message can be + * re-sent with a differently set certificate_errors field + */ + g_signal_connect (message, "accept-certificate", + G_CALLBACK (accept_certificate), backend); - http_backend = G_VFS_BACKEND_HTTP (backend); - session = http_backend->session; + g_signal_connect (message, "authenticate", + G_CALLBACK (soup_authenticate), + &dav_backend->auth_info); +} + +static GInputStream * +g_vfs_backend_dav_send (GVfsBackend *backend, + SoupMessage *message, + gboolean cb_connect, + GError **error) +{ + SoupSession *session = G_VFS_BACKEND_HTTP (backend)->session; /* We have our own custom redirect handler */ soup_message_set_flags (message, SOUP_MESSAGE_NO_REDIRECT); - if (G_VFS_BACKEND_DAV (backend)->certificate_errors) - g_signal_connect (message, "notify::tls-errors", - G_CALLBACK (certificate_error_handler), backend); + if (cb_connect) + dav_message_connect_signals (message, backend); - soup_message_add_header_handler (message, "got_body", "Location", - G_CALLBACK (redirect_handler), session); - - return http_backend_send_message (backend, message); + return g_vfs_backend_dav_redirect (session, message, error); } static void -g_vfs_backend_dav_queue_message (GVfsBackend *backend, - SoupMessage *msg, - SoupSessionCallback callback, - gpointer user_data) +g_vfs_backend_dav_send_async (GVfsBackend *backend, + SoupMessage *message, + gboolean cb_connect, + GAsyncReadyCallback callback, + gpointer user_data) { - if (G_VFS_BACKEND_DAV (backend)->certificate_errors) - g_signal_connect (msg, "notify::tls-errors", - G_CALLBACK (certificate_error_handler), backend); + SoupSession *session = G_VFS_BACKEND_HTTP (backend)->session; + + if (cb_connect) + dav_message_connect_signals (message, backend); - soup_session_queue_message (G_VFS_BACKEND_HTTP (backend)->session, msg, - callback, user_data); + soup_session_send_async (session, message, G_PRIORITY_DEFAULT, NULL, + callback, user_data); } /* ************************************************************************* */ @@ -692,33 +730,38 @@ xml_node_iter_get_current (xmlNodeIter *iter) return iter->cur_node; } +static int +xml_read_cb (void *ctx, char *buf, int len) +{ + return g_input_stream_read (ctx, buf, len, NULL, NULL); +} + static xmlDocPtr parse_xml (SoupMessage *msg, + GInputStream *body, xmlNodePtr *root, const char *name, GError **error) { - xmlDocPtr doc; + xmlDocPtr doc; - if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) + if (!SOUP_STATUS_IS_SUCCESSFUL (soup_message_get_status (msg))) { g_set_error (error, G_IO_ERROR, - http_error_code_from_status (msg->status_code), - _("HTTP Error: %s"), msg->reason_phrase); + http_error_code_from_status (soup_message_get_status (msg)), + _("HTTP Error: %s"), soup_message_get_reason_phrase (msg)); return NULL; } - doc = xmlReadMemory (msg->response_body->data, - msg->response_body->length, - "response.xml", - NULL, - XML_PARSE_NONET | - XML_PARSE_NOWARNING | - XML_PARSE_NOBLANKS | - XML_PARSE_NSCLEAN | - XML_PARSE_NOCDATA | - XML_PARSE_COMPACT); + doc = xmlReadIO (xml_read_cb, NULL, body, "response.xml", NULL, + XML_PARSE_NONET | + XML_PARSE_NOWARNING | + XML_PARSE_NOBLANKS | + XML_PARSE_NSCLEAN | + XML_PARSE_NOCDATA | + XML_PARSE_COMPACT); + if (doc == NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, @@ -759,7 +802,7 @@ struct _Multistatus { xmlDocPtr doc; xmlNodePtr root; - SoupURI *target; + GUri *target; char *path; }; @@ -784,13 +827,16 @@ struct _MsPropstat { static gboolean -multistatus_parse (SoupMessage *msg, Multistatus *multistatus, GError **error) +multistatus_parse (SoupMessage *msg, + GInputStream *body, + Multistatus *multistatus, + GError **error) { xmlDocPtr doc; xmlNodePtr root; - SoupURI *uri; + GUri *uri; - doc = parse_xml (msg, &root, "multistatus", error); + doc = parse_xml (msg, body, &root, "multistatus", error); if (doc == NULL) return FALSE; @@ -800,7 +846,7 @@ multistatus_parse (SoupMessage *msg, Multistatus *multistatus, GError **error) multistatus->doc = doc; multistatus->root = root; multistatus->target = uri; - multistatus->path = g_uri_unescape_string (uri->path, "/"); + multistatus->path = g_uri_unescape_string (g_uri_get_path (uri), "/"); return TRUE; } @@ -830,7 +876,7 @@ multistatus_get_response (xmlNodeIter *resp_iter, MsResponse *response) xmlNodePtr iter; xmlNodePtr href; xmlNodePtr propstat; - SoupURI *uri; + GUri *uri; const char *text; char *path; @@ -871,13 +917,12 @@ multistatus_get_response (xmlNodeIter *resp_iter, MsResponse *response) if (text == NULL) return FALSE; - uri = soup_uri_new_with_base (multistatus->target, text); - + uri = g_uri_parse_relative (multistatus->target, text, SOUP_HTTP_URI_FLAGS, NULL); if (uri == NULL) return FALSE; - path = g_uri_unescape_string (uri->path, "/"); - soup_uri_free (uri); + path = g_uri_unescape_string (g_uri_get_path (uri), "/"); + g_uri_unref (uri); response->path = path; response->is_target = path_equal (path, multistatus->path, TRUE); @@ -1102,16 +1147,16 @@ ms_response_to_file_info (MsResponse *response, } else if (node_has_name (node, "getlastmodified")) { - SoupDate *sd; + GDateTime *gd; - sd = soup_date_new_from_string(text); - if (sd) - { - g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED, soup_date_to_time_t (sd)); + gd = soup_date_time_new_from_http_string (text); + if (gd) + { + g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED, g_date_time_to_unix (gd)); g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC, 0); - soup_date_free (sd); - } - } + g_date_time_unref (gd); + } + } } } @@ -1250,6 +1295,19 @@ typedef struct _PropName { } PropName; +static void +propfind_stat_msg_starting_restarted (SoupMessage *msg, gpointer user_data) +{ + soup_message_set_request_body_from_bytes (msg, "application/xml", user_data); +} + +static void +propfind_stat_bytes_unref (gpointer data, GClosure *closure) +{ + (void)closure; + g_bytes_unref (data); +} + static SoupMessage * propfind_request_new (GVfsBackend *backend, @@ -1258,13 +1316,14 @@ propfind_request_new (GVfsBackend *backend, const PropName *properties) { SoupMessage *msg; - SoupURI *uri; + GUri *uri; const char *header_depth; GString *body; + GBytes *bytes; uri = g_vfs_backend_dav_uri_for_path (backend, filename, depth > 0); msg = soup_message_new_from_uri (SOUP_METHOD_PROPFIND, uri); - soup_uri_free (uri); + g_uri_unref (uri); if (msg == NULL) return NULL; @@ -1276,7 +1335,8 @@ propfind_request_new (GVfsBackend *backend, else header_depth = "infinity"; - soup_message_headers_append (msg->request_headers, "Depth", header_depth); + soup_message_headers_append (soup_message_get_request_headers (msg), + "Depth", header_depth); body = g_string_new (PROPSTAT_XML_BEGIN); @@ -1302,22 +1362,29 @@ propfind_request_new (GVfsBackend *backend, g_string_append (body, PROPSTAT_XML_END); - soup_message_set_request (msg, "application/xml", - SOUP_MEMORY_TAKE, - body->str, - body->len); + bytes = g_string_free_to_bytes (body); + + g_signal_connect_data (msg, "starting", + G_CALLBACK (propfind_stat_msg_starting_restarted), + g_bytes_ref (bytes), propfind_stat_bytes_unref, 0); - g_string_free (body, FALSE); + /* only called with implicit redirects enabled */ + g_signal_connect_data (msg, "restarted", + G_CALLBACK (propfind_stat_msg_starting_restarted), + g_bytes_ref (bytes), propfind_stat_bytes_unref, 0); + + g_bytes_unref (bytes); return msg; } static SoupMessage * -stat_location_begin (SoupURI *uri, - gboolean count_children) +stat_location_begin (GUri *uri, + gboolean count_children) { SoupMessage *msg; const char *depth; + GBytes *bytes; static const char *stat_profind_body = PROPSTAT_XML_BEGIN PROPSTAT_XML_PROP_BEGIN @@ -1333,33 +1400,42 @@ stat_location_begin (SoupURI *uri, else depth = "0"; - soup_message_headers_append (msg->request_headers, "Depth", depth); + soup_message_headers_append (soup_message_get_request_headers (msg), + "Depth", depth); + + bytes = g_bytes_new (stat_profind_body, strlen (stat_profind_body)); + + g_signal_connect_data (msg, "starting", + G_CALLBACK (propfind_stat_msg_starting_restarted), + g_bytes_ref (bytes), propfind_stat_bytes_unref, 0); + + /* only called with implicit redirects enabled */ + g_signal_connect_data (msg, "restarted", + G_CALLBACK (propfind_stat_msg_starting_restarted), + g_bytes_ref (bytes), propfind_stat_bytes_unref, 0); + + g_bytes_unref (bytes); - soup_message_set_request (msg, "application/xml", - SOUP_MEMORY_STATIC, - stat_profind_body, - strlen (stat_profind_body)); return msg; } static gboolean -stat_location_finish (SoupMessage *msg, - GFileType *target_type, - gint64 *target_size, - guint *num_children) +stat_location_finish (SoupMessage *msg, + GInputStream *body, + GFileType *target_type, + gint64 *target_size, + guint *num_children) { Multistatus ms; xmlNodeIter iter; gboolean res; - GError *error; guint child_count; GFileInfo *file_info; - if (msg->status_code != 207) + if (!body || soup_message_get_status (msg) != SOUP_STATUS_MULTI_STATUS) return FALSE; - res = multistatus_parse (msg, &ms, &error); - + res = multistatus_parse (msg, body, &ms, NULL); if (res == FALSE) return FALSE; @@ -1405,43 +1481,52 @@ stat_location_finish (SoupMessage *msg, static gboolean stat_location (GVfsBackend *backend, - SoupURI *uri, + GUri *uri, GFileType *target_type, gint64 *target_size, guint *num_children, GError **error) { SoupMessage *msg; - guint status; - gboolean count_children; - gboolean res; + GInputStream *body; + guint status; + gboolean count_children; + gboolean res; count_children = num_children != NULL; msg = stat_location_begin (uri, count_children); - if (msg == NULL) return FALSE; - status = g_vfs_backend_dav_send_message (backend, msg); + body = g_vfs_backend_dav_send (backend, msg, TRUE, error); - if (status != 207) + if (!body) + { + g_object_unref (msg); + return FALSE; + } + + status = soup_message_get_status (msg); + if (status != SOUP_STATUS_MULTI_STATUS) { g_set_error_literal (error, - G_IO_ERROR, - http_error_code_from_status (status), - msg->reason_phrase); + G_IO_ERROR, + http_error_code_from_status (status), + soup_message_get_reason_phrase (msg)); g_object_unref (msg); + g_object_unref (body); return FALSE; } - res = stat_location_finish (msg, target_type, target_size, num_children); + res = stat_location_finish (msg, body, target_type, target_size, num_children); g_object_unref (msg); + g_object_unref (body); if (res == FALSE) - g_set_error_literal (error, - G_IO_ERROR, G_IO_ERROR_FAILED, - _("Response invalid")); + g_set_error_literal (error, + G_IO_ERROR, G_IO_ERROR_FAILED, + _("Response invalid")); return res; } @@ -1464,38 +1549,11 @@ mount_auth_info_free (MountAuthData *data) } -static void -soup_authenticate_from_data (SoupSession *session, - SoupMessage *msg, - SoupAuth *auth, - gboolean retrying, - gpointer user_data) -{ - MountAuthData *data; - AuthInfo *info; - - g_debug ("+ soup_authenticate_from_data (%s) \n", - retrying ? "retrying" : "first auth"); - - if (retrying) - return; - - data = (MountAuthData *) user_data; - - if (soup_auth_is_for_proxy (auth)) - info = &data->proxy_auth; - else - info = &data->server_auth; - - soup_auth_authenticate (auth, info->username, info->password); -} - -static void -soup_authenticate_interactive (SoupSession *session, - SoupMessage *msg, - SoupAuth *auth, - gboolean retrying, - gpointer user_data) +static gboolean +soup_authenticate (SoupMessage *msg, + SoupAuth *auth, + gboolean retrying, + gpointer user_data) { MountAuthData *data; AuthInfo *info; @@ -1512,9 +1570,28 @@ soup_authenticate_interactive (SoupSession *session, data = (MountAuthData *) user_data; - retrying = (retrying || data->retrying_after_403); + is_proxy = soup_auth_is_for_proxy (auth); + + if (is_proxy) + info = &(data->proxy_auth); + else + info = &(data->server_auth); + + if (!data->interactive) + { + g_debug ("+ soup_authenticate (%s) \n", + retrying ? "retrying" : "first auth"); + + if (!retrying) + soup_auth_authenticate (auth, info->username, info->password); + + return FALSE; + } - g_debug ("+ soup_authenticate_interactive (%s) \n", + if (data->retrying_after_403) + retrying = TRUE; + + g_debug ("+ soup_authenticate (interactive, %s) \n", retrying ? "retrying" : "first auth"); new_username = NULL; @@ -1522,43 +1599,53 @@ soup_authenticate_interactive (SoupSession *session, realm = NULL; pw_ask_flags = G_ASK_PASSWORD_NEED_PASSWORD; - is_proxy = soup_auth_is_for_proxy (auth); realm = soup_auth_get_realm (auth); - if (is_proxy) - info = &(data->proxy_auth); - else - info = &(data->server_auth); - if (realm && info->realm == NULL) info->realm = g_strdup (realm); else if (realm && info->realm && !g_str_equal (realm, info->realm)) - return; + return FALSE; have_auth = info->username && info->password; if (have_auth == FALSE && g_vfs_keyring_is_available ()) { - SoupURI *uri; - SoupURI *uri_free = NULL; + const char *host; + gchar *hostp = NULL; + gint port; pw_ask_flags |= G_ASK_PASSWORD_SAVING_SUPPORTED; if (is_proxy) { - g_object_get (session, SOUP_SESSION_PROXY_URI, &uri_free, NULL); - uri = uri_free; + gboolean ret; + gchar *urip; + + urip = g_strdup_printf ("http://%s", soup_auth_get_authority (auth)); + ret = g_uri_split_network (urip, G_URI_FLAGS_NONE, + NULL, &hostp, &port, NULL); + g_free (urip); + + if (!ret) + return FALSE; + + host = hostp; } else - uri = soup_message_get_uri (msg); + { + GUri *uri = soup_message_get_uri (msg); + + host = g_uri_get_host (uri); + port = g_uri_get_port (uri); + } res = g_vfs_keyring_lookup_password (info->username, - uri->host, + host, NULL, "http", realm, is_proxy ? "proxy" : "basic", - uri->port, + port, &new_username, NULL, &new_password); @@ -1572,14 +1659,14 @@ soup_authenticate_interactive (SoupSession *session, info->password = new_password; } - if (uri_free) - soup_uri_free (uri_free); + if (hostp) + g_free (hostp); } if (retrying == FALSE && have_auth) { soup_auth_authenticate (auth, info->username, info->password); - return; + return FALSE; } if (is_proxy == FALSE) @@ -1624,41 +1711,46 @@ soup_authenticate_interactive (SoupSession *session, info->pw_save = pw_save; } else - soup_session_cancel_message (session, msg, SOUP_STATUS_CANCELLED); + soup_auth_cancel (auth); g_debug ("- soup_authenticate \n"); g_free (prompt); + + return !res || aborted; } static void keyring_save_authinfo (AuthInfo *info, - SoupURI *uri, + GUri *uri, gboolean is_proxy) { const char *type = is_proxy ? "proxy" : "basic"; g_vfs_keyring_save_password (info->username, - uri->host, + g_uri_get_host (uri), NULL, "http", info->realm, type, - uri->port, + g_uri_get_port (uri), info->password, info->pw_save); } /* ************************************************************************* */ -static SoupURI * +static GUri * g_mount_spec_to_dav_uri (GMountSpec *spec) { - SoupURI *uri; + GUri *uri; const char *host; const char *user; const char *port; const char *ssl; const char *path; + const char *scheme; + char *host_str; + char *path_str; gint port_num; host = g_mount_spec_get (spec, "host"); @@ -1670,37 +1762,51 @@ g_mount_spec_to_dav_uri (GMountSpec *spec) if (host == NULL || *host == 0) return NULL; - uri = soup_uri_new (NULL); - if (ssl != NULL && (strcmp (ssl, "true") == 0)) - soup_uri_set_scheme (uri, SOUP_URI_SCHEME_HTTPS); + scheme = "https"; else - soup_uri_set_scheme (uri, SOUP_URI_SCHEME_HTTP); + scheme = "http"; - soup_uri_set_user (uri, user); + port_num = -1; + + /* always a valid number if set */ + if (port != NULL) + port_num = atoi (port); /* IPv6 host does not include brackets in SoupURI, but GMountSpec host does */ if (gvfs_is_ipv6 (host)) - uri->host = g_strndup (host + 1, strlen (host) - 2); + host_str = g_strndup (host + 1, strlen (host) - 2); else - soup_uri_set_host (uri, host); + host_str = g_strdup (host); - if (port && (port_num = atoi (port))) - soup_uri_set_port (uri, port_num); + path_str = dav_uri_encode (path); - g_free (uri->path); - uri->path = dav_uri_encode (path); + uri = g_uri_build_with_user (SOUP_HTTP_URI_FLAGS, + scheme, + user, + NULL, + NULL, + host_str, + port_num, + path_str, + NULL, + NULL); + + g_free (host_str); + g_free (path_str); return uri; } static GMountSpec * g_mount_spec_from_dav_uri (GVfsBackendDav *dav_backend, - SoupURI *uri) + GUri *uri) { GMountSpec *spec; - const char *ssl; - char *local_path; + char *local_path; + gboolean ssl; + const gchar *user; + gint port_num; #ifdef HAVE_AVAHI if (dav_backend->resolver != NULL) @@ -1724,36 +1830,35 @@ g_mount_spec_from_dav_uri (GVfsBackendDav *dav_backend, spec = g_mount_spec_new ("dav"); - /* IPv6 host does not include brackets in SoupURI, but GMountSpec host does */ - if (strchr (uri->host, ':')) + /* IPv6 host does not include brackets in GUri, but GMountSpec host does */ + if (strchr (g_uri_get_host (uri), ':')) { - char *host = g_strdup_printf ("[%s]", uri->host); + char *host = g_strdup_printf ("[%s]", g_uri_get_host (uri)); g_mount_spec_set (spec, "host", host); g_free (host); } else - g_mount_spec_set (spec, "host", uri->host); + g_mount_spec_set (spec, "host", g_uri_get_host (uri)); - if (uri->scheme == SOUP_URI_SCHEME_HTTPS) - ssl = "true"; - else - ssl = "false"; + ssl = !strcmp (g_uri_get_scheme (uri), "https"); - g_mount_spec_set (spec, "ssl", ssl); + g_mount_spec_set (spec, "ssl", ssl ? "true" : "false"); - if (uri->user) - g_mount_spec_set (spec, "user", uri->user); + user = g_uri_get_user (uri); + if (user != NULL) + g_mount_spec_set (spec, "user", user); - if (! soup_uri_uses_default_port (uri)) + port_num = g_uri_get_port (uri); + if (port_num > 0 && port_num != (ssl ? 443 : 80)) { - char *port = g_strdup_printf ("%u", uri->port); + char *port = g_strdup_printf ("%d", port_num); g_mount_spec_set (spec, "port", port); g_free (port); } /* There must not be any illegal characters in the URL at this point */ - local_path = g_uri_unescape_string (uri->path, "/"); + local_path = g_uri_unescape_string (g_uri_get_path (uri), "/"); g_mount_spec_set_mount_prefix (spec, local_path); g_free (local_path); @@ -1761,15 +1866,17 @@ g_mount_spec_from_dav_uri (GVfsBackendDav *dav_backend, } #ifdef HAVE_AVAHI -static SoupURI * +static GUri * dav_uri_from_dns_sd_resolver (GVfsBackendDav *dav_backend) { - SoupURI *uri; + GUri *uri; char *user; char *path; char *address; + char *host; gchar *interface; const char *service_type; + const char *scheme; guint port; service_type = g_vfs_dns_sd_resolver_get_service_type (dav_backend->resolver); @@ -1783,42 +1890,42 @@ dav_uri_from_dns_sd_resolver (GVfsBackendDav *dav_backend) * there's also a TXT record "p" for password. Handle this. */ - uri = soup_uri_new (NULL); - if (strcmp (service_type, "_webdavs._tcp") == 0) - soup_uri_set_scheme (uri, SOUP_URI_SCHEME_HTTPS); + scheme = "https"; else - soup_uri_set_scheme (uri, SOUP_URI_SCHEME_HTTP); - - soup_uri_set_user (uri, user); - - soup_uri_set_port (uri, port); + scheme = "http"; - /* IPv6 host does not include brackets in SoupURI, but GVfsDnsSdResolver host does */ + /* IPv6 host does not include brackets in GUri, but GVfsDnsSdResolver host does */ if (gvfs_is_ipv6 (address)) { /* Link-local addresses require interface to be specified. */ if (g_str_has_prefix (address, "[fe80:") && interface != NULL) { - uri->host = g_strconcat (address + 1, interface, NULL); - uri->host[strlen (address) - 2] = '%'; + host = g_strconcat (address + 1, interface, NULL); + host[strlen (address) - 2] = '%'; } else - uri->host = g_strndup (address + 1, strlen (address) - 2); + host = g_strndup (address + 1, strlen (address) - 2); } else - soup_uri_set_host (uri, address); - - if (path != NULL) - soup_uri_set_path (uri, path); - else - soup_uri_set_path (uri, "/"); + host = g_strdup (address); + uri = g_uri_build_with_user (SOUP_HTTP_URI_FLAGS, + scheme, + user, + NULL, + NULL, + host, + port, + path, + NULL, + NULL); g_free (address); g_free (interface); g_free (user); g_free (path); + g_free (host); return uri; } @@ -1849,13 +1956,15 @@ do_mount (GVfsBackend *backend, SoupSession *session; SoupMessage *msg_opts; SoupMessage *msg_stat; - SoupURI *mount_base; - gulong signal_id; + GUri *mount_base; + GUri *tmp; + GError *error = NULL; guint status; gboolean is_success; gboolean is_webdav; gboolean is_collection; - gboolean auth_interactive; + gboolean sig_opts = TRUE; + gboolean sig_stat = TRUE; gboolean res; char *last_good_path; const char *host; @@ -1870,11 +1979,8 @@ do_mount (GVfsBackend *backend, /* resolve DNS-SD style URIs */ if ((strcmp (type, "dav+sd") == 0 || strcmp (type, "davs+sd") == 0) && host != NULL) { - GError *error; - dav_backend->resolver = g_vfs_dns_sd_resolver_new_for_encoded_triple (host, "u"); - error = NULL; if (!g_vfs_dns_sd_resolver_resolve_sync (dav_backend->resolver, NULL, &error)) @@ -1905,28 +2011,17 @@ do_mount (GVfsBackend *backend, } session = G_VFS_BACKEND_HTTP (backend)->session; - G_VFS_BACKEND_HTTP (backend)->mount_base = mount_base; + G_VFS_BACKEND_HTTP (backend)->mount_base = mount_base; soup_session_add_feature_by_type (session, SOUP_TYPE_AUTH_NEGOTIATE); soup_session_add_feature_by_type (session, SOUP_TYPE_AUTH_NTLM); - /* Override the HTTP backend's default. */ - g_object_set (session, - "ssl-strict", TRUE, - SOUP_SESSION_MAX_CONNS_PER_HOST, MAX_CONNS, - SOUP_SESSION_MAX_CONNS, MAX_CONNS, - NULL); - data = &(G_VFS_BACKEND_DAV (backend)->auth_info); data->mount_source = g_object_ref (mount_source); - data->server_auth.username = g_strdup (mount_base->user); + data->server_auth.username = g_strdup (g_uri_get_user (mount_base)); data->server_auth.pw_save = G_PASSWORD_SAVE_NEVER; data->proxy_auth.pw_save = G_PASSWORD_SAVE_NEVER; - - signal_id = g_signal_connect (session, "authenticate", - G_CALLBACK (soup_authenticate_interactive), - data); - auth_interactive = TRUE; + data->interactive = TRUE; last_good_path = NULL; msg_opts = soup_message_new_from_uri (SOUP_METHOD_OPTIONS, mount_base); @@ -1936,12 +2031,16 @@ do_mount (GVfsBackend *backend, msg_stat = stat_location_begin (mount_base, TRUE); do { + GInputStream *body; GFileType file_type; - SoupURI *cur_uri; + GUri *cur_uri; + char *new_path; res = TRUE; - status = g_vfs_backend_dav_send_message (backend, msg_opts); - is_success = SOUP_STATUS_IS_SUCCESSFUL (status); + body = g_vfs_backend_dav_send (backend, msg_opts, sig_opts, &error); + sig_opts = FALSE; + status = body ? soup_message_get_status (msg_opts) : SOUP_STATUS_NONE; + is_success = body && SOUP_STATUS_IS_SUCCESSFUL (status); is_webdav = sm_has_header (msg_opts, "DAV"); /* Workaround for servers which response with 403 instead of 401 in case of @@ -1956,7 +2055,7 @@ do_mount (GVfsBackend *backend, data->retrying_after_403 = TRUE; g_clear_pointer (&data->server_auth.username, g_free); - data->server_auth.username = g_strdup (mount_base->user); + data->server_auth.username = g_strdup (g_uri_get_user (mount_base)); g_clear_pointer (&data->server_auth.password, g_free); g_clear_pointer (&data->proxy_auth.password, g_free); @@ -1975,23 +2074,26 @@ do_mount (GVfsBackend *backend, * gets a chance to override it. If they do, ssl-strict is disabled but * the certificate is stored, and checked on each subsequent connection to * ensure that it hasn't changed. */ - if (status == SOUP_STATUS_SSL_FAILED && + if (g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE) && !dav_backend->certificate_errors) { GTlsCertificate *certificate; GTlsCertificateFlags errors; - soup_message_get_https_status (msg_opts, &certificate, &errors); - + certificate = soup_message_get_tls_peer_certificate (msg_opts); + errors = soup_message_get_tls_peer_certificate_errors (msg_opts); if (gvfs_accept_certificate (mount_source, certificate, errors)) { - g_object_set (session, "ssl-strict", FALSE, NULL); + g_clear_error (&error); dav_backend->certificate = g_object_ref (certificate); dav_backend->certificate_errors = errors; continue; } else { + /* break the loop, if last_good_path is NULL then the error is + * propagated, otherwise it is cleared later by the else branch + */ break; } } @@ -1999,46 +2101,61 @@ do_mount (GVfsBackend *backend, if (!is_success || !is_webdav) break; - soup_message_headers_clear (msg_opts->response_headers); - soup_message_body_truncate (msg_opts->response_body); + if (!g_vfs_backend_dav_stream_skip (body, &error)) + { + g_object_unref (body); + break; + } + + g_object_unref (body); + + soup_message_headers_clear (soup_message_get_response_headers (msg_opts)); cur_uri = soup_message_get_uri (msg_opts); soup_message_set_uri (msg_stat, cur_uri); - g_vfs_backend_dav_send_message (backend, msg_stat); - res = stat_location_finish (msg_stat, &file_type, NULL, NULL); + body = g_vfs_backend_dav_send (backend, msg_stat, sig_stat, NULL); + sig_stat = FALSE; + res = stat_location_finish (msg_stat, body, &file_type, NULL, NULL); is_collection = res && file_type == G_FILE_TYPE_DIRECTORY; + if (body && !g_vfs_backend_dav_stream_skip (body, &error)) + { + g_object_unref (body); + break; + } + + g_clear_object (&body); + + soup_message_headers_clear (soup_message_get_response_headers (msg_stat)); + g_debug (" [%s] webdav: %d, collection %d [res: %d]\n", - mount_base->path, is_webdav, is_collection, res); + g_uri_get_path (mount_base), is_webdav, is_collection, res); if (is_collection == FALSE) break; /* we have found a new good root, try the parent ... */ g_free (last_good_path); - last_good_path = mount_base->path; - mount_base->path = path_get_parent_dir (mount_base->path); + last_good_path = g_strdup (g_uri_get_path (mount_base)); + new_path = path_get_parent_dir (last_good_path); + + tmp = mount_base; + mount_base = dav_uri_dup_with (mount_base, new_path, NULL); + g_uri_unref (tmp); + G_VFS_BACKEND_HTTP (backend)->mount_base = mount_base; + + g_free (new_path); + soup_message_set_uri (msg_opts, mount_base); - if (auth_interactive) - { - /* if we have found a root that is good then we assume - that we also have obtained to correct credentials - and we switch the auth handler. This will prevent us - from asking for *different* credentials *again* if the - server should response with 401 for some of the parent - collections. See also bug #677753 */ - - g_signal_handler_disconnect (session, signal_id); - g_signal_connect (session, "authenticate", - G_CALLBACK (soup_authenticate_from_data), - data); - auth_interactive = FALSE; - } - - soup_message_headers_clear (msg_stat->response_headers); - soup_message_body_truncate (msg_stat->response_body); + /* if we have found a root that is good then we assume + that we also have obtained to correct credentials + and we switch the auth handler. This will prevent us + from asking for *different* credentials *again* if the + server should response with 401 for some of the parent + collections. See also bug #677753 */ + data->interactive = FALSE; } while (g_strcmp0 (last_good_path, "/") != 0); @@ -2049,8 +2166,13 @@ do_mount (GVfsBackend *backend, /* check if we at all have a good path */ if (last_good_path == NULL) { - if ((is_success && !is_webdav) || - msg_opts->status_code == SOUP_STATUS_METHOD_NOT_ALLOWED) + if (error) + { + g_vfs_job_failed_from_error (G_VFS_JOB (job), error); + g_error_free (error); + } + else if ((is_success && !is_webdav) || + soup_message_get_status (msg_opts) == SOUP_STATUS_METHOD_NOT_ALLOWED) { /* This means the either: a) OPTIONS request succeeded (which should be the case even for non-existent @@ -2068,14 +2190,12 @@ do_mount (GVfsBackend *backend, /* Either the OPTIONS request (is_success) or the PROPFIND request (res) failed. */ SoupMessage *target = !is_success ? msg_opts : msg_stat; - int error_code = http_error_code_from_status (target->status_code); - - if (error_code == G_IO_ERROR_CANCELLED) - error_code = G_IO_ERROR_FAILED_HANDLED; + int error_code = http_error_code_from_status (soup_message_get_status (target)); g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, error_code, - _("HTTP Error: %s"), target->reason_phrase); + _("HTTP Error: %s"), + soup_message_get_reason_phrase (target)); } else { @@ -2091,6 +2211,8 @@ do_mount (GVfsBackend *backend, return; } + else if (error) + g_error_free (error); /* Success! We are mounted */ /* Save the auth info in the keyring */ @@ -2099,8 +2221,12 @@ do_mount (GVfsBackend *backend, /* TODO: save proxy auth */ /* Set the working path in mount path */ - g_free (mount_base->path); - mount_base->path = last_good_path; + tmp = mount_base; + mount_base = dav_uri_dup_with (mount_base, last_good_path, NULL); + g_uri_unref (tmp); + g_clear_pointer (&G_VFS_BACKEND_HTTP (backend)->mount_base, g_uri_unref); + G_VFS_BACKEND_HTTP (backend)->mount_base = mount_base; + g_free (last_good_path); /* dup the mountspec, but only copy known fields */ mount_spec = g_mount_spec_from_dav_uri (dav_backend, mount_base); @@ -2141,39 +2267,35 @@ do_query_info (GVfsBackend *backend, GFileAttributeMatcher *matcher) { SoupMessage *msg; - Multistatus ms; - xmlNodeIter iter; - gboolean res; - GError *error; - - error = NULL; + GInputStream *body; + Multistatus ms; + xmlNodeIter iter; + gboolean res; + GError *error = NULL; g_debug ("Query info %s\n", filename); msg = propfind_request_new (backend, filename, 0, ls_propnames); - if (msg == NULL) { g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_FAILED, _("Could not create request")); - return; } message_add_redirect_header (msg, flags); - g_vfs_backend_dav_send_message (backend, msg); + body = g_vfs_backend_dav_send (backend, msg, TRUE, &error); + + if (!body) + goto error; - res = multistatus_parse (msg, &ms, &error); + res = multistatus_parse (msg, body, &ms, &error); + g_object_unref (body); if (res == FALSE) - { - g_vfs_job_failed_from_error (G_VFS_JOB (job), error); - g_error_free (error); - g_object_unref (msg); - return; - } + goto error; res = FALSE; multistatus_get_response_iter (&ms, &iter); @@ -2203,7 +2325,12 @@ do_query_info (GVfsBackend *backend, g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_FAILED, _("Response invalid")); + return; + error: + g_vfs_job_failed_from_error (G_VFS_JOB (job), error); + g_error_free (error); + g_object_unref (msg); } static PropName fs_info_propnames[] = { @@ -2220,10 +2347,11 @@ do_query_fs_info (GVfsBackend *backend, GFileAttributeMatcher *attribute_matcher) { SoupMessage *msg; - Multistatus ms; - xmlNodeIter iter; - gboolean res; - GError *error; + GInputStream *body; + Multistatus ms; + xmlNodeIter iter; + gboolean res; + GError *error = NULL; g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, @@ -2247,7 +2375,6 @@ do_query_fs_info (GVfsBackend *backend, } msg = propfind_request_new (backend, filename, 0, fs_info_propnames); - if (msg == NULL) { g_vfs_job_failed (G_VFS_JOB (job), @@ -2257,18 +2384,16 @@ do_query_fs_info (GVfsBackend *backend, return; } - g_vfs_backend_dav_send_message (backend, msg); + body = g_vfs_backend_dav_send (backend, msg, TRUE, &error); + + if (!body) + goto error; - error = NULL; - res = multistatus_parse (msg, &ms, &error); + res = multistatus_parse (msg, body, &ms, &error); + g_object_unref (body); if (res == FALSE) - { - g_vfs_job_failed_from_error (G_VFS_JOB (job), error); - g_error_free (error); - g_object_unref (msg); - return; - } + goto error; res = FALSE; multistatus_get_response_iter (&ms, &iter); @@ -2298,7 +2423,12 @@ do_query_fs_info (GVfsBackend *backend, g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_FAILED, _("Response invalid")); + return; + error: + g_vfs_job_failed_from_error (G_VFS_JOB (job), error); + g_error_free (error); + g_object_unref (msg); } /* *** enumerate *** */ @@ -2310,17 +2440,15 @@ do_enumerate (GVfsBackend *backend, GFileQueryInfoFlags flags) { SoupMessage *msg; - Multistatus ms; - xmlNodeIter iter; - gboolean res; - GError *error; - - error = NULL; + GInputStream *body; + Multistatus ms; + xmlNodeIter iter; + gboolean res; + GError *error = NULL; g_debug ("+ do_enumerate: %s\n", filename); msg = propfind_request_new (backend, filename, 1, ls_propnames); - if (msg == NULL) { g_vfs_job_failed (G_VFS_JOB (job), @@ -2332,17 +2460,17 @@ do_enumerate (GVfsBackend *backend, message_add_redirect_header (msg, flags); - g_vfs_backend_dav_send_message (backend, msg); + body = g_vfs_backend_dav_send (backend, msg, TRUE, &error); + + if (!body) + goto error; - res = multistatus_parse (msg, &ms, &error); + res = multistatus_parse (msg, body, &ms, &error); + g_object_unref (body); if (res == FALSE) - { - g_vfs_job_failed_from_error (G_VFS_JOB (job), error); - g_error_free (error); - g_object_unref (msg); - return; - } + goto error; + g_vfs_job_succeeded (G_VFS_JOB (job)); multistatus_get_response_iter (&ms, &iter); @@ -2370,6 +2498,12 @@ do_enumerate (GVfsBackend *backend, g_object_unref (msg); g_vfs_job_enumerate_done (G_VFS_JOB_ENUMERATE (job)); + return; + + error: + g_vfs_job_failed_from_error (G_VFS_JOB (job), error); + g_error_free (error); + g_object_unref (msg); } /* ************************************************************************* */ @@ -2377,37 +2511,51 @@ do_enumerate (GVfsBackend *backend, /* *** open () *** */ static void -try_open_stat_done (SoupSession *session, - SoupMessage *msg, - gpointer user_data) +try_open_stat_done (GObject *source, + GAsyncResult *result, + gpointer user_data) { - GVfsJob *job = G_VFS_JOB (user_data); - GVfsBackend *backend = job->backend_data; - GFileType target_type; - SoupURI *uri; - gboolean res; + GInputStream *body; + SoupSession *session = SOUP_SESSION (source); + SoupMessage *msg; + GVfsJob *job = G_VFS_JOB (user_data); + GVfsBackend *backend = job->backend_data; + GError *error = NULL; + GUri *uri; + GFileType target_type; + gboolean res; - if (msg->status_code != 207) + msg = soup_session_get_async_result_message (session, result); + if (soup_message_get_status (msg) != SOUP_STATUS_MULTI_STATUS) { http_job_failed (job, msg); return; } - res = stat_location_finish (msg, &target_type, NULL, NULL); + body = soup_session_send_finish (session, result, &error); + if (!body) + { + g_vfs_job_failed_from_error (job, error); + g_error_free (error); + return; + } + + res = stat_location_finish (msg, body, &target_type, NULL, NULL); + g_object_unref (body); if (res == FALSE) { g_vfs_job_failed (job, - G_IO_ERROR, G_IO_ERROR_FAILED, - _("Response invalid")); + G_IO_ERROR, G_IO_ERROR_FAILED, + _("Response invalid")); return; } if (target_type == G_FILE_TYPE_DIRECTORY) { g_vfs_job_failed (job, - G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY, - _("File is directory")); + G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY, + _("File is directory")); return; } @@ -2422,12 +2570,12 @@ try_open_for_read (GVfsBackend *backend, GVfsJobOpenForRead *job, const char *filename) { - SoupMessage *msg; - SoupURI *uri; + SoupMessage *msg; + GUri *uri; uri = g_vfs_backend_dav_uri_for_path (backend, filename, FALSE); msg = stat_location_begin (uri, FALSE); - soup_uri_free (uri); + g_uri_unref (uri); if (msg == NULL) { @@ -2439,7 +2587,7 @@ try_open_for_read (GVfsBackend *backend, } g_vfs_job_set_backend_data (G_VFS_JOB (job), backend, NULL); - g_vfs_backend_dav_queue_message (backend, msg, try_open_stat_done, job); + g_vfs_backend_dav_send_async (backend, msg, TRUE, try_open_stat_done, job); return TRUE; } @@ -2449,16 +2597,29 @@ try_open_for_read (GVfsBackend *backend, /* *** create () *** */ static void -try_create_tested_existence (SoupSession *session, SoupMessage *msg, +try_create_tested_existence (GObject *source, + GAsyncResult *result, gpointer user_data) { GVfsJob *job = G_VFS_JOB (user_data); - GOutputStream *stream; - SoupMessage *put_msg; - SoupURI *uri; + GOutputStream *stream; + GInputStream *body; + SoupMessage *msg = job->backend_data; + SoupMessage *put_msg; + GError *error = NULL; + GUri *uri; - if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) + body = soup_session_send_finish (SOUP_SESSION (source), result, &error); + if (!body) { + g_vfs_job_failed_from_error (job, error); + g_error_free (error); + return; + } + + if (SOUP_STATUS_IS_SUCCESSFUL (soup_message_get_status (msg))) + { + g_object_unref (body); g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_EXISTS, @@ -2467,12 +2628,14 @@ try_create_tested_existence (SoupSession *session, SoupMessage *msg, } /* TODO: other errors */ + g_object_unref (body); + uri = soup_message_get_uri (msg); put_msg = soup_message_new_from_uri (SOUP_METHOD_PUT, uri); /* * Doesn't work with apache > 2.2.9 - * soup_message_headers_append (put_msg->request_headers, "If-None-Match", "*"); + * soup_message_headers_append (soup_message_get_request_headers (put_msg), "If-None-Match", "*"); */ stream = g_memory_output_stream_new (NULL, 0, g_try_realloc, g_free); g_object_set_data_full (G_OBJECT (stream), "-gvfs-stream-msg", put_msg, g_object_unref); @@ -2492,25 +2655,28 @@ try_create (GVfsBackend *backend, GFileCreateFlags flags) { SoupMessage *msg; - SoupURI *uri; + GUri *uri; /* TODO: if we supported chunked requests, we could * use a PUT with "If-None-Match: *" and "Expect: 100-continue" */ uri = g_vfs_backend_dav_uri_for_path (backend, filename, FALSE); msg = soup_message_new_from_uri (SOUP_METHOD_HEAD, uri); - soup_uri_free (uri); + g_uri_unref (uri); - g_vfs_job_set_backend_data (G_VFS_JOB (job), backend, NULL); + g_vfs_job_set_backend_data (G_VFS_JOB (job), msg, NULL); + + g_vfs_backend_dav_send_async (backend, msg, TRUE, try_create_tested_existence, job); - g_vfs_backend_dav_queue_message (backend, msg, try_create_tested_existence, job); return TRUE; } /* *** replace () *** */ static void -open_for_replace_succeeded (GVfsBackendHttp *op_backend, GVfsJob *job, - SoupURI *uri, const char *etag) +open_for_replace_succeeded (GVfsBackendHttp *op_backend, + GVfsJob *job, + GUri *uri, + const char *etag) { SoupMessage *put_msg; GOutputStream *stream; @@ -2518,7 +2684,9 @@ open_for_replace_succeeded (GVfsBackendHttp *op_backend, GVfsJob *job, put_msg = soup_message_new_from_uri (SOUP_METHOD_PUT, uri); if (etag) - soup_message_headers_append (put_msg->request_headers, "If-Match", etag); + soup_message_headers_append (soup_message_get_request_headers (put_msg), + "If-Match", + etag); stream = g_memory_output_stream_new (NULL, 0, g_try_realloc, g_free); g_object_set_data_full (G_OBJECT (stream), "-gvfs-stream-msg", put_msg, g_object_unref); @@ -2532,14 +2700,29 @@ open_for_replace_succeeded (GVfsBackendHttp *op_backend, GVfsJob *job, } static void -try_replace_checked_etag (SoupSession *session, SoupMessage *msg, - gpointer user_data) +try_replace_checked_etag (GObject *source, + GAsyncResult *result, + gpointer user_data) { GVfsJob *job = G_VFS_JOB (user_data); GVfsBackendHttp *op_backend = job->backend_data; + GInputStream *body; + SoupSession *session = SOUP_SESSION (source); + SoupMessage *msg; + GError *error; - if (msg->status_code == SOUP_STATUS_PRECONDITION_FAILED) + msg = soup_session_get_async_result_message (session, result); + body = soup_session_send_finish (session, result, &error); + if (!body) { + g_vfs_job_failed_from_error (job, error); + g_error_free (error); + return; + } + + if (soup_message_get_status (msg) == SOUP_STATUS_PRECONDITION_FAILED) + { + g_object_unref (body); g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_WRONG_ETAG, @@ -2548,8 +2731,11 @@ try_replace_checked_etag (SoupSession *session, SoupMessage *msg, } /* TODO: other errors */ + g_object_unref (body); + open_for_replace_succeeded (op_backend, job, soup_message_get_uri (msg), - soup_message_headers_get_one (msg->request_headers, "If-Match")); + soup_message_headers_get_one (soup_message_get_request_headers (msg), + "If-Match")); } static gboolean @@ -2561,7 +2747,7 @@ try_replace (GVfsBackend *backend, GFileCreateFlags flags) { GVfsBackendHttp *op_backend; - SoupURI *uri; + GUri *uri; /* TODO: if SoupOutputStream supported chunked requests, we could * use a PUT with "If-Match: ..." and "Expect: 100-continue" @@ -2587,17 +2773,21 @@ try_replace (GVfsBackend *backend, SoupMessage *msg; msg = soup_message_new_from_uri (SOUP_METHOD_HEAD, uri); - soup_uri_free (uri); - soup_message_headers_append (msg->request_headers, "If-Match", etag); + g_uri_unref (uri); + + soup_message_headers_append (soup_message_get_request_headers (msg), + "If-Match", etag); g_vfs_job_set_backend_data (G_VFS_JOB (job), op_backend, NULL); - g_vfs_backend_dav_queue_message (backend, msg, - try_replace_checked_etag, job); + g_vfs_backend_dav_send_async (backend, msg, TRUE, + try_replace_checked_etag, job); + return TRUE; } open_for_replace_succeeded (op_backend, G_VFS_JOB (job), uri, NULL); - soup_uri_free (uri); + g_uri_unref (uri); + return TRUE; } @@ -2698,14 +2888,27 @@ do_truncate (GVfsBackend *backend, /* *** close_write () *** */ static void -try_close_write_sent (SoupSession *session, - SoupMessage *msg, - gpointer user_data) +try_close_write_sent (GObject *source, + GAsyncResult *result, + gpointer user_data) { - GVfsJob *job; + GInputStream *body; + GVfsJob *job = G_VFS_JOB (user_data); + SoupSession *session = SOUP_SESSION (source); + SoupMessage *msg = soup_session_get_async_result_message (session, result); + GError *error; - job = G_VFS_JOB (user_data); - if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) + body = soup_session_send_finish (session, result, &error); + if (!body) + { + g_vfs_job_failed_from_error (job, error); + g_error_free (error); + return; + } + + g_object_unref (body); + + if (!SOUP_STATUS_IS_SUCCESSFUL (soup_message_get_status (msg))) http_job_failed (job, msg); else g_vfs_job_succeeded (job); @@ -2718,8 +2921,7 @@ try_close_write (GVfsBackend *backend, { GOutputStream *stream; SoupMessage *msg; - gsize length; - gchar *data; + GBytes *bytes; stream = G_OUTPUT_STREAM (handle); @@ -2728,13 +2930,13 @@ try_close_write (GVfsBackend *backend, g_object_set_data (G_OBJECT (stream), "-gvfs-stream-msg", NULL); g_output_stream_close (stream, NULL, NULL); - length = g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (stream)); - data = g_memory_output_stream_steal_data (G_MEMORY_OUTPUT_STREAM (stream)); + bytes = g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (stream)); g_object_unref (stream); - soup_message_body_append (msg->request_body, SOUP_MEMORY_TAKE, data, length); - g_vfs_backend_dav_queue_message (backend, msg, - try_close_write_sent, job); + soup_message_set_request_body_from_bytes (msg, NULL, bytes); + g_vfs_backend_dav_send_async (backend, msg, TRUE, + try_close_write_sent, job); + g_bytes_unref (bytes); return TRUE; } @@ -2744,15 +2946,26 @@ do_make_directory (GVfsBackend *backend, GVfsJobMakeDirectory *job, const char *filename) { + GInputStream *body; SoupMessage *msg; - SoupURI *uri; - guint status; + GUri *uri; + GError *error = NULL; + guint status; uri = g_vfs_backend_dav_uri_for_path (backend, filename, TRUE); msg = soup_message_new_from_uri (SOUP_METHOD_MKCOL, uri); - soup_uri_free (uri); + g_uri_unref (uri); + + body = g_vfs_backend_dav_send (backend, msg, TRUE, &error); + if (!body) + { + g_vfs_job_failed_from_error (G_VFS_JOB (job), error); + g_error_free (error); + g_object_unref (msg); + return; + } - status = g_vfs_backend_dav_send_message (backend, msg); + status = soup_message_get_status (msg); if (! SOUP_STATUS_IS_SUCCESSFUL (status)) if (status == SOUP_STATUS_METHOD_NOT_ALLOWED) @@ -2764,6 +2977,7 @@ do_make_directory (GVfsBackend *backend, else g_vfs_job_succeeded (G_VFS_JOB (job)); + g_object_unref (body); g_object_unref (msg); } @@ -2772,24 +2986,22 @@ do_delete (GVfsBackend *backend, GVfsJobDelete *job, const char *filename) { + GInputStream *body; SoupMessage *msg; - SoupURI *uri; - GFileType file_type; - gboolean res; - guint num_children; - guint status; - GError *error; - - error = NULL; + GUri *uri; + GFileType file_type; + gboolean res; + guint num_children; + guint status; + GError *error = NULL; uri = g_vfs_backend_dav_uri_for_path (backend, filename, FALSE); res = stat_location (backend, uri, &file_type, NULL, &num_children, &error); - if (res == FALSE) { g_vfs_job_failed_from_error (G_VFS_JOB (job), error); g_error_free (error); - soup_uri_free (uri); + g_uri_unref (uri); return; } @@ -2798,21 +3010,30 @@ do_delete (GVfsBackend *backend, g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_EMPTY, _("Directory not empty")); - soup_uri_free (uri); + g_uri_unref (uri); return; } msg = soup_message_new_from_uri (SOUP_METHOD_DELETE, uri); + body = g_vfs_backend_dav_send (backend, msg, TRUE, &error); + if (!body) + { + g_vfs_job_failed_from_error (G_VFS_JOB (job), error); + g_error_free (error); + g_object_unref (msg); + g_uri_unref (uri); + return; + } - status = g_vfs_backend_dav_send_message (backend, msg); - + status = soup_message_get_status (msg); if (!SOUP_STATUS_IS_SUCCESSFUL (status)) http_job_failed (G_VFS_JOB (job), msg); else g_vfs_job_succeeded (G_VFS_JOB (job)); - soup_uri_free (uri); + g_uri_unref (uri); g_object_unref (msg); + g_object_unref (body); } static void @@ -2821,12 +3042,14 @@ do_set_display_name (GVfsBackend *backend, const char *filename, const char *display_name) { + GInputStream *body; SoupMessage *msg; - SoupURI *source; - SoupURI *target; - char *target_path; - char *dirname; - guint status; + GUri *source; + GUri *target; + char *target_path; + char *dirname; + GError *error = NULL; + guint status; source = g_vfs_backend_dav_uri_for_path (backend, filename, FALSE); msg = soup_message_new_from_uri (SOUP_METHOD_MOVE, source); @@ -2838,7 +3061,14 @@ do_set_display_name (GVfsBackend *backend, message_add_destination_header (msg, target); message_add_overwrite_header (msg, FALSE); - status = g_vfs_backend_dav_send_message (backend, msg); + body = g_vfs_backend_dav_send (backend, msg, TRUE, &error); + if (!body) + { + http_job_failed (G_VFS_JOB (job), msg); + goto error; + } + + status = soup_message_get_status (msg); /* * The precondition of SOUP_STATUS_PRECONDITION_FAILED (412) in @@ -2870,11 +3100,14 @@ do_set_display_name (GVfsBackend *backend, else http_job_failed (G_VFS_JOB (job), msg); + g_object_unref (body); + + error: g_object_unref (msg); g_free (dirname); g_free (target_path); - soup_uri_free (target); - soup_uri_free (source); + g_uri_unref (target); + g_uri_unref (source); } static void @@ -2886,9 +3119,10 @@ do_move (GVfsBackend *backend, GFileProgressCallback progress_callback, gpointer progress_callback_data) { + GInputStream *body = NULL; SoupMessage *msg; - SoupURI *source_uri; - SoupURI *target_uri; + GUri *source_uri; + GUri *target_uri; guint status; GFileType source_ft, target_ft; GError *error = NULL; @@ -2958,14 +3192,20 @@ do_move (GVfsBackend *backend, msg = soup_message_new_from_uri (SOUP_METHOD_DELETE, target_uri); - status = g_vfs_backend_dav_send_message (backend, msg); + body = g_vfs_backend_dav_send (backend, msg, TRUE, &error); + if (!body) + { + g_vfs_job_failed_from_error (G_VFS_JOB (job), error); + goto error; + } + status = soup_message_get_status (msg); if (!SOUP_STATUS_IS_SUCCESSFUL (status)) { http_job_failed (G_VFS_JOB (job), msg); - g_object_unref (msg); goto error; } + g_object_unref (body); g_object_unref (msg); } } @@ -2988,11 +3228,16 @@ do_move (GVfsBackend *backend, message_add_destination_header (msg, target_uri); message_add_overwrite_header (msg, flags & G_FILE_COPY_OVERWRITE); - status = g_vfs_backend_dav_send_message (backend, msg); + body = g_vfs_backend_dav_send (backend, msg, TRUE, &error); + if (!body) + { + g_vfs_job_failed_from_error (G_VFS_JOB (job), error); + goto error; + } /* See do_set_display_name () for the explanation of the PRECONDITION_FAILED * and IS_REDIRECTION handling below. */ - + status = soup_message_get_status (msg); if (SOUP_STATUS_IS_SUCCESSFUL (status)) { if (stat_res && progress_callback) @@ -3007,11 +3252,12 @@ do_move (GVfsBackend *backend, else http_job_failed (G_VFS_JOB (job), msg); -error: + error: + g_clear_object (&body); g_object_unref (msg); g_clear_error (&error); - soup_uri_free (source_uri); - soup_uri_free (target_uri); + g_uri_unref (source_uri); + g_uri_unref (target_uri); } static void @@ -3023,9 +3269,10 @@ do_copy (GVfsBackend *backend, GFileProgressCallback progress_callback, gpointer progress_callback_data) { + GInputStream *body; SoupMessage *msg; - SoupURI *source_uri; - SoupURI *target_uri; + GUri *source_uri; + GUri *target_uri; guint status; GFileType source_ft, target_ft; GError *error = NULL; @@ -3102,11 +3349,17 @@ do_copy (GVfsBackend *backend, message_add_destination_header (msg, target_uri); message_add_overwrite_header (msg, flags & G_FILE_COPY_OVERWRITE); - status = g_vfs_backend_dav_send_message (backend, msg); + body = g_vfs_backend_dav_send (backend, msg, TRUE, &error); + if (!body) + { + g_vfs_job_failed_from_error (G_VFS_JOB (job), error); + g_object_unref (msg); + goto error; + } /* See do_set_display_name () for the explanation of the PRECONDITION_FAILED * and IS_REDIRECTION handling below. */ - + status = soup_message_get_status (msg); if (SOUP_STATUS_IS_SUCCESSFUL (status)) { if (progress_callback) @@ -3121,24 +3374,17 @@ do_copy (GVfsBackend *backend, else http_job_failed (G_VFS_JOB (job), msg); + g_object_unref (body); g_object_unref (msg); error: g_clear_error (&error); - soup_uri_free (source_uri); - soup_uri_free (target_uri); + g_uri_unref (source_uri); + g_uri_unref (target_uri); } #define CHUNK_SIZE 65536 -/* Used to keep track of the state of reads in flight when the restarted signal - * is received. */ -typedef enum { - PUSH_READ_STATUS_NONE, - PUSH_READ_STATUS_RESET, - PUSH_READ_STATUS_DEFERRED, -} PushReadStatus; - typedef struct { /* Job details */ GVfsBackend *backend; @@ -3147,21 +3393,15 @@ typedef struct { /* Local file */ GInputStream *in; - unsigned char *buf; goffset size; - goffset n_read; - PushReadStatus read_status; /* Remote file */ - SoupURI *uri; + GUri *uri; SoupMessage *msg; goffset n_written; } PushHandle; static void -push_write_next_chunk (SoupMessage *msg, gpointer user_data); - -static void push_handle_free (PushHandle *handle) { if (handle->in) @@ -3171,121 +3411,20 @@ push_handle_free (PushHandle *handle) } g_object_unref (handle->backend); g_object_unref (handle->job); - soup_uri_free (handle->uri); - g_slice_free (PushHandle, handle); -} + g_object_unref (handle->msg); + g_uri_unref (handle->uri); -static void -push_read_cb (GObject *source, GAsyncResult *res, gpointer user_data) -{ - PushHandle *handle = user_data; - GError *error = NULL; - gssize n; - - n = g_input_stream_read_finish (handle->in, res, &error); - - /* Ignore this read if we've subsequently been restarted. */ - if (handle->read_status != PUSH_READ_STATUS_NONE) - { - g_free (handle->buf); - handle->buf = NULL; - - /* Queue another read if we've been subsequently restarted and - * push_write_next_chunk () was called in the meantime. */ - if (handle->read_status == PUSH_READ_STATUS_DEFERRED) - push_write_next_chunk (handle->msg, handle); - - return; - } - - if (n > 0) - { - soup_message_body_append_take (handle->msg->request_body, handle->buf, n); - handle->buf = NULL; - handle->n_read += n; - soup_session_unpause_message (G_VFS_BACKEND_HTTP (handle->backend)->session, - handle->msg); - } - else if (n == 0) - { - g_free (handle->buf); - handle->buf = NULL; - - if (handle->n_read != handle->size) - { - g_vfs_job_failed_literal (handle->job, - G_IO_ERROR, - G_IO_ERROR_FAILED, - _("File length changed during transfer")); - - soup_session_cancel_message (G_VFS_BACKEND_HTTP (handle->backend)->session, - handle->msg, - SOUP_STATUS_CANCELLED); - } - } - else - { - g_free (handle->buf); - handle->buf = NULL; - g_vfs_job_failed_from_error (handle->job, error); - g_error_free (error); - soup_session_cancel_message (G_VFS_BACKEND_HTTP (handle->backend)->session, - handle->msg, - SOUP_STATUS_CANCELLED); - } -} - -static void -push_write_next_chunk (SoupMessage *msg, gpointer user_data) -{ - PushHandle *handle = user_data; - - /* If we've been restarted, seek to the beginning of the file. */ - if (handle->read_status == PUSH_READ_STATUS_RESET) - { - GError *error = NULL; - - /* We've been restarted but there's still a read in flight, so defer. */ - if (handle->buf) - { - handle->read_status = PUSH_READ_STATUS_DEFERRED; - return; - } - - handle->n_read = 0; - handle->n_written = 0; - handle->read_status = PUSH_READ_STATUS_NONE; - - if (!g_seekable_seek (G_SEEKABLE (handle->in), - 0, G_SEEK_SET, - handle->job->cancellable, &error)) - { - g_vfs_job_failed_from_error (handle->job, error); - g_error_free (error); - soup_session_cancel_message (G_VFS_BACKEND_HTTP (handle->backend)->session, - handle->msg, - SOUP_STATUS_CANCELLED); - return; - } - } - - handle->buf = g_malloc (CHUNK_SIZE); - g_input_stream_read_async (handle->in, - handle->buf, CHUNK_SIZE, - 0, handle->job->cancellable, - push_read_cb, handle); + g_slice_free (PushHandle, handle); } static void push_setup_message (PushHandle *handle) { - soup_message_set_flags (handle->msg, SOUP_MESSAGE_CAN_REBUILD); - soup_message_body_set_accumulate (handle->msg->request_body, FALSE); message_add_overwrite_header (handle->msg, handle->op_job->flags & G_FILE_COPY_OVERWRITE); - soup_message_headers_set_encoding (handle->msg->request_headers, + soup_message_headers_set_encoding (soup_message_get_request_headers (handle->msg), SOUP_ENCODING_CONTENT_LENGTH); - soup_message_headers_set_content_length (handle->msg->request_headers, + soup_message_headers_set_content_length (soup_message_get_request_headers (handle->msg), handle->size); } @@ -3294,28 +3433,31 @@ push_restarted (SoupMessage *msg, gpointer user_data) { PushHandle *handle = user_data; - handle->read_status = PUSH_READ_STATUS_RESET; - msg->method = SOUP_METHOD_PUT; + handle->n_written = 0; + + g_object_set (msg, "method", SOUP_METHOD_PUT, NULL); push_setup_message (handle); + + soup_message_set_request_body (handle->msg, NULL, handle->in, handle->size); } static void -push_wrote_body_data (SoupMessage *msg, SoupBuffer *chunk, gpointer user_data) +push_wrote_body_data (SoupMessage *msg, guint chunk_size, gpointer user_data) { PushHandle *handle = user_data; - handle->n_written += chunk->length; + handle->n_written += chunk_size; g_vfs_job_progress_callback (handle->n_written, handle->size, handle->job); } static void -push_done (SoupSession *session, SoupMessage *msg, gpointer user_data) +push_finished (SoupMessage *msg, gpointer user_data) { PushHandle *handle = user_data; if (g_vfs_job_is_finished (handle->job)) ; /* We got an error so we finished the job and cancelled msg. */ - else if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) + else if (!SOUP_STATUS_IS_SUCCESSFUL (soup_message_get_status (msg))) http_job_failed (handle->job, msg); else { @@ -3329,15 +3471,43 @@ push_done (SoupSession *session, SoupMessage *msg, gpointer user_data) } static void -push_stat_dest_cb (SoupSession *session, SoupMessage *msg, gpointer user_data) +push_done (GObject *source, GAsyncResult *result, gpointer user_data) +{ + GInputStream *body; + GVfsJob *job = G_VFS_JOB (user_data);; + GError *error = NULL; + + body = soup_session_send_finish (SOUP_SESSION (source), result, &error); + if (!body) + { + g_vfs_job_failed_from_error (job, error); + g_error_free (error); + } + else + g_object_unref (body); +} + +static void +push_stat_dest_cb (GObject *source, GAsyncResult *result, gpointer user_data) { + GInputStream *body; PushHandle *handle = user_data; GFileType type; + GError *error = NULL; - if (stat_location_finish (msg, &type, NULL, NULL)) + body = soup_session_send_finish (SOUP_SESSION (source), result, &error); + if (!body) + { + g_vfs_job_failed_from_error (handle->job, error); + g_error_free (error); + return; + } + + if (stat_location_finish (handle->msg, body, &type, NULL, NULL)) { if (!(handle->op_job->flags & G_FILE_COPY_OVERWRITE)) { + g_object_unref (body); g_vfs_job_failed (handle->job, G_IO_ERROR, G_IO_ERROR_EXISTS, @@ -3347,6 +3517,7 @@ push_stat_dest_cb (SoupSession *session, SoupMessage *msg, gpointer user_data) } if (type == G_FILE_TYPE_DIRECTORY) { + g_object_unref (body); g_vfs_job_failed (handle->job, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY, @@ -3356,20 +3527,23 @@ push_stat_dest_cb (SoupSession *session, SoupMessage *msg, gpointer user_data) } } + g_object_unref (body); + g_object_unref (handle->msg); + handle->msg = soup_message_new_from_uri (SOUP_METHOD_PUT, handle->uri); push_setup_message (handle); + soup_message_set_request_body (handle->msg, NULL, handle->in, handle->size); + g_signal_connect (handle->msg, "restarted", G_CALLBACK (push_restarted), handle); - g_signal_connect (handle->msg, "wrote_headers", - G_CALLBACK (push_write_next_chunk), handle); - g_signal_connect (handle->msg, "wrote_chunk", - G_CALLBACK (push_write_next_chunk), handle); g_signal_connect (handle->msg, "wrote-body-data", G_CALLBACK (push_wrote_body_data), handle); + g_signal_connect (handle->msg, "finished", + G_CALLBACK (push_finished), handle); - g_vfs_backend_dav_queue_message (handle->backend, handle->msg, - push_done, handle); + g_vfs_backend_dav_send_async (handle->backend, handle->msg, TRUE, + push_done, handle->job); } static void @@ -3383,14 +3557,12 @@ push_source_fstat_cb (GObject *source, GAsyncResult *res, gpointer user_data) info = g_file_input_stream_query_info_finish (fin, res, &error); if (info) { - SoupMessage *msg; - handle->size = g_file_info_get_size (info); g_object_unref (info); - msg = stat_location_begin (handle->uri, FALSE); - g_vfs_backend_dav_queue_message (handle->backend, msg, - push_stat_dest_cb, handle); + handle->msg = stat_location_begin (handle->uri, FALSE); + g_vfs_backend_dav_send_async (handle->backend, handle->msg, TRUE, + push_stat_dest_cb, handle); } else { @@ -3538,4 +3710,10 @@ g_vfs_backend_dav_class_init (GVfsBackendDavClass *klass) backend_class->move = do_move; backend_class->copy = do_copy; backend_class->try_push = try_push; + + /* override the maximum number of connections, since the libsoup defaults + * of 10 and 2 respectively are too low and may cause backend lockups when + * a lot of files are opened at the same time + */ + http_try_init_session (32, 32); } 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 <libsoup/soup.h> #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 *** |