diff --git a/android_webview/browser/aw_browser_context.cc b/android_webview/browser/aw_browser_context.cc index 6167ef5ed42af..6af9e477c4c8a 100644 --- a/android_webview/browser/aw_browser_context.cc +++ b/android_webview/browser/aw_browser_context.cc @@ -574,6 +574,9 @@ void AwBrowserContext::ConfigureNetworkContextParams( // (http://crbug.com/921750). context_params->enforce_chrome_ct_policy = false; + // WebView does not support ftp yet. + context_params->enable_ftp_url_support = false; + context_params->enable_brotli = true; context_params->enable_zstd = base::FeatureList::IsEnabled(net::features::kZstdContentEncoding); diff --git a/chrome/app/app-Info.plist b/chrome/app/app-Info.plist index 5654e5c9d5858..08d00c2005d7b 100644 --- a/chrome/app/app-Info.plist +++ b/chrome/app/app-Info.plist @@ -233,6 +233,14 @@ https + + CFBundleURLName + FTP site URL + CFBundleURLSchemes + + ftp + + CFBundleURLName Local file URL diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index ceabbced0f67e..c298005d46bc3 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -13574,6 +13574,9 @@ This can include information about installed software, files, your browser, and Date Modified + + Oh, no! This server is sending data $1Thorium can't understand. Please <a href="https://issues.chromium.org/issues/new">report a bug</a></a>, and include the <a href="LOCATION">raw listing</a>. + diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index c9e74052c7afb..d271ccc0dca71 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc @@ -8542,6 +8542,12 @@ const FeatureEntry kFeatureEntries[] = { FEATURE_VALUE_TYPE( heavy_ad_intervention::features::kHeavyAdPrivacyMitigations)}, +#if !BUILDFLAG(DISABLE_FTP_SUPPORT) + {"enable-ftp", flag_descriptions::kEnableFtpName, + flag_descriptions::kEnableFtpDescription, kOsAll, + FEATURE_VALUE_TYPE(network::features::kFtpProtocol)}, +#endif + #if BUILDFLAG(IS_CHROMEOS_ASH) {"crostini-container-install", flag_descriptions::kCrostiniContainerInstallName, diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index e2a95fa3aadaa..5b608e9272838 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc @@ -355,6 +355,13 @@ const char kEnableDrDcDescription[] = "(raster, webgl, video) " " continues using the gpu main thread."; +const char kEnableFtpName[] = "Enable support for FTP URLs. Enabled by default. Thorium Flag."; +const char kEnableFtpDescription[] = + "When enabled, the browser will handle navigations to ftp:// URLs by " + "either showing a directory listing or downloading the resource over FTP. " + "When disabled, the browser has no special handling for ftp:// URLs and " + "by default defers handling of the URL to the underlying platform."; + const char kTextBasedAudioDescriptionName[] = "Enable audio descriptions."; const char kTextBasedAudioDescriptionDescription[] = "When enabled, HTML5 video elements with a 'descriptions' WebVTT track " diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index 4ef2b219f1d74..69f9c4f7d9e1a 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h @@ -255,6 +255,9 @@ extern const char extern const char kEnableExtensionsPermissionsForSupervisedUsersOnDesktopDescription[]; +extern const char kEnableFtpName[]; +extern const char kEnableFtpDescription[]; + extern const char kEnableSupervisedUserSkipParentApprovalToInstallExtensionsName[]; extern const char diff --git a/chrome/browser/net/profile_network_context_service.cc b/chrome/browser/net/profile_network_context_service.cc index e50641649a959..0f0ef6bd2b0fd 100644 --- a/chrome/browser/net/profile_network_context_service.cc +++ b/chrome/browser/net/profile_network_context_service.cc @@ -1190,6 +1190,15 @@ void ProfileNetworkContextService::ConfigureNetworkContextParamsInternal( network_context_params->hsts_policy_bypass_list.push_back(*string_value); } + // NOTE(mmenke): Keep these protocol handlers and + // ProfileIOData::SetUpJobFactoryDefaultsForBuilder in sync with + // ProfileIOData::IsHandledProtocol(). + // TODO(mmenke): Find a better way of handling tracking supported schemes. +#if !BUILDFLAG(DISABLE_FTP_SUPPORT) + network_context_params->enable_ftp_url_support = + base::FeatureList::IsEnabled(network::features::kFtpProtocol); +#endif // !BUILDFLAG(DISABLE_FTP_SUPPORT) + proxy_config_monitor_.AddToNetworkContextParams(network_context_params); network_context_params->enable_certificate_reporting = true; diff --git a/chrome/browser/net/system_network_context_manager.cc b/chrome/browser/net/system_network_context_manager.cc index 22a62ad698fdb..ec30208a38bf0 100644 --- a/chrome/browser/net/system_network_context_manager.cc +++ b/chrome/browser/net/system_network_context_manager.cc @@ -1042,6 +1042,12 @@ SystemNetworkContextManager::CreateNetworkContextParams() { network_context_params->http_cache_enabled = false; + // These are needed for PAC scripts that use FTP URLs. +#if !BUILDFLAG(DISABLE_FTP_SUPPORT) + network_context_params->enable_ftp_url_support = + base::FeatureList::IsEnabled(network::features::kFtpProtocol); +#endif + proxy_config_monitor_.AddToNetworkContextParams(network_context_params.get()); return network_context_params; diff --git a/chrome/browser/profiles/profile_io_data.cc b/chrome/browser/profiles/profile_io_data.cc index 39f96f45bca3a..123e400761e0f 100644 --- a/chrome/browser/profiles/profile_io_data.cc +++ b/chrome/browser/profiles/profile_io_data.cc @@ -15,6 +15,7 @@ #include "components/dom_distiller/core/url_constants.h" #include "extensions/buildflags/buildflags.h" #include "net/net_buildflags.h" +#include "services/network/public/cpp/features.h" #include "url/gurl.h" #if BUILDFLAG(ENABLE_EXTENSIONS_CORE) @@ -54,6 +55,9 @@ bool ProfileIOData::IsHandledProtocol(const std::string& scheme) { #if !BUILDFLAG(IS_ANDROID) chrome::kIsolatedAppScheme, #endif // !BUILDFLAG(IS_ANDROID) +#if !BUILDFLAG(DISABLE_FTP_SUPPORT) + url::kFtpScheme, +#endif // !BUILDFLAG(DISABLE_FTP_SUPPORT) }); return kProtocolList.contains(scheme); diff --git a/chrome/browser/ui/login/login_tab_helper.cc b/chrome/browser/ui/login/login_tab_helper.cc index f6702ac512f8d..a54956a7ec00a 100644 --- a/chrome/browser/ui/login/login_tab_helper.cc +++ b/chrome/browser/ui/login/login_tab_helper.cc @@ -74,13 +74,16 @@ void LoginTabHelper::DidFinishNavigation( return; } - // Show a login prompt with the navigation's AuthChallengeInfo on HTTP 401/407 - // responses. - int response_code = navigation_handle->GetResponseHeaders()->response_code(); - if (response_code != - net::HttpStatusCode::HTTP_PROXY_AUTHENTICATION_REQUIRED && - response_code != net::HttpStatusCode::HTTP_UNAUTHORIZED) { - return; + // Show a login prompt with the navigation's AuthChallengeInfo on FTP + // navigations and on HTTP 401/407 responses. + if (!navigation_handle->GetURL().SchemeIs(url::kFtpScheme)) { + int response_code = + navigation_handle->GetResponseHeaders()->response_code(); + if (response_code != + net::HttpStatusCode::HTTP_PROXY_AUTHENTICATION_REQUIRED && + response_code != net::HttpStatusCode::HTTP_UNAUTHORIZED) { + return; + } } challenge_ = navigation_handle->GetAuthChallengeInfo().value(); diff --git a/chrome/common/net/net_resource_provider.cc b/chrome/common/net/net_resource_provider.cc index 88d43505881b5..92a452fc5f9e9 100644 --- a/chrome/common/net/net_resource_provider.cc +++ b/chrome/common/net/net_resource_provider.cc @@ -35,6 +35,8 @@ struct LazyDirectoryListerCacher { l10n_util::GetStringUTF8(IDS_DIRECTORY_LISTING_DATE_MODIFIED)); value.Set("language", l10n_util::GetLanguage(base::i18n::GetConfiguredLocale())); + value.Set("listingParsingErrorBoxText", + l10n_util::GetStringUTF8(IDS_DIRECTORY_LISTING_PARSING_ERROR_BOX_TEXT)); value.Set("textdirection", base::i18n::IsRTL() ? "rtl" : "ltr"); std::string str = webui::GetI18nTemplateHtml( ui::ResourceBundle::GetSharedInstance().LoadDataResourceString( diff --git a/chrome/installer/util/shell_util.cc b/chrome/installer/util/shell_util.cc index c087d1c02bca4..aa5d721c5ea93 100644 --- a/chrome/installer/util/shell_util.cc +++ b/chrome/installer/util/shell_util.cc @@ -1,4 +1,4 @@ -// Copyright 2012 The Chromium Authors +// Copyright 2024 The Chromium Authors and Alex313031 // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // @@ -1545,10 +1545,10 @@ const wchar_t* ShellUtil::kDefaultFileAssociations[] = { const wchar_t* ShellUtil::kPotentialFileAssociations[] = { L".htm", L".html", L".mhtml", L".pdf", L".shtml", L".svg", L".xht", L".xhtml", L".webp", nullptr}; -const wchar_t* ShellUtil::kBrowserProtocolAssociations[] = {L"http", L"https", +const wchar_t* ShellUtil::kBrowserProtocolAssociations[] = {L"ftp", L"http", L"https", nullptr}; const wchar_t* ShellUtil::kPotentialProtocolAssociations[] = { - L"http", L"https", L"irc", L"mailto", L"mms", L"news", L"nntp", + L"ftp", L"http", L"https", L"irc", L"mailto", L"mms", L"news", L"nntp", L"sms", L"smsto", L"snews", L"tel", L"urn", L"webcal", nullptr}; const wchar_t* ShellUtil::kRegUrlProtocol = L"URL Protocol"; const wchar_t* ShellUtil::kRegApplication = L"\\Application"; diff --git a/chrome/test/data/ftp/dir1/test.html b/chrome/test/data/ftp/dir1/test.html new file mode 100644 index 0000000000000..ce0c4c91818cd --- /dev/null +++ b/chrome/test/data/ftp/dir1/test.html @@ -0,0 +1,5 @@ + + +PASS + + diff --git a/components/safe_browsing/content/browser/safe_browsing_network_context.cc b/components/safe_browsing/content/browser/safe_browsing_network_context.cc index 37852c47b90d4..8be654c440fc9 100644 --- a/components/safe_browsing/content/browser/safe_browsing_network_context.cc +++ b/components/safe_browsing/content/browser/safe_browsing_network_context.cc @@ -133,6 +133,10 @@ class SafeBrowsingNetworkContext::SharedURLLoaderFactory network_context_params->file_paths->trigger_migration = trigger_migration_; network_context_params->file_paths->cookie_database_name = base::FilePath( base::FilePath::StringType(kSafeBrowsingBaseFilename) + kCookiesFile); + // These are needed for PAC scripts that use FTP URLs. +#if !BUILDFLAG(DISABLE_FTP_SUPPORT) + network_context_params->enable_ftp_url_support = true; +#endif // !BUILDFLAG(DISABLE_FTP_SUPPORT) network_context_params->enable_encrypted_cookies = false; return network_context_params; diff --git a/content/browser/browser_url_handler_impl.cc b/content/browser/browser_url_handler_impl.cc index 3158ee1460b5b..8f62b65b2ee3e 100644 --- a/content/browser/browser_url_handler_impl.cc +++ b/content/browser/browser_url_handler_impl.cc @@ -34,6 +34,7 @@ static bool HandleViewSource(GURL* url, BrowserContext* browser_context) { std::vector all_allowed_sub_schemes({ url::kHttpScheme, url::kHttpsScheme, + url::kFtpScheme, kChromeUIScheme, url::kFileScheme, url::kFileSystemScheme, diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc index 30f63bfc1750f..a38a1839d7207 100644 --- a/content/public/common/content_switches.cc +++ b/content/public/common/content_switches.cc @@ -329,6 +329,9 @@ const char kEnableFakeNoAllocDirectCallForTesting[] = // status:"experimental", which are enabled when running web tests. const char kEnableBlinkTestFeatures[] = "enable-blink-test-features"; +// Enables support for FTP URLs. See https://crbug.com/333943. +const char kEnableFtp[] = "enable-ftp"; + // Disables all RuntimeEnabledFeatures that can be enabled via OriginTrials. const char kDisableOriginTrialControlledBlinkFeatures[] = "disable-origin-trial-controlled-blink-features"; diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h index b6f3b21a2b1ff..d01af10dcec28 100644 --- a/content/public/common/content_switches.h +++ b/content/public/common/content_switches.h @@ -106,6 +106,7 @@ CONTENT_EXPORT extern const char kEnableExperimentalWebAssemblyFeatures[]; CONTENT_EXPORT extern const char kEnableExperimentalWebAssemblyFeatures[]; CONTENT_EXPORT extern const char kEnableExperimentalWebPlatformFeatures[]; CONTENT_EXPORT extern const char kEnableBlinkTestFeatures[]; +CONTENT_EXPORT extern const char kEnableFtp[]; CONTENT_EXPORT extern const char kEnableGpuMemoryBufferVideoFrames[]; CONTENT_EXPORT extern const char kEnableIsolatedWebAppsInRenderer[]; CONTENT_EXPORT extern const char kEnableLCDText[]; diff --git a/content/public/renderer/render_frame.h b/content/public/renderer/render_frame.h index c7aa04a3f3edb..61b37176a24ab 100644 --- a/content/public/renderer/render_frame.h +++ b/content/public/renderer/render_frame.h @@ -179,6 +179,9 @@ class CONTENT_EXPORT RenderFrame : virtual blink::AssociatedInterfaceProvider* GetRemoteAssociatedInterfaces() = 0; + // Returns true if this frame is a FTP directory listing. + virtual bool IsFTPDirectoryListing() = 0; + // Notifies the browser of text selection changes made. virtual void SetSelectedText(const std::u16string& selection_text, size_t offset, diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc index f362ee7c23241..d34f9fbe475b2 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc @@ -2500,6 +2500,10 @@ RenderFrameImpl::GetRemoteAssociatedInterfaces() { return remote_associated_interfaces_.get(); } +bool RenderFrameImpl::IsFTPDirectoryListing() { + return frame_->GetDocumentLoader()->IsListingFtpDirectory(); +} + void RenderFrameImpl::SetSelectedText(const std::u16string& selection_text, size_t offset, const gfx::Range& range) { diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h index b32fbb5887cb2..626bbdd3b1e5d 100644 --- a/content/renderer/render_frame_impl.h +++ b/content/renderer/render_frame_impl.h @@ -400,6 +400,7 @@ class CONTENT_EXPORT RenderFrameImpl mojo::ScopedMessagePipeHandle interface_pipe) override; blink::AssociatedInterfaceRegistry* GetAssociatedInterfaceRegistry() override; blink::AssociatedInterfaceProvider* GetRemoteAssociatedInterfaces() override; + bool IsFTPDirectoryListing() override; void SetSelectedText(const std::u16string& selection_text, size_t offset, const gfx::Range& range) override; diff --git a/ios/chrome/browser/profile/model/off_the_record_profile_ios_io_data.mm b/ios/chrome/browser/profile/model/off_the_record_profile_ios_io_data.mm index 00516bebc6fa3..62b2eceb8e48a 100644 --- a/ios/chrome/browser/profile/model/off_the_record_profile_ios_io_data.mm +++ b/ios/chrome/browser/profile/model/off_the_record_profile_ios_io_data.mm @@ -26,6 +26,7 @@ #import "ios/web/public/thread/web_thread.h" #import "net/cookies/cookie_store.h" #import "net/disk_cache/disk_cache.h" +#import "net/ftp/ftp_network_layer.h" #import "net/http/http_cache.h" #import "net/http/http_network_session.h" #import "net/http/http_server_properties.h" index e229ec4c97bb6..dd33fcf48c550 100644 --- a/net/BUILD.gn +++ b/net/BUILD.gn @@ -95,6 +95,7 @@ buildflag_header("buildflags") { flags = [ "POSIX_BYPASS_MMAP=$posix_bypass_mmap", "DISABLE_FILE_SUPPORT=$disable_file_support", + "DISABLE_FTP_SUPPORT=$disable_ftp_support", "ENABLE_MDNS=$enable_mdns", "ENABLE_REPORTING=$enable_reporting", "ENABLE_WEBSOCKETS=$enable_websockets", @@ -1587,6 +1588,41 @@ component("net") { ] } + if (!disable_ftp_support) { + sources += [ + "ftp/ftp_auth_cache.cc", + "ftp/ftp_auth_cache.h", + "ftp/ftp_ctrl_response_buffer.cc", + "ftp/ftp_ctrl_response_buffer.h", + "ftp/ftp_directory_listing_parser.cc", + "ftp/ftp_directory_listing_parser.h", + "ftp/ftp_directory_listing_parser_ls.cc", + "ftp/ftp_directory_listing_parser_ls.h", + "ftp/ftp_directory_listing_parser_vms.cc", + "ftp/ftp_directory_listing_parser_vms.h", + "ftp/ftp_directory_listing_parser_windows.cc", + "ftp/ftp_directory_listing_parser_windows.h", + "ftp/ftp_network_layer.cc", + "ftp/ftp_network_layer.h", + "ftp/ftp_network_session.cc", + "ftp/ftp_network_session.h", + "ftp/ftp_network_transaction.cc", + "ftp/ftp_network_transaction.h", + "ftp/ftp_request_info.h", + "ftp/ftp_response_info.cc", + "ftp/ftp_response_info.h", + "ftp/ftp_server_type.h", + "ftp/ftp_transaction.h", + "ftp/ftp_transaction_factory.h", + "ftp/ftp_util.cc", + "ftp/ftp_util.h", + "url_request/ftp_protocol_handler.cc", + "url_request/ftp_protocol_handler.h", + "url_request/url_request_ftp_job.cc", + "url_request/url_request_ftp_job.h", + ] + } + if (enable_websockets) { sources += [ "server/http_connection.cc", @@ -3203,6 +3239,21 @@ target(_test_target_type, "net_unittests") { ] } + if (!disable_ftp_support) { + sources += [ + "ftp/ftp_auth_cache_unittest.cc", + "ftp/ftp_ctrl_response_buffer_unittest.cc", + "ftp/ftp_directory_listing_parser_ls_unittest.cc", + "ftp/ftp_directory_listing_parser_unittest.cc", + "ftp/ftp_directory_listing_parser_unittest.h", + "ftp/ftp_directory_listing_parser_vms_unittest.cc", + "ftp/ftp_directory_listing_parser_windows_unittest.cc", + "ftp/ftp_network_transaction_unittest.cc", + "ftp/ftp_util_unittest.cc", + "url_request/url_request_ftp_job_unittest.cc", + ] + } + if (enable_built_in_dns) { sources += [ "url_request/http_with_dns_over_https_unittest.cc" ] } @@ -3720,6 +3771,46 @@ fuzzer_test("net_spdy_headers_to_http_response_headers_fuzzer") { libfuzzer_options = [ "max_len = 512" ] } +if (!disable_ftp_support) { + fuzzer_test("net_ftp_ctrl_response_fuzzer") { + sources = [ "ftp/ftp_ctrl_response_fuzzer.cc" ] + deps = [ + ":net_fuzzer_test_support", + "//base", + "//net", + ] + } + + fuzzer_test("net_ftp_directory_listing_fuzzer") { + sources = [ "ftp/ftp_directory_listing_fuzzer.cc" ] + deps = [ + ":net_fuzzer_test_support", + "//base", + "//net", + ] + + # TODO(https://crbug.com/921297): Re-enable once source of timeout is + # understood (probably just needs to restrict maximum input size). + additional_configs = [ "//testing/libfuzzer:no_clusterfuzz" ] + } + + fuzzer_test("net_url_request_ftp_fuzzer") { + sources = [ "url_request/url_request_ftp_fuzzer.cc" ] + deps = [ + ":net_fuzzer_test_support", + ":test_support", + "//base", + "//net", + ] + dict = "data/fuzzer_dictionaries/net_url_request_ftp_fuzzer.dict" + seed_corpus = "data/fuzzer_data/net_url_request_ftp_fuzzer/" + + # TODO(https://crbug.com/962087): Re-enable once source of timeout is + # understood (probably just needs to restrict maximum input size). + additional_configs = [ "//testing/libfuzzer:no_clusterfuzz" ] + } +} + fuzzer_test("net_unescape_url_component_fuzzer") { sources = [ "base/unescape_url_component_fuzzer.cc" ] deps = [ diff --git a/net/DEPS b/net/DEPS index 82956220ad000..d016bd9e7d052 100644 --- a/net/DEPS +++ b/net/DEPS @@ -45,6 +45,14 @@ specific_include_rules = { "+base/i18n", ], + "ftp_util\.cc": [ + "+base/i18n", + "+third_party/icu", + ], + "ftp_directory_listing_parser\.cc": [ + "+base/i18n", + ], + "brotli_source_stream\.cc": [ "+third_party/brotli", ], diff --git a/net/base/auth.h b/net/base/auth.h index e80006ee9ac79..c5ddb3f652b4e 100644 --- a/net/base/auth.h +++ b/net/base/auth.h @@ -31,8 +31,8 @@ class NET_EXPORT AuthChallengeInfo { // The service issuing the challenge. url::SchemeHostPort challenger; - // The authentication scheme used, such as "basic" or "digest". The encoding - // is ASCII. + // The authentication scheme used, such as "basic" or "digest". If the + // |source| is FTP_SERVER, this is an empty string. The encoding is ASCII. std::string scheme; // The realm of the challenge. May be empty. The encoding is UTF-8. diff --git a/net/base/dir_header.html b/net/base/dir_header.html index a2421e94b4465..10539e9ee4a0a 100644 --- a/net/base/dir_header.html +++ b/net/base/dir_header.html @@ -81,6 +81,13 @@ function onHasParentDirectory() { link.href = root + ".."; } +function onListingParsingError() { + var box = document.getElementById("listingParsingErrorBox"); + box.innerHTML = box.innerHTML.replace("LOCATION", encodeURI(document.location) + + "?raw"); + box.style.display = "block"; +} + function sortTable(column) { var theader = document.getElementById("theader"); var oldOrder = theader.cells[column].dataset.order || '1'; @@ -191,6 +198,13 @@ window.addEventListener('DOMContentLoaded', onLoad); margin-bottom: 10px; padding-bottom: 10px; } + + #listingParsingErrorBox { + border: 1px solid black; + background: #fae691; + padding: 10px; + display: none; + } @@ -199,6 +213,8 @@ window.addEventListener('DOMContentLoaded', onLoad); +
$i18nRaw{listingParsingErrorBoxText}
+

$i18n{header}