summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorphajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-09-22 21:05:47 +0000
committerphajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-09-22 21:05:47 +0000
commit64d50ed649d61c5b84b09369b091018f70547bf8 (patch)
tree32f25e3f4d6721d68f6ce553bc51172a218bb071
parentdbf5e6c9053bf8e4c5fb08a1ca3005636380b5bb (diff)
downloadchromium_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.cc44
-rw-r--r--chrome/chrome.gyp4
-rw-r--r--chrome/common/net/net_resource_provider.cc59
-rw-r--r--chrome/common/net/net_resource_provider.h19
-rw-r--r--chrome/renderer/renderer_main.cc5
-rw-r--r--net/base/mime_util.cc1
-rw-r--r--net/url_request/url_request_new_ftp_job.cc241
-rw-r--r--net/url_request/url_request_new_ftp_job.h11
-rw-r--r--webkit/glue/DEPS3
-rw-r--r--webkit/glue/ftp_directory_listing_response_delegate.cc253
-rw-r--r--webkit/glue/ftp_directory_listing_response_delegate.h62
-rw-r--r--webkit/glue/weburlloader_impl.cc24
-rw-r--r--webkit/webkit.gyp2
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',