diff options
author | phajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-09-22 21:05:47 +0000 |
---|---|---|
committer | phajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-09-22 21:05:47 +0000 |
commit | 64d50ed649d61c5b84b09369b091018f70547bf8 (patch) | |
tree | 32f25e3f4d6721d68f6ce553bc51172a218bb071 | |
parent | dbf5e6c9053bf8e4c5fb08a1ca3005636380b5bb (diff) | |
download | chromium_src-64d50ed649d61c5b84b09369b091018f70547bf8.zip chromium_src-64d50ed649d61c5b84b09369b091018f70547bf8.tar.gz chromium_src-64d50ed649d61c5b84b09369b091018f70547bf8.tar.bz2 |
Move FTP LIST parsing code to the renderer process.
TEST=none
BUG=none
Review URL: http://codereview.chromium.org/210027
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@26860 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/browser_main.cc | 44 | ||||
-rw-r--r-- | chrome/chrome.gyp | 4 | ||||
-rw-r--r-- | chrome/common/net/net_resource_provider.cc | 59 | ||||
-rw-r--r-- | chrome/common/net/net_resource_provider.h | 19 | ||||
-rw-r--r-- | chrome/renderer/renderer_main.cc | 5 | ||||
-rw-r--r-- | net/base/mime_util.cc | 1 | ||||
-rw-r--r-- | net/url_request/url_request_new_ftp_job.cc | 241 | ||||
-rw-r--r-- | net/url_request/url_request_new_ftp_job.h | 11 | ||||
-rw-r--r-- | webkit/glue/DEPS | 3 | ||||
-rw-r--r-- | webkit/glue/ftp_directory_listing_response_delegate.cc | 253 | ||||
-rw-r--r-- | webkit/glue/ftp_directory_listing_response_delegate.h | 62 | ||||
-rw-r--r-- | webkit/glue/weburlloader_impl.cc | 24 | ||||
-rw-r--r-- | webkit/webkit.gyp | 2 |
13 files changed, 441 insertions, 287 deletions
diff --git a/chrome/browser/browser_main.cc b/chrome/browser/browser_main.cc index 2b048a9..cf21a8a 100644 --- a/chrome/browser/browser_main.cc +++ b/chrome/browser/browser_main.cc @@ -51,6 +51,7 @@ #include "chrome/common/histogram_synchronizer.h" #include "chrome/common/jstemplate_builder.h" #include "chrome/common/main_function_params.h" +#include "chrome/common/net/net_resource_provider.h" #include "chrome/common/pref_names.h" #include "chrome/common/pref_service.h" #include "chrome/common/result_codes.h" @@ -58,7 +59,6 @@ #include "chrome/installer/util/master_preferences.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" -#include "grit/net_resources.h" #include "net/base/cookie_monster.h" #include "net/base/net_module.h" #include "net/http/http_network_session.h" @@ -142,44 +142,6 @@ void HandleErrorTestParameters(const CommandLine& command_line) { } } -// The net module doesn't have access to this HTML or the strings that need to -// be localized. The Chrome locale will never change while we're running, so -// it's safe to have a static string that we always return a pointer into. -// This allows us to have the ResourceProvider return a pointer into the actual -// resource (via a StringPiece), instead of always copying resources. -struct LazyDirectoryListerCacher { - LazyDirectoryListerCacher() { - DictionaryValue value; - value.SetString(L"header", - l10n_util::GetString(IDS_DIRECTORY_LISTING_HEADER)); - value.SetString(L"parentDirText", - l10n_util::GetString(IDS_DIRECTORY_LISTING_PARENT)); - value.SetString(L"headerName", - l10n_util::GetString(IDS_DIRECTORY_LISTING_NAME)); - value.SetString(L"headerSize", - l10n_util::GetString(IDS_DIRECTORY_LISTING_SIZE)); - value.SetString(L"headerDateModified", - l10n_util::GetString(IDS_DIRECTORY_LISTING_DATE_MODIFIED)); - html_data = jstemplate_builder::GetI18nTemplateHtml( - ResourceBundle::GetSharedInstance().GetRawDataResource( - IDR_DIR_HEADER_HTML), - &value); - } - - std::string html_data; -}; - -base::LazyInstance<LazyDirectoryListerCacher> lazy_dir_lister( - base::LINKER_INITIALIZED); - -// This is called indirectly by the network layer to access resources. -base::StringPiece NetResourceProvider(int key) { - if (IDR_DIR_HEADER_HTML == key) - return base::StringPiece(lazy_dir_lister.Pointer()->html_data); - - return ResourceBundle::GetSharedInstance().GetRawDataResource(key); -} - void RunUIMessageLoop(BrowserProcess* browser_process) { #if defined(TOOLKIT_VIEWS) views::AcceleratorHandler accelerator_handler; @@ -703,8 +665,8 @@ int BrowserMain(const MainFunctionParams& parameters) { RLZTracker::InitRlzDelayed(base::DIR_MODULE, is_first_run, rlz_ping_delay); #endif - // Config the network module so it has access to resources. - net::NetModule::SetResourceProvider(NetResourceProvider); + // Configure the network module so it has access to resources. + net::NetModule::SetResourceProvider(chrome_common_net::NetResourceProvider); // Register our global network handler for chrome:// and // chrome-extension:// URLs. diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index b5491cf..6927110 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -405,6 +405,7 @@ '../build/temp_gyp/googleurl.gyp:googleurl', '../ipc/ipc.gyp:ipc', '../net/net.gyp:net', + '../net/net.gyp:net_resources', '../skia/skia.gyp:skia', '../third_party/icu/icu.gyp:icui18n', '../third_party/icu/icu.gyp:icuuc', @@ -438,6 +439,8 @@ 'common/extensions/user_script.h', 'common/gfx/utils.h', 'common/net/dns.h', + 'common/net/net_resource_provider.cc', + 'common/net/net_resource_provider.h', 'common/net/url_request_intercept_job.cc', 'common/net/url_request_intercept_job.h', 'common/web_resource/web_resource_unpacker.cc', @@ -672,7 +675,6 @@ '../app/app.gyp:app_resources', '../app/app.gyp:app_strings', '../media/media.gyp:media', - '../net/net.gyp:net_resources', '../printing/printing.gyp:printing', '../skia/skia.gyp:skia', '../third_party/bzip2/bzip2.gyp:bzip2', diff --git a/chrome/common/net/net_resource_provider.cc b/chrome/common/net/net_resource_provider.cc new file mode 100644 index 0000000..ef86ac3 --- /dev/null +++ b/chrome/common/net/net_resource_provider.cc @@ -0,0 +1,59 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/net/net_resource_provider.h" + +#include <string> + +#include "app/l10n_util.h" +#include "app/resource_bundle.h" +#include "base/string_piece.h" +#include "base/values.h" +#include "chrome/common/jstemplate_builder.h" +#include "grit/generated_resources.h" +#include "grit/net_resources.h" + +namespace { + +// The net module doesn't have access to this HTML or the strings that need to +// be localized. The Chrome locale will never change while we're running, so +// it's safe to have a static string that we always return a pointer into. +// This allows us to have the ResourceProvider return a pointer into the actual +// resource (via a StringPiece), instead of always copying resources. +struct LazyDirectoryListerCacher { + LazyDirectoryListerCacher() { + DictionaryValue value; + value.SetString(L"header", + l10n_util::GetString(IDS_DIRECTORY_LISTING_HEADER)); + value.SetString(L"parentDirText", + l10n_util::GetString(IDS_DIRECTORY_LISTING_PARENT)); + value.SetString(L"headerName", + l10n_util::GetString(IDS_DIRECTORY_LISTING_NAME)); + value.SetString(L"headerSize", + l10n_util::GetString(IDS_DIRECTORY_LISTING_SIZE)); + value.SetString(L"headerDateModified", + l10n_util::GetString(IDS_DIRECTORY_LISTING_DATE_MODIFIED)); + html_data = jstemplate_builder::GetI18nTemplateHtml( + ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_DIR_HEADER_HTML), + &value); + } + + std::string html_data; +}; + +} // namespace + +namespace chrome_common_net { + +base::StringPiece NetResourceProvider(int key) { + static LazyDirectoryListerCacher lazy_dir_lister; + + if (IDR_DIR_HEADER_HTML == key) + return base::StringPiece(lazy_dir_lister.html_data); + + return ResourceBundle::GetSharedInstance().GetRawDataResource(key); +} + +} // namespace chrome_common_net diff --git a/chrome/common/net/net_resource_provider.h b/chrome/common/net/net_resource_provider.h new file mode 100644 index 0000000..4965adf --- /dev/null +++ b/chrome/common/net/net_resource_provider.h @@ -0,0 +1,19 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_NET_NET_RESOURCE_PROVIDER_H_ +#define CHROME_COMMON_NET_NET_RESOURCE_PROVIDER_H_ + +namespace base { +class StringPiece; +} + +namespace chrome_common_net { + +// This is called indirectly by the network layer to access resources. +base::StringPiece NetResourceProvider(int key); + +} // namespace chrome_common_net + +#endif // CHROME_COMMON_NET_NET_RESOURCE_PROVIDER_H_ diff --git a/chrome/renderer/renderer_main.cc b/chrome/renderer/renderer_main.cc index 48060c4..6c7f01b 100644 --- a/chrome/renderer/renderer_main.cc +++ b/chrome/renderer/renderer_main.cc @@ -20,11 +20,13 @@ #include "chrome/common/chrome_switches.h" #include "chrome/common/logging_chrome.h" #include "chrome/common/main_function_params.h" +#include "chrome/common/net/net_resource_provider.h" #include "chrome/renderer/renderer_main_platform_delegate.h" #include "chrome/renderer/render_process.h" #include "chrome/renderer/render_thread.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" +#include "net/base/net_module.h" #if defined(OS_LINUX) #include "chrome/app/breakpad_linux.h" @@ -86,6 +88,9 @@ int RendererMain(const MainFunctionParams& parameters) { InitCrashReporter(); #endif + // Configure the network module so it has access to resources. + net::NetModule::SetResourceProvider(chrome_common_net::NetResourceProvider); + // This function allows pausing execution using the --renderer-startup-dialog // flag allowing us to attach a debugger. // Do not move this function down since that would mean we can't easily debug diff --git a/net/base/mime_util.cc b/net/base/mime_util.cc index 22e8eea..0fbb8c3 100644 --- a/net/base/mime_util.cc +++ b/net/base/mime_util.cc @@ -226,6 +226,7 @@ static const char* const supported_non_image_types[] = { // So, by including "text/css" into this list we choose Firefox // behavior - css files will be displayed: "text/css", + "text/vnd.chromium.ftp-dir", "text/", "image/svg+xml", // SVG is text-based XML, even though it has an image/ type "application/xml", diff --git a/net/url_request/url_request_new_ftp_job.cc b/net/url_request/url_request_new_ftp_job.cc index 83d1a9c..c4c3782 100644 --- a/net/url_request/url_request_new_ftp_job.cc +++ b/net/url_request/url_request_new_ftp_job.cc @@ -5,65 +5,18 @@ #include "net/url_request/url_request_new_ftp_job.h" #include "base/compiler_specific.h" -#include "base/file_version_info.h" #include "base/message_loop.h" -#include "base/sys_string_conversions.h" #include "net/base/auth.h" -#include "net/base/escape.h" #include "net/base/net_errors.h" #include "net/base/net_util.h" #include "net/ftp/ftp_response_info.h" -#include "net/ftp/ftp_server_type_histograms.h" #include "net/ftp/ftp_transaction_factory.h" -#include "net/third_party/parseftp/ParseFTPList.h" #include "net/url_request/url_request.h" #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_error_job.h" -#include "unicode/ucsdet.h" - -namespace { - -// A very simple-minded character encoding detection. -// TODO(jungshik): We can apply more heuristics here (e.g. using various hints -// like TLD, the UI language/default encoding of a client, etc). In that case, -// this should be pulled out of here and moved somewhere in base because there -// can be other use cases. -std::string DetectEncoding(const char* input, size_t len) { - if (IsStringASCII(std::string(input, len))) - return std::string(); - UErrorCode status = U_ZERO_ERROR; - UCharsetDetector* detector = ucsdet_open(&status); - ucsdet_setText(detector, input, static_cast<int32_t>(len), &status); - const UCharsetMatch* match = ucsdet_detect(detector, &status); - const char* encoding = ucsdet_getName(match, &status); - // Should we check the quality of the match? A rather arbitrary number is - // assigned by ICU and it's hard to come up with a lower limit. - if (U_FAILURE(status)) - return std::string(); - return encoding; -} - -string16 RawByteSequenceToFilename(const char* raw_filename, - const std::string& encoding) { - if (encoding.empty()) - return ASCIIToUTF16(raw_filename); - - // Try the detected encoding before falling back to the native codepage. - // Using the native codepage does not make much sense, but we don't have - // much else to resort to. - string16 filename; - if (!CodepageToUTF16(raw_filename, encoding.c_str(), - OnStringUtilConversionError::SUBSTITUTE, &filename)) - filename = WideToUTF16Hack(base::SysNativeMBToWide(raw_filename)); - return filename; -} - -} // namespace URLRequestNewFtpJob::URLRequestNewFtpJob(URLRequest* request) : URLRequestJob(request), - response_info_(NULL), - dir_listing_buf_size_(0), ALLOW_THIS_IN_INITIALIZER_LIST( start_callback_(this, &URLRequestNewFtpJob::OnStartCompleted)), ALLOW_THIS_IN_INITIALIZER_LIST( @@ -90,6 +43,14 @@ URLRequestJob* URLRequestNewFtpJob::Factory(URLRequest* request, return new URLRequestNewFtpJob(request); } +bool URLRequestNewFtpJob::GetMimeType(std::string* mime_type) const { + if (transaction_->GetResponseInfo()->is_directory_listing) { + *mime_type = "text/vnd.chromium.ftp-dir"; + return true; + } + return false; +} + void URLRequestNewFtpJob::Start() { DCHECK(!transaction_.get()); request_info_.url = request_->url(); @@ -159,65 +120,13 @@ bool URLRequestNewFtpJob::ReadRawData(net::IOBuffer* buf, DCHECK_NE(buf_size, 0); DCHECK(bytes_read); DCHECK(!read_in_progress_); - if (response_info_ == NULL) { - response_info_ = transaction_->GetResponseInfo(); - if (response_info_->is_directory_listing) { - std::string escaped_path = - UnescapeURLComponent(request_->url().path(), - UnescapeRule::SPACES | UnescapeRule::URL_SPECIAL_CHARS); - string16 path_utf16; - // Per RFC 2640, FTP servers should use UTF-8 or its proper subset ASCII, - // but many old FTP servers use legacy encodings. Try UTF-8 first and - // detect the encoding. - if (IsStringUTF8(escaped_path)) { - path_utf16 = UTF8ToUTF16(escaped_path); - } else { - std::string encoding = DetectEncoding(escaped_path.c_str(), - escaped_path.size()); - // Try the detected encoding. If it fails, resort to the - // OS native encoding. - if (encoding.empty() || - !CodepageToUTF16(escaped_path, encoding.c_str(), - OnStringUtilConversionError::SUBSTITUTE, - &path_utf16)) - path_utf16 = WideToUTF16Hack(base::SysNativeMBToWide(escaped_path)); - } - - directory_html_ = net::GetDirectoryListingHeader(path_utf16); - // If this isn't top level directory (i.e. the path isn't "/",) - // add a link to the parent directory. - if (request_->url().path().length() > 1) - directory_html_.append( - net::GetDirectoryListingEntry(ASCIIToUTF16(".."), - std::string(), - false, 0, - base::Time())); - } - } - if (!directory_html_.empty()) { - size_t bytes_to_copy = std::min(static_cast<size_t>(buf_size), - directory_html_.size()); - memcpy(buf->data(), directory_html_.c_str(), bytes_to_copy); - *bytes_read = bytes_to_copy; - directory_html_.erase(0, bytes_to_copy); - return true; - } int rv = transaction_->Read(buf, buf_size, &read_callback_); if (rv >= 0) { - if (response_info_->is_directory_listing) { - *bytes_read = ProcessFtpDir(buf, buf_size, rv); - } else { - *bytes_read = rv; - } + *bytes_read = rv; return true; } - if (response_info_->is_directory_listing) { - dir_listing_buf_ = buf; - dir_listing_buf_size_ = buf_size; - } - if (rv == net::ERR_IO_PENDING) { read_in_progress_ = true; SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); @@ -227,130 +136,6 @@ bool URLRequestNewFtpJob::ReadRawData(net::IOBuffer* buf, return false; } -int URLRequestNewFtpJob::ProcessFtpDir(net::IOBuffer *buf, - int buf_size, - int bytes_read) { - std::string file_entry; - std::string line; - - // If all we've seen so far is ASCII, encoding_ is empty. Try to detect the - // encoding. We don't do the separate UTF-8 check here because the encoding - // detection with a longer chunk (as opposed to the relatively short path - // component of the url) is unlikely to mistake UTF-8 for a legacy encoding. - // If it turns out to be wrong, a separate UTF-8 check has to be added. - // - // TODO(jungshik): UTF-8 has to be 'enforced' without any heuristics when - // we're talking to an FTP server compliant to RFC 2640 (that is, its response - // to FEAT command includes 'UTF8'). - // See http://wiki.filezilla-project.org/Character_Set - if (encoding_.empty()) - encoding_ = DetectEncoding(buf->data(), bytes_read); - - int64 file_size; - std::istringstream iss(std::string(buf->data(), bytes_read)); - struct net::list_state state; - memset(&state, 0, sizeof(state)); - while (getline(iss, line)) { - struct net::list_result result; - std::replace(line.begin(), line.end(), '\r', '\0'); - int line_type = net::ParseFTPList(line.c_str(), &state, &result); - - // The original code assumed months are in range 0-11 (PRExplodedTime), - // but our Time class expects a 1-12 range. Adjust it here, because - // the third-party parsing code uses bit-shifting on the month, - // and it'd be too easy to break that logic. - result.fe_time.month++; - DCHECK_LE(1, result.fe_time.month); - DCHECK_GE(12, result.fe_time.month); - - switch (line_type) { - case 'd': // Directory entry. - file_entry.append(net::GetDirectoryListingEntry( - RawByteSequenceToFilename(result.fe_fname, encoding_), - result.fe_fname, true, 0, - base::Time::FromLocalExploded(result.fe_time))); - break; - case 'f': // File entry. - if (StringToInt64(result.fe_size, &file_size)) - file_entry.append(net::GetDirectoryListingEntry( - RawByteSequenceToFilename(result.fe_fname, encoding_), - result.fe_fname, false, file_size, - base::Time::FromLocalExploded(result.fe_time))); - break; - case 'l': { // Symlink entry. - std::string filename(result.fe_fname, result.fe_fnlen); - - // Parsers for styles 'U' and 'W' handle " -> " themselves. - if (state.lstyle != 'U' && state.lstyle != 'W') { - std::string::size_type offset = filename.find(" -> "); - if (offset != std::string::npos) - filename = filename.substr(0, offset); - } - - if (StringToInt64(result.fe_size, &file_size)) { - file_entry.append(net::GetDirectoryListingEntry( - RawByteSequenceToFilename(filename.c_str(), encoding_), - filename, false, file_size, - base::Time::FromLocalExploded(result.fe_time))); - } - } - break; - case '?': // Junk entry. - case '"': // Comment entry. - break; - default: - NOTREACHED(); - break; - } - } - - // We can't recognize server type based on empty directory listings. Only log - // server type when we have enough data to recognize one. - if (state.parsed_one) - LogFtpServerType(state.lstyle); - - directory_html_.append(file_entry); - size_t bytes_to_copy = std::min(static_cast<size_t>(buf_size), - directory_html_.length()); - if (bytes_to_copy) { - memcpy(buf->data(), directory_html_.c_str(), bytes_to_copy); - directory_html_.erase(0, bytes_to_copy); - } - return bytes_to_copy; -} - -void URLRequestNewFtpJob::LogFtpServerType(char server_type) { - switch (server_type) { - case 'E': - net::UpdateFtpServerTypeHistograms(net::SERVER_EPLF); - break; - case 'V': - net::UpdateFtpServerTypeHistograms(net::SERVER_VMS); - break; - case 'C': - net::UpdateFtpServerTypeHistograms(net::SERVER_CMS); - break; - case 'W': - net::UpdateFtpServerTypeHistograms(net::SERVER_DOS); - break; - case 'O': - net::UpdateFtpServerTypeHistograms(net::SERVER_OS2); - break; - case 'U': - net::UpdateFtpServerTypeHistograms(net::SERVER_LSL); - break; - case 'w': - net::UpdateFtpServerTypeHistograms(net::SERVER_W16); - break; - case 'D': - net::UpdateFtpServerTypeHistograms(net::SERVER_DLS); - break; - default: - net::UpdateFtpServerTypeHistograms(net::SERVER_UNKNOWN); - break; - } -} - void URLRequestNewFtpJob::OnStartCompleted(int result) { // If the request was destroyed, then there is no more work to do. if (!request_ || !request_->delegate()) @@ -396,11 +181,6 @@ void URLRequestNewFtpJob::OnReadCompleted(int result) { } else if (result < 0) { NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result)); } else { - // TODO(ibrar): find the best place to delete dir_listing_buf_ - // Filter for Directory listing. - if (response_info_->is_directory_listing) - result = ProcessFtpDir(dir_listing_buf_, dir_listing_buf_size_, result); - // Clear the IO_PENDING status SetStatus(URLRequestStatus()); } @@ -410,8 +190,6 @@ void URLRequestNewFtpJob::OnReadCompleted(int result) { void URLRequestNewFtpJob::RestartTransactionWithAuth() { DCHECK(server_auth_ && server_auth_->state == net::AUTH_STATE_HAVE_AUTH); - response_info_ = NULL; - // No matter what, we want to report our status as IO pending since we will // be notifying our consumer asynchronously via OnStartCompleted. SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); @@ -457,5 +235,4 @@ void URLRequestNewFtpJob::DestroyTransaction() { DCHECK(transaction_.get()); transaction_.reset(); - response_info_ = NULL; } diff --git a/net/url_request/url_request_new_ftp_job.h b/net/url_request/url_request_new_ftp_job.h index e29687f..95b0a3b 100644 --- a/net/url_request/url_request_new_ftp_job.h +++ b/net/url_request/url_request_new_ftp_job.h @@ -30,6 +30,9 @@ class URLRequestNewFtpJob : public URLRequestJob { static URLRequestJob* Factory(URLRequest* request, const std::string& scheme); + // URLRequestJob methods: + virtual bool GetMimeType(std::string* mime_type) const; + private: // URLRequestJob methods: virtual void Start(); @@ -54,23 +57,15 @@ class URLRequestNewFtpJob : public URLRequestJob { void RestartTransactionWithAuth(); - int ProcessFtpDir(net::IOBuffer *buf, int buf_size, int bytes_read); - void LogFtpServerType(char server_type); net::FtpRequestInfo request_info_; scoped_ptr<net::FtpTransaction> transaction_; - const net::FtpResponseInfo* response_info_; - - scoped_refptr<net::IOBuffer> dir_listing_buf_; - int dir_listing_buf_size_; net::CompletionCallbackImpl<URLRequestNewFtpJob> start_callback_; net::CompletionCallbackImpl<URLRequestNewFtpJob> read_callback_; - std::string directory_html_; bool read_in_progress_; - std::string encoding_; scoped_refptr<net::AuthData> server_auth_; diff --git a/webkit/glue/DEPS b/webkit/glue/DEPS index 1129c20..89a1131 100644 --- a/webkit/glue/DEPS +++ b/webkit/glue/DEPS @@ -1,5 +1,6 @@ include_rules = [ "+media", + "+net/third_party/parseftp", "+skia/ext", "+skia/include", "+webkit/tools/test_shell", # Needed for test shell tests. @@ -7,7 +8,7 @@ include_rules = [ # This is not actually a directory, but npruntime_util.cc includes a file # from WebKit starting with this path in JSCore mode. "+bindings/c", - + # FIXME - refactor code and remove these dependencies "+chrome/browser", "+chrome/common", diff --git a/webkit/glue/ftp_directory_listing_response_delegate.cc b/webkit/glue/ftp_directory_listing_response_delegate.cc new file mode 100644 index 0000000..a0ee89d --- /dev/null +++ b/webkit/glue/ftp_directory_listing_response_delegate.cc @@ -0,0 +1,253 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "webkit/glue/ftp_directory_listing_response_delegate.h" + +#include <vector> + +#include "base/logging.h" +#include "base/string_util.h" +#include "base/sys_string_conversions.h" +#include "base/time.h" +#include "net/base/escape.h" +#include "net/base/net_util.h" +#include "net/ftp/ftp_server_type_histograms.h" +#include "unicode/ucsdet.h" +#include "webkit/api/public/WebURL.h" +#include "webkit/api/public/WebURLLoaderClient.h" + +using WebKit::WebURLLoader; +using WebKit::WebURLLoaderClient; +using WebKit::WebURLResponse; + +namespace { + +// A very simple-minded character encoding detection. +// TODO(jungshik): We can apply more heuristics here (e.g. using various hints +// like TLD, the UI language/default encoding of a client, etc). In that case, +// this should be pulled out of here and moved somewhere in base because there +// can be other use cases. +std::string DetectEncoding(const std::string& text) { + if (IsStringASCII(text)) + return std::string(); + UErrorCode status = U_ZERO_ERROR; + UCharsetDetector* detector = ucsdet_open(&status); + ucsdet_setText(detector, text.data(), static_cast<int32_t>(text.length()), + &status); + const UCharsetMatch* match = ucsdet_detect(detector, &status); + const char* encoding = ucsdet_getName(match, &status); + // Should we check the quality of the match? A rather arbitrary number is + // assigned by ICU and it's hard to come up with a lower limit. + if (U_FAILURE(status)) + return std::string(); + return encoding; +} + +string16 RawByteSequenceToFilename(const char* raw_filename, + const std::string& encoding) { + if (encoding.empty()) + return ASCIIToUTF16(raw_filename); + + // Try the detected encoding before falling back to the native codepage. + // Using the native codepage does not make much sense, but we don't have + // much else to resort to. + string16 filename; + if (!CodepageToUTF16(raw_filename, encoding.c_str(), + OnStringUtilConversionError::SUBSTITUTE, &filename)) + filename = WideToUTF16Hack(base::SysNativeMBToWide(raw_filename)); + return filename; +} + +void ExtractFullLinesFromBuffer(std::string* buffer, + std::vector<std::string>* lines) { + int cut_pos = 0; + for (size_t i = 0; i < buffer->length(); i++) { + if (i >= 1 && (*buffer)[i - 1] == '\r' && (*buffer)[i] == '\n') { + lines->push_back(buffer->substr(cut_pos, i - cut_pos - 1)); + cut_pos = i + 1; + } + } + buffer->erase(0, cut_pos); +} + +void LogFtpServerType(char server_type) { + switch (server_type) { + case 'E': + net::UpdateFtpServerTypeHistograms(net::SERVER_EPLF); + break; + case 'V': + net::UpdateFtpServerTypeHistograms(net::SERVER_VMS); + break; + case 'C': + net::UpdateFtpServerTypeHistograms(net::SERVER_CMS); + break; + case 'W': + net::UpdateFtpServerTypeHistograms(net::SERVER_DOS); + break; + case 'O': + net::UpdateFtpServerTypeHistograms(net::SERVER_OS2); + break; + case 'U': + net::UpdateFtpServerTypeHistograms(net::SERVER_LSL); + break; + case 'w': + net::UpdateFtpServerTypeHistograms(net::SERVER_W16); + break; + case 'D': + net::UpdateFtpServerTypeHistograms(net::SERVER_DLS); + break; + default: + net::UpdateFtpServerTypeHistograms(net::SERVER_UNKNOWN); + break; + } +} + +} // namespace + +namespace webkit_glue { + +FtpDirectoryListingResponseDelegate::FtpDirectoryListingResponseDelegate( + WebURLLoaderClient* client, + WebURLLoader* loader, + const WebURLResponse& response) + : client_(client), + loader_(loader), + original_response_(response) { + Init(); +} + +void FtpDirectoryListingResponseDelegate::OnReceivedData(const char* data, + int data_len) { + input_buffer_.append(data, data_len); + + // If all we've seen so far is ASCII, encoding_ is empty. Try to detect the + // encoding. We don't do the separate UTF-8 check here because the encoding + // detection with a longer chunk (as opposed to the relatively short path + // component of the url) is unlikely to mistake UTF-8 for a legacy encoding. + // If it turns out to be wrong, a separate UTF-8 check has to be added. + // + // TODO(jungshik): UTF-8 has to be 'enforced' without any heuristics when + // we're talking to an FTP server compliant to RFC 2640 (that is, its response + // to FEAT command includes 'UTF8'). + // See http://wiki.filezilla-project.org/Character_Set + if (encoding_.empty()) + encoding_ = DetectEncoding(input_buffer_); + + std::vector<std::string> lines; + ExtractFullLinesFromBuffer(&input_buffer_, &lines); + + for (std::vector<std::string>::const_iterator line = lines.begin(); + line != lines.end(); ++line) { + struct net::list_result result; + int line_type = net::ParseFTPList(line->c_str(), &parse_state_, &result); + + // The original code assumed months are in range 0-11 (PRExplodedTime), + // but our Time class expects a 1-12 range. Adjust it here, because + // the third-party parsing code uses bit-shifting on the month, + // and it'd be too easy to break that logic. + result.fe_time.month++; + DCHECK_LE(1, result.fe_time.month); + DCHECK_GE(12, result.fe_time.month); + + int64 file_size; + switch (line_type) { + case 'd': // Directory entry. + response_buffer_.append(net::GetDirectoryListingEntry( + RawByteSequenceToFilename(result.fe_fname, encoding_), + result.fe_fname, true, 0, + base::Time::FromLocalExploded(result.fe_time))); + break; + case 'f': // File entry. + if (StringToInt64(result.fe_size, &file_size)) { + response_buffer_.append(net::GetDirectoryListingEntry( + RawByteSequenceToFilename(result.fe_fname, encoding_), + result.fe_fname, false, file_size, + base::Time::FromLocalExploded(result.fe_time))); + } + break; + case 'l': { // Symlink entry. + std::string filename(result.fe_fname, result.fe_fnlen); + + // Parsers for styles 'U' and 'W' handle " -> " themselves. + if (parse_state_.lstyle != 'U' && parse_state_.lstyle != 'W') { + std::string::size_type offset = filename.find(" -> "); + if (offset != std::string::npos) + filename = filename.substr(0, offset); + } + + if (StringToInt64(result.fe_size, &file_size)) { + response_buffer_.append(net::GetDirectoryListingEntry( + RawByteSequenceToFilename(filename.c_str(), encoding_), + filename, false, file_size, + base::Time::FromLocalExploded(result.fe_time))); + } + } + break; + case '?': // Junk entry. + case '"': // Comment entry. + break; + default: + NOTREACHED(); + break; + } + } + + SendResponseBufferToClient(); +} + +void FtpDirectoryListingResponseDelegate::OnCompletedRequest() { + SendResponseBufferToClient(); + + // Only log the server type if we got enough data to reliably detect it. + if (parse_state_.parsed_one) + LogFtpServerType(parse_state_.lstyle); +} + +void FtpDirectoryListingResponseDelegate::Init() { + memset(&parse_state_, 0, sizeof(parse_state_)); + + GURL response_url(original_response_.url()); + UnescapeRule::Type unescape_rules = UnescapeRule::SPACES | + UnescapeRule::URL_SPECIAL_CHARS; + std::string unescaped_path = UnescapeURLComponent(response_url.path(), + unescape_rules); + string16 path_utf16; + // Per RFC 2640, FTP servers should use UTF-8 or its proper subset ASCII, + // but many old FTP servers use legacy encodings. Try UTF-8 first and + // detect the encoding. + if (IsStringUTF8(unescaped_path)) { + path_utf16 = UTF8ToUTF16(unescaped_path); + } else { + std::string encoding = DetectEncoding(unescaped_path); + // Try the detected encoding. If it fails, resort to the + // OS native encoding. + if (encoding.empty() || + !CodepageToUTF16(unescaped_path, encoding.c_str(), + OnStringUtilConversionError::SUBSTITUTE, + &path_utf16)) + path_utf16 = WideToUTF16Hack(base::SysNativeMBToWide(unescaped_path)); + } + + response_buffer_ = net::GetDirectoryListingHeader(path_utf16); + + // If this isn't top level directory (i.e. the path isn't "/",) + // add a link to the parent directory. + if (response_url.path().length() > 1) { + response_buffer_.append( + net::GetDirectoryListingEntry(ASCIIToUTF16(".."), + std::string(), + false, 0, + base::Time())); + } +} + +void FtpDirectoryListingResponseDelegate::SendResponseBufferToClient() { + if (!response_buffer_.empty()) { + client_->didReceiveData(loader_, response_buffer_.data(), + response_buffer_.length(), -1); + response_buffer_.clear(); + } +} + +} // namespace webkit_glue diff --git a/webkit/glue/ftp_directory_listing_response_delegate.h b/webkit/glue/ftp_directory_listing_response_delegate.h new file mode 100644 index 0000000..1ca42d7 --- /dev/null +++ b/webkit/glue/ftp_directory_listing_response_delegate.h @@ -0,0 +1,62 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// A delegate class of WebURLLoaderImpl that handles text/vnd.chromium.ftp-dir +// data. + +#ifndef WEBKIT_GLUE_FTP_DIRECTORY_LISTING_RESPONSE_DELEGATE_H_ +#define WEBKIT_GLUE_FTP_DIRECTORY_LISTING_RESPONSE_DELEGATE_H_ + +#include <string> + +#include "net/third_party/parseftp/ParseFTPList.h" +#include "webkit/api/public/WebURLResponse.h" + +namespace WebKit { +class WebURLLoader; +class WebURLLoaderClient; +} + +namespace webkit_glue { + +class FtpDirectoryListingResponseDelegate { + public: + FtpDirectoryListingResponseDelegate(WebKit::WebURLLoaderClient* client, + WebKit::WebURLLoader* loader, + const WebKit::WebURLResponse& response); + + // Passed through from ResourceHandleInternal + void OnReceivedData(const char* data, int data_len); + void OnCompletedRequest(); + + private: + void Init(); + + void SendResponseBufferToClient(); + + // Pointers to the client and associated loader so we can make callbacks as + // we parse pieces of data. + WebKit::WebURLLoaderClient* client_; + WebKit::WebURLLoader* loader_; + + // The original resource response for this request. We use this as a + // starting point for each parts response. + WebKit::WebURLResponse original_response_; + + // State kept between parsing each line of the response. + struct net::list_state parse_state_; + + // Detected encoding of the response. + std::string encoding_; + + // Buffer to hold not-yet-parsed input. + std::string input_buffer_; + + // Buffer to hold response not-yet-sent to the caller. + std::string response_buffer_; +}; + +} // namespace webkit_glue + +#endif // WEBKIT_GLUE_FTP_DIRECTORY_LISTING_RESPONSE_DELEGATE_H_ diff --git a/webkit/glue/weburlloader_impl.cc b/webkit/glue/weburlloader_impl.cc index b6d82c9..8884bdc 100644 --- a/webkit/glue/weburlloader_impl.cc +++ b/webkit/glue/weburlloader_impl.cc @@ -21,6 +21,7 @@ #include "webkit/api/public/WebURLLoaderClient.h" #include "webkit/api/public/WebURLRequest.h" #include "webkit/api/public/WebURLResponse.h" +#include "webkit/glue/ftp_directory_listing_response_delegate.h" #include "webkit/glue/glue_util.h" #include "webkit/glue/multipart_response_delegate.h" #include "webkit/glue/resource_loader_bridge.h" @@ -225,6 +226,7 @@ class WebURLLoaderImpl::Context : public base::RefCounted<Context>, WebURLRequest request_; WebURLLoaderClient* client_; scoped_ptr<ResourceLoaderBridge> bridge_; + scoped_ptr<FtpDirectoryListingResponseDelegate> ftp_listing_delegate_; scoped_ptr<MultipartResponseDelegate> multipart_delegate_; int64 expected_content_length_; }; @@ -427,13 +429,17 @@ void WebURLLoaderImpl::Context::OnReceivedResponse( expected_content_length_ = response.expectedContentLength(); + if (info.mime_type == "text/vnd.chromium.ftp-dir") + response.setMIMEType(WebString::fromUTF8("text/html")); + client_->didReceiveResponse(loader_, response); - // we may have been cancelled after didReceiveResponse, which would leave us - // without a client and therefore without much need to do multipart handling. + // We may have been cancelled after didReceiveResponse, which would leave us + // without a client and therefore without much need to do further handling. if (!client_) return; + DCHECK(!ftp_listing_delegate_.get()); DCHECK(!multipart_delegate_.get()); if (info.headers && info.mime_type == "multipart/x-mixed-replace") { std::string content_type; @@ -448,6 +454,9 @@ void WebURLLoaderImpl::Context::OnReceivedResponse( multipart_delegate_.reset( new MultipartResponseDelegate(client_, loader_, response, boundary)); } + } else if (info.mime_type == "text/vnd.chromium.ftp-dir") { + ftp_listing_delegate_.reset( + new FtpDirectoryListingResponseDelegate(client_, loader_, response)); } } @@ -455,7 +464,11 @@ void WebURLLoaderImpl::Context::OnReceivedData(const char* data, int len) { if (!client_) return; - if (multipart_delegate_.get()) { + if (ftp_listing_delegate_.get()) { + // The FTP listing delegate will make the appropriate calls to + // client_->didReceiveData and client_->didReceiveResponse. + ftp_listing_delegate_->OnReceivedData(data, len); + } else if (multipart_delegate_.get()) { // The multipart delegate will make the appropriate calls to // client_->didReceiveData and client_->didReceiveResponse. multipart_delegate_->OnReceivedData(data, len); @@ -467,7 +480,10 @@ void WebURLLoaderImpl::Context::OnReceivedData(const char* data, int len) { void WebURLLoaderImpl::Context::OnCompletedRequest( const URLRequestStatus& status, const std::string& security_info) { - if (multipart_delegate_.get()) { + if (ftp_listing_delegate_.get()) { + ftp_listing_delegate_->OnCompletedRequest(); + ftp_listing_delegate_.reset(NULL); + } else if (multipart_delegate_.get()) { multipart_delegate_->OnCompletedRequest(); multipart_delegate_.reset(NULL); } diff --git a/webkit/webkit.gyp b/webkit/webkit.gyp index a0d73fe..8122d20 100644 --- a/webkit/webkit.gyp +++ b/webkit/webkit.gyp @@ -482,6 +482,8 @@ 'glue/feed_preview.cc', 'glue/feed_preview.h', 'glue/form_data.h', + 'glue/ftp_directory_listing_response_delegate.cc', + 'glue/ftp_directory_listing_response_delegate.h', 'glue/glue_accessibility_object.cc', 'glue/glue_accessibility_object.h', 'glue/glue_serialize.cc', |