ftp_directory_listing_response_delegate.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "content/child/ftp_directory_listing_response_delegate.h" 6 7#include <vector> 8 9#include "base/i18n/icu_encoding_detection.h" 10#include "base/i18n/icu_string_conversions.h" 11#include "base/logging.h" 12#include "base/strings/string_util.h" 13#include "base/strings/sys_string_conversions.h" 14#include "base/strings/utf_string_conversions.h" 15#include "base/time/time.h" 16#include "content/child/weburlresponse_extradata_impl.h" 17#include "net/base/escape.h" 18#include "net/base/net_errors.h" 19#include "net/base/net_util.h" 20#include "net/ftp/ftp_directory_listing_parser.h" 21#include "third_party/WebKit/public/platform/WebURL.h" 22#include "third_party/WebKit/public/platform/WebURLLoaderClient.h" 23 24using blink::WebURLLoader; 25using blink::WebURLLoaderClient; 26using blink::WebURLResponse; 27using net::FtpDirectoryListingEntry; 28 29namespace { 30 31base::string16 ConvertPathToUTF16(const std::string& path) { 32 // Per RFC 2640, FTP servers should use UTF-8 or its proper subset ASCII, 33 // but many old FTP servers use legacy encodings. Try UTF-8 first. 34 if (base::IsStringUTF8(path)) 35 return base::UTF8ToUTF16(path); 36 37 // Try detecting the encoding. The sample is rather small though, so it may 38 // fail. 39 std::string encoding; 40 if (base::DetectEncoding(path, &encoding) && !encoding.empty()) { 41 base::string16 path_utf16; 42 if (base::CodepageToUTF16(path, encoding.c_str(), 43 base::OnStringConversionError::SUBSTITUTE, 44 &path_utf16)) { 45 return path_utf16; 46 } 47 } 48 49 // Use system native encoding as the last resort. 50 return base::WideToUTF16(base::SysNativeMBToWide(path)); 51} 52 53} // namespace 54 55namespace content { 56 57FtpDirectoryListingResponseDelegate::FtpDirectoryListingResponseDelegate( 58 WebURLLoaderClient* client, 59 WebURLLoader* loader, 60 const WebURLResponse& response) 61 : client_(client), 62 loader_(loader) { 63 if (response.extraData()) { 64 // extraData can be NULL during tests. 65 WebURLResponseExtraDataImpl* extra_data = 66 static_cast<WebURLResponseExtraDataImpl*>(response.extraData()); 67 extra_data->set_is_ftp_directory_listing(true); 68 } 69 Init(response.url()); 70} 71 72void FtpDirectoryListingResponseDelegate::Cancel() { 73 client_ = NULL; 74 loader_ = NULL; 75} 76 77void FtpDirectoryListingResponseDelegate::OnReceivedData(const char* data, 78 int data_len) { 79 buffer_.append(data, data_len); 80} 81 82void FtpDirectoryListingResponseDelegate::OnCompletedRequest() { 83 std::vector<FtpDirectoryListingEntry> entries; 84 int rv = -1; 85#if !defined(DISABLE_FTP_SUPPORT) 86 rv = net::ParseFtpDirectoryListing(buffer_, base::Time::Now(), &entries); 87#endif 88 if (rv != net::OK) { 89 SendDataToClient("<script>onListingParsingError();</script>\n"); 90 return; 91 } 92 for (size_t i = 0; i < entries.size(); i++) { 93 const FtpDirectoryListingEntry& entry = entries[i]; 94 95 // Skip the current and parent directory entries in the listing. Our header 96 // always includes them. 97 if (EqualsASCII(entry.name, ".") || EqualsASCII(entry.name, "..")) 98 continue; 99 100 bool is_directory = (entry.type == FtpDirectoryListingEntry::DIRECTORY); 101 int64 size = entry.size; 102 if (entry.type != FtpDirectoryListingEntry::FILE) 103 size = 0; 104 SendDataToClient(net::GetDirectoryListingEntry( 105 entry.name, entry.raw_name, is_directory, size, entry.last_modified)); 106 } 107} 108 109void FtpDirectoryListingResponseDelegate::Init(const GURL& response_url) { 110 net::UnescapeRule::Type unescape_rules = net::UnescapeRule::SPACES | 111 net::UnescapeRule::URL_SPECIAL_CHARS; 112 std::string unescaped_path = net::UnescapeURLComponent(response_url.path(), 113 unescape_rules); 114 SendDataToClient(net::GetDirectoryListingHeader( 115 ConvertPathToUTF16(unescaped_path))); 116 117 // If this isn't top level directory (i.e. the path isn't "/",) 118 // add a link to the parent directory. 119 if (response_url.path().length() > 1) { 120 SendDataToClient(net::GetDirectoryListingEntry( 121 base::ASCIIToUTF16(".."), std::string(), false, 0, base::Time())); 122 } 123} 124 125void FtpDirectoryListingResponseDelegate::SendDataToClient( 126 const std::string& data) { 127 if (client_) 128 client_->didReceiveData(loader_, data.data(), data.length(), -1); 129} 130 131} // namespace content 132