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