summaryrefslogtreecommitdiffstats
path: root/webkit
diff options
context:
space:
mode:
Diffstat (limited to 'webkit')
-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
5 files changed, 339 insertions, 5 deletions
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',