1a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "content/child/ftp_directory_listing_response_delegate.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <vector>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/i18n/icu_encoding_detection.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/i18n/icu_string_conversions.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
125e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)#include "base/strings/string_util.h"
13c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/strings/sys_string_conversions.h"
14868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
15eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/time/time.h"
165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "content/child/weburlresponse_extradata_impl.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/escape.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/net_errors.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/net_util.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/ftp/ftp_directory_listing_parser.h"
21868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "third_party/WebKit/public/platform/WebURL.h"
22868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "third_party/WebKit/public/platform/WebURLLoaderClient.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)using blink::WebURLLoader;
25f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)using blink::WebURLLoaderClient;
26f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)using blink::WebURLResponse;
27cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)using net::FtpDirectoryListingEntry;
28a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
31c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)base::string16 ConvertPathToUTF16(const std::string& path) {
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Per RFC 2640, FTP servers should use UTF-8 or its proper subset ASCII,
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // but many old FTP servers use legacy encodings. Try UTF-8 first.
34010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  if (base::IsStringUTF8(path))
355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return base::UTF8ToUTF16(path);
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Try detecting the encoding. The sample is rather small though, so it may
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // fail.
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string encoding;
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (base::DetectEncoding(path, &encoding) && !encoding.empty()) {
41c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    base::string16 path_utf16;
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (base::CodepageToUTF16(path, encoding.c_str(),
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              base::OnStringConversionError::SUBSTITUTE,
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              &path_utf16)) {
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return path_utf16;
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Use system native encoding as the last resort.
505c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  return base::WideToUTF16(base::SysNativeMBToWide(path));
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)namespace content {
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)FtpDirectoryListingResponseDelegate::FtpDirectoryListingResponseDelegate(
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    WebURLLoaderClient* client,
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    WebURLLoader* loader,
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const WebURLResponse& response)
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : client_(client),
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      loader_(loader) {
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (response.extraData()) {
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // extraData can be NULL during tests.
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    WebURLResponseExtraDataImpl* extra_data =
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        static_cast<WebURLResponseExtraDataImpl*>(response.extraData());
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    extra_data->set_is_ftp_directory_listing(true);
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Init(response.url());
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
72cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void FtpDirectoryListingResponseDelegate::Cancel() {
73cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  client_ = NULL;
74cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  loader_ = NULL;
75cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
76cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void FtpDirectoryListingResponseDelegate::OnReceivedData(const char* data,
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                         int data_len) {
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  buffer_.append(data, data_len);
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void FtpDirectoryListingResponseDelegate::OnCompletedRequest() {
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<FtpDirectoryListingEntry> entries;
84116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  int rv = -1;
85116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#if !defined(DISABLE_FTP_SUPPORT)
86116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  rv = net::ParseFtpDirectoryListing(buffer_, base::Time::Now(), &entries);
87116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#endif
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (rv != net::OK) {
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SendDataToClient("<script>onListingParsingError();</script>\n");
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < entries.size(); i++) {
935c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    const FtpDirectoryListingEntry& entry = entries[i];
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Skip the current and parent directory entries in the listing. Our header
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // always includes them.
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (EqualsASCII(entry.name, ".") || EqualsASCII(entry.name, ".."))
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bool is_directory = (entry.type == FtpDirectoryListingEntry::DIRECTORY);
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int64 size = entry.size;
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (entry.type != FtpDirectoryListingEntry::FILE)
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      size = 0;
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SendDataToClient(net::GetDirectoryListingEntry(
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        entry.name, entry.raw_name, is_directory, size, entry.last_modified));
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void FtpDirectoryListingResponseDelegate::Init(const GURL& response_url) {
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  net::UnescapeRule::Type unescape_rules = net::UnescapeRule::SPACES |
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                           net::UnescapeRule::URL_SPECIAL_CHARS;
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string unescaped_path = net::UnescapeURLComponent(response_url.path(),
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                         unescape_rules);
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SendDataToClient(net::GetDirectoryListingHeader(
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ConvertPathToUTF16(unescaped_path)));
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If this isn't top level directory (i.e. the path isn't "/",)
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // add a link to the parent directory.
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (response_url.path().length() > 1) {
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SendDataToClient(net::GetDirectoryListingEntry(
1215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        base::ASCIIToUTF16(".."), std::string(), false, 0, base::Time()));
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void FtpDirectoryListingResponseDelegate::SendDataToClient(
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& data) {
127cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (client_)
128cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    client_->didReceiveData(loader_, data.data(), data.length(), -1);
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
131a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}  // namespace content
132