blob: 80737dbe1f6fc0dc41d0fad9e4ab68da69830afc [file]
// 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/i18n/icu_encoding_detection.h"
#include "base/i18n/icu_string_conversions.h"
#include "base/logging.h"
#include "base/string_util.h"
#include "base/sys_string_conversions.h"
#include "base/utf_string_conversions.h"
#include "base/time.h"
#include "net/base/escape.h"
#include "net/base/net_errors.h"
#include "net/base/net_util.h"
#include "net/ftp/ftp_directory_listing_parser.h"
#include "net/ftp/ftp_server_type_histograms.h"
#include "third_party/WebKit/WebKit/chromium/public/WebURL.h"
#include "third_party/WebKit/WebKit/chromium/public/WebURLLoaderClient.h"
using net::FtpDirectoryListingEntry;
using WebKit::WebURLLoader;
using WebKit::WebURLLoaderClient;
using WebKit::WebURLResponse;
namespace {
string16 ConvertPathToUTF16(const std::string& path) {
// 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.
if (IsStringUTF8(path))
return UTF8ToUTF16(path);
// Try detecting the encoding. The sample is rather small though, so it may
// fail.
std::string encoding;
if (base::DetectEncoding(path, &encoding) && !encoding.empty()) {
string16 path_utf16;
if (base::CodepageToUTF16(path, encoding.c_str(),
base::OnStringConversionError::SUBSTITUTE,
&path_utf16)) {
return path_utf16;
}
}
// Use system native encoding as the last resort.
return WideToUTF16Hack(base::SysNativeMBToWide(path));
}
} // namespace
namespace webkit_glue {
FtpDirectoryListingResponseDelegate::FtpDirectoryListingResponseDelegate(
WebURLLoaderClient* client,
WebURLLoader* loader,
const WebURLResponse& response)
: client_(client),
loader_(loader),
original_response_(response),
buffer_(base::Time::Now()),
updated_histograms_(false),
had_parsing_error_(false) {
Init();
}
void FtpDirectoryListingResponseDelegate::OnReceivedData(const char* data,
int data_len) {
if (had_parsing_error_)
return;
if (buffer_.ConsumeData(data, data_len) == net::OK)
ProcessReceivedEntries();
else
had_parsing_error_ = true;
}
void FtpDirectoryListingResponseDelegate::OnCompletedRequest() {
if (!had_parsing_error_ && buffer_.ProcessRemainingData() == net::OK)
ProcessReceivedEntries();
else
had_parsing_error_ = true;
if (had_parsing_error_)
SendDataToClient("<script>onListingParsingError();</script>\n");
}
void FtpDirectoryListingResponseDelegate::Init() {
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);
SendDataToClient(net::GetDirectoryListingHeader(
ConvertPathToUTF16(unescaped_path)));
// 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) {
SendDataToClient(net::GetDirectoryListingEntry(
ASCIIToUTF16(".."), std::string(), false, 0, base::Time()));
}
}
bool FtpDirectoryListingResponseDelegate::ConvertToServerEncoding(
const string16& filename, std::string* raw_bytes) const {
if (buffer_.encoding().empty()) {
*raw_bytes = std::string();
return true;
}
return base::UTF16ToCodepage(filename, buffer_.encoding().c_str(),
base::OnStringConversionError::FAIL,
raw_bytes);
}
void FtpDirectoryListingResponseDelegate::ProcessReceivedEntries() {
if (!updated_histograms_ && buffer_.EntryAvailable()) {
// Only log the server type if we got enough data to reliably detect it.
net::UpdateFtpServerTypeHistograms(buffer_.GetServerType());
updated_histograms_ = true;
}
while (buffer_.EntryAvailable()) {
FtpDirectoryListingEntry entry = buffer_.PopEntry();
// Skip the current and parent directory entries in the listing. Our header
// always includes them.
if (EqualsASCII(entry.name, ".") || EqualsASCII(entry.name, ".."))
continue;
bool is_directory = (entry.type == FtpDirectoryListingEntry::DIRECTORY);
int64 size = entry.size;
if (entry.type != FtpDirectoryListingEntry::FILE)
size = 0;
std::string raw_bytes;
if (ConvertToServerEncoding(entry.name, &raw_bytes)) {
SendDataToClient(net::GetDirectoryListingEntry(
entry.name, raw_bytes, is_directory, size, entry.last_modified));
} else {
// Consider an encoding problem a non-fatal error. The server's support
// for non-ASCII characters might be buggy. Display an error message,
// but keep trying to display the rest of the listing (most file names
// are ASCII anyway, we could be just unlucky with this one).
had_parsing_error_ = true;
}
}
}
void FtpDirectoryListingResponseDelegate::SendDataToClient(
const std::string& data) {
client_->didReceiveData(loader_, data.data(), data.length());
}
} // namespace webkit_glue