summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorphajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-03-26 11:08:50 +0000
committerphajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-03-26 11:08:50 +0000
commit621241a1de4dcafe244ea5aaaf513ade7e05a131 (patch)
treef675de3e2670f381eaf63071c0b7ee46285bfcde /net
parentbd57b686619dd598890cefffb82f9b06e9a8066c (diff)
downloadchromium_src-621241a1de4dcafe244ea5aaaf513ade7e05a131.zip
chromium_src-621241a1de4dcafe244ea5aaaf513ade7e05a131.tar.gz
chromium_src-621241a1de4dcafe244ea5aaaf513ade7e05a131.tar.bz2
FTP: Detect the character encoding only after the entire listing is received.
Otherwise the parser could be confused extremely easily. Imagine a listing which has only ASCII characters at the beginning, but after we detect it as ASCII-encoded, some UTF-8 bytes appear. It can be made more complex though, for example the beginning might look like UTF-8, but the entire listing may use a different encoding incompatible with UTF-8 (that was the case here, see bug). This change also simplifies the directory listing parser. When we always have the entire listing when parsing, some complex code can be removed. BUG=76171 TEST=see bug Review URL: http://codereview.chromium.org/6670085 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@79490 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r--net/ftp/ftp_directory_listing_buffer.cc194
-rw-r--r--net/ftp/ftp_directory_listing_buffer.h101
-rw-r--r--net/ftp/ftp_directory_listing_parser.cc101
-rw-r--r--net/ftp/ftp_directory_listing_parser.h34
-rw-r--r--net/ftp/ftp_directory_listing_parser_ls.cc227
-rw-r--r--net/ftp/ftp_directory_listing_parser_ls.h45
-rw-r--r--net/ftp/ftp_directory_listing_parser_ls_unittest.cc76
-rw-r--r--net/ftp/ftp_directory_listing_parser_netware.cc121
-rw-r--r--net/ftp/ftp_directory_listing_parser_netware.h45
-rw-r--r--net/ftp/ftp_directory_listing_parser_netware_unittest.cc42
-rw-r--r--net/ftp/ftp_directory_listing_parser_unittest.cc (renamed from net/ftp/ftp_directory_listing_buffer_unittest.cc)30
-rw-r--r--net/ftp/ftp_directory_listing_parser_unittest.h26
-rw-r--r--net/ftp/ftp_directory_listing_parser_vms.cc220
-rw-r--r--net/ftp/ftp_directory_listing_parser_vms.h58
-rw-r--r--net/ftp/ftp_directory_listing_parser_vms_unittest.cc159
-rw-r--r--net/ftp/ftp_directory_listing_parser_windows.cc101
-rw-r--r--net/ftp/ftp_directory_listing_parser_windows.h27
-rw-r--r--net/ftp/ftp_directory_listing_parser_windows_unittest.cc59
-rw-r--r--net/net.gyp4
19 files changed, 691 insertions, 979 deletions
diff --git a/net/ftp/ftp_directory_listing_buffer.cc b/net/ftp/ftp_directory_listing_buffer.cc
deleted file mode 100644
index 58533b8..0000000
--- a/net/ftp/ftp_directory_listing_buffer.cc
+++ /dev/null
@@ -1,194 +0,0 @@
-// 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 "net/ftp/ftp_directory_listing_buffer.h"
-
-#include "base/i18n/icu_encoding_detection.h"
-#include "base/i18n/icu_string_conversions.h"
-#include "base/stl_util-inl.h"
-#include "base/string_util.h"
-#include "net/base/net_errors.h"
-#include "net/ftp/ftp_directory_listing_parser_ls.h"
-#include "net/ftp/ftp_directory_listing_parser_netware.h"
-#include "net/ftp/ftp_directory_listing_parser_vms.h"
-#include "net/ftp/ftp_directory_listing_parser_windows.h"
-
-namespace net {
-
-FtpDirectoryListingBuffer::FtpDirectoryListingBuffer(
- const base::Time& current_time)
- : current_parser_(NULL) {
- parsers_.insert(new FtpDirectoryListingParserLs(current_time));
- parsers_.insert(new FtpDirectoryListingParserNetware(current_time));
- parsers_.insert(new FtpDirectoryListingParserVms());
- parsers_.insert(new FtpDirectoryListingParserWindows());
-}
-
-FtpDirectoryListingBuffer::~FtpDirectoryListingBuffer() {
- STLDeleteElements(&parsers_);
-}
-
-int FtpDirectoryListingBuffer::ConsumeData(const char* data, int data_length) {
- buffer_.append(data, data_length);
-
- if (!encoding_.empty() || buffer_.length() > 1024) {
- int rv = ConsumeBuffer();
- if (rv != OK)
- return rv;
- }
-
- return ParseLines();
-}
-
-int FtpDirectoryListingBuffer::ProcessRemainingData() {
- int rv = ConsumeBuffer();
- if (rv != OK)
- return rv;
-
- DCHECK(buffer_.empty());
- if (!converted_buffer_.empty())
- return ERR_INVALID_RESPONSE;
-
- rv = ParseLines();
- if (rv != OK)
- return rv;
-
- rv = OnEndOfInput();
- if (rv != OK)
- return rv;
-
- return OK;
-}
-
-bool FtpDirectoryListingBuffer::EntryAvailable() const {
- return (current_parser_ ? current_parser_->EntryAvailable() : false);
-}
-
-FtpDirectoryListingEntry FtpDirectoryListingBuffer::PopEntry() {
- DCHECK(EntryAvailable());
- return current_parser_->PopEntry();
-}
-
-FtpServerType FtpDirectoryListingBuffer::GetServerType() const {
- return (current_parser_ ? current_parser_->GetServerType() : SERVER_UNKNOWN);
-}
-
-int FtpDirectoryListingBuffer::DecodeBufferUsingEncoding(
- const std::string& encoding) {
- string16 converted;
- if (!base::CodepageToUTF16(buffer_,
- encoding.c_str(),
- base::OnStringConversionError::FAIL,
- &converted))
- return ERR_ENCODING_CONVERSION_FAILED;
-
- buffer_.clear();
- converted_buffer_ += converted;
- return OK;
-}
-
-int FtpDirectoryListingBuffer::ConvertBufferToUTF16() {
- if (encoding_.empty()) {
- std::vector<std::string> encodings;
- if (!base::DetectAllEncodings(buffer_, &encodings))
- return ERR_ENCODING_DETECTION_FAILED;
-
- // Use first encoding that can be used to decode the buffer.
- for (size_t i = 0; i < encodings.size(); i++) {
- if (DecodeBufferUsingEncoding(encodings[i]) == OK) {
- encoding_ = encodings[i];
- return OK;
- }
- }
-
- return ERR_ENCODING_DETECTION_FAILED;
- }
-
- return DecodeBufferUsingEncoding(encoding_);
-}
-
-void FtpDirectoryListingBuffer::ExtractFullLinesFromBuffer() {
- int cut_pos = 0;
- // TODO(phajdan.jr): This code accepts all endlines matching \r*\n. Should it
- // be more strict, or enforce consistent line endings?
- for (size_t i = 0; i < converted_buffer_.length(); ++i) {
- if (converted_buffer_[i] != '\n')
- continue;
- int line_length = i - cut_pos;
- if (i >= 1 && converted_buffer_[i - 1] == '\r')
- line_length--;
- lines_.push_back(converted_buffer_.substr(cut_pos, line_length));
- cut_pos = i + 1;
- }
- converted_buffer_.erase(0, cut_pos);
-}
-
-int FtpDirectoryListingBuffer::ConsumeBuffer() {
- int rv = ConvertBufferToUTF16();
- if (rv != OK)
- return rv;
-
- ExtractFullLinesFromBuffer();
- return OK;
-}
-
-int FtpDirectoryListingBuffer::ParseLines() {
- while (!lines_.empty()) {
- string16 line = lines_.front();
- lines_.pop_front();
- if (current_parser_) {
- if (!current_parser_->ConsumeLine(line))
- return ERR_FAILED;
- } else {
- ParserSet::iterator i = parsers_.begin();
- while (i != parsers_.end()) {
- if ((*i)->ConsumeLine(line)) {
- i++;
- } else {
- delete *i;
- parsers_.erase(i++);
- }
- }
- if (parsers_.empty())
- return ERR_UNRECOGNIZED_FTP_DIRECTORY_LISTING_FORMAT;
- if (parsers_.size() == 1)
- current_parser_ = *parsers_.begin();
- }
- }
-
- return OK;
-}
-
-int FtpDirectoryListingBuffer::OnEndOfInput() {
- ParserSet::iterator i = parsers_.begin();
- while (i != parsers_.end()) {
- if ((*i)->OnEndOfInput()) {
- i++;
- } else {
- delete *i;
- parsers_.erase(i++);
- }
- }
-
- if (parsers_.size() != 1) {
- current_parser_ = NULL;
-
- // We may hit an ambiguity in case of listings which have no entries. That's
- // fine, as long as all remaining parsers agree that the listing is empty.
- bool all_listings_empty = true;
- for (ParserSet::iterator i = parsers_.begin(); i != parsers_.end(); ++i) {
- if ((*i)->EntryAvailable())
- all_listings_empty = false;
- }
- if (all_listings_empty)
- return OK;
-
- return ERR_UNRECOGNIZED_FTP_DIRECTORY_LISTING_FORMAT;
- }
-
- current_parser_ = *parsers_.begin();
- return OK;
-}
-
-} // namespace net
diff --git a/net/ftp/ftp_directory_listing_buffer.h b/net/ftp/ftp_directory_listing_buffer.h
deleted file mode 100644
index ea68932..0000000
--- a/net/ftp/ftp_directory_listing_buffer.h
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright (c) 2010 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.
-
-#ifndef NET_FTP_FTP_DIRECTORY_LISTING_BUFFER_H_
-#define NET_FTP_FTP_DIRECTORY_LISTING_BUFFER_H_
-#pragma once
-
-#include <deque>
-#include <set>
-#include <string>
-
-#include "base/basictypes.h"
-#include "base/string16.h"
-#include "base/time.h"
-#include "net/ftp/ftp_server_type_histograms.h"
-
-namespace net {
-
-struct FtpDirectoryListingEntry;
-class FtpDirectoryListingParser;
-
-class FtpDirectoryListingBuffer {
- public:
- // Constructor. When the current time is needed to guess the year on partial
- // date strings, |current_time| will be used. This allows passing a specific
- // date during testing.
- explicit FtpDirectoryListingBuffer(const base::Time& current_time);
- ~FtpDirectoryListingBuffer();
-
- // Called when data is received from the data socket. Returns network
- // error code.
- int ConsumeData(const char* data, int data_length);
-
- // Called when all received data has been consumed by this buffer. Tells the
- // buffer to try to parse remaining raw data and returns network error code.
- int ProcessRemainingData();
-
- bool EntryAvailable() const;
-
- // Returns the next entry. It is an error to call this function
- // unless EntryAvailable returns true.
- FtpDirectoryListingEntry PopEntry();
-
- // Returns recognized server type. It is valid to call this function at any
- // time, although it will return SERVER_UNKNOWN if it doesn't know the answer.
- FtpServerType GetServerType() const;
-
- const std::string& encoding() const { return encoding_; }
-
- private:
- typedef std::set<FtpDirectoryListingParser*> ParserSet;
-
- // Decodes the raw buffer using specified |encoding|. On success
- // clears the raw buffer and appends data to |converted_buffer_|.
- // Returns network error code.
- int DecodeBufferUsingEncoding(const std::string& encoding);
-
- // Converts the raw buffer to UTF-16. Returns network error code.
- int ConvertBufferToUTF16();
-
- // Extracts lines from the converted buffer, and puts them in |lines_|.
- void ExtractFullLinesFromBuffer();
-
- // Consumes the raw buffer (i.e. does the character set conversion
- // and line splitting). Returns network error code.
- int ConsumeBuffer();
-
- // Tries to parse full lines stored in |lines_|. Returns network error code.
- int ParseLines();
-
- // Called when we received the entire input. Propagates that info to remaining
- // parsers. Returns network error code.
- int OnEndOfInput();
-
- // Detected encoding of the response (empty if unknown).
- std::string encoding_;
-
- // Buffer to keep data before character set conversion.
- std::string buffer_;
-
- // Buffer to keep data before line splitting.
- string16 converted_buffer_;
-
- // CRLF-delimited lines, without the CRLF, not yet consumed by parser.
- std::deque<string16> lines_;
-
- // A collection of parsers for different listing styles. The parsers are owned
- // by this FtpDirectoryListingBuffer.
- ParserSet parsers_;
-
- // When we're sure about the listing format, its parser is stored in
- // |current_parser_|.
- FtpDirectoryListingParser* current_parser_;
-
- DISALLOW_COPY_AND_ASSIGN(FtpDirectoryListingBuffer);
-};
-
-} // namespace net
-
-#endif // NET_FTP_FTP_DIRECTORY_LISTING_BUFFER_H_
diff --git a/net/ftp/ftp_directory_listing_parser.cc b/net/ftp/ftp_directory_listing_parser.cc
index 2f3eeca..8aa3696 100644
--- a/net/ftp/ftp_directory_listing_parser.cc
+++ b/net/ftp/ftp_directory_listing_parser.cc
@@ -1,12 +1,105 @@
-// 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.
+// Copyright (c) 2011 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 "net/ftp/ftp_directory_listing_parser.h"
+#include "base/i18n/icu_encoding_detection.h"
+#include "base/i18n/icu_string_conversions.h"
+#include "base/stl_util-inl.h"
+#include "base/string_split.h"
+#include "base/string_util.h"
+#include "net/base/net_errors.h"
+#include "net/ftp/ftp_directory_listing_parser_ls.h"
+#include "net/ftp/ftp_directory_listing_parser_netware.h"
+#include "net/ftp/ftp_directory_listing_parser_vms.h"
+#include "net/ftp/ftp_directory_listing_parser_windows.h"
+#include "net/ftp/ftp_server_type_histograms.h"
+
+namespace {
+
+// Converts a string with unknown character encoding to UTF-16. On success
+// fills in |converted_text| and |encoding|. Returns network error code.
+int ConvertStringToUTF16(const std::string& text,
+ string16* converted_text,
+ std::string* encoding) {
+ std::vector<std::string> encodings;
+ if (!base::DetectAllEncodings(text, &encodings))
+ return net::ERR_ENCODING_DETECTION_FAILED;
+
+ // Use first encoding that can be used to decode the text.
+ for (size_t i = 0; i < encodings.size(); i++) {
+ if (base::CodepageToUTF16(text,
+ encodings[i].c_str(),
+ base::OnStringConversionError::FAIL,
+ converted_text)) {
+ *encoding = encodings[i];
+ return net::OK;
+ }
+ }
+
+ return net::ERR_ENCODING_DETECTION_FAILED;
+}
+
+int FillInRawName(const std::string& encoding,
+ std::vector<net::FtpDirectoryListingEntry>* entries) {
+ for (size_t i = 0; i < entries->size(); i++) {
+ if (!base::UTF16ToCodepage(entries->at(i).name, encoding.c_str(),
+ base::OnStringConversionError::FAIL,
+ &entries->at(i).raw_name)) {
+ return net::ERR_ENCODING_CONVERSION_FAILED;
+ }
+ }
+
+ return net::OK;
+}
+
+} // namespace
+
namespace net {
-FtpDirectoryListingParser::~FtpDirectoryListingParser() {
+int ParseFtpDirectoryListing(const std::string& text,
+ const base::Time& current_time,
+ std::vector<FtpDirectoryListingEntry>* entries) {
+ std::string encoding;
+
+ string16 converted_text;
+ int rv = ConvertStringToUTF16(text, &converted_text, &encoding);
+ if (rv != OK)
+ return rv;
+
+ std::vector<string16> lines;
+ base::SplitString(converted_text, '\n', &lines);
+
+ // TODO(phajdan.jr): Use a table of callbacks instead of repeating code.
+
+ entries->clear();
+ if (ParseFtpDirectoryListingLs(lines, current_time, entries)) {
+ UpdateFtpServerTypeHistograms(SERVER_LS);
+ return FillInRawName(encoding, entries);
+ }
+
+ entries->clear();
+ if (ParseFtpDirectoryListingWindows(lines, entries)) {
+ UpdateFtpServerTypeHistograms(SERVER_WINDOWS);
+ return FillInRawName(encoding, entries);
+ }
+
+ entries->clear();
+ if (ParseFtpDirectoryListingVms(lines, entries)) {
+ UpdateFtpServerTypeHistograms(SERVER_VMS);
+ return FillInRawName(encoding, entries);
+ }
+
+ entries->clear();
+ if (ParseFtpDirectoryListingNetware(lines, current_time, entries)) {
+ UpdateFtpServerTypeHistograms(SERVER_NETWARE);
+ return FillInRawName(encoding, entries);
+ }
+
+ entries->clear();
+ UpdateFtpServerTypeHistograms(SERVER_UNKNOWN);
+ return ERR_UNRECOGNIZED_FTP_DIRECTORY_LISTING_FORMAT;
}
} // namespace
diff --git a/net/ftp/ftp_directory_listing_parser.h b/net/ftp/ftp_directory_listing_parser.h
index 6373312..b6c95fe 100644
--- a/net/ftp/ftp_directory_listing_parser.h
+++ b/net/ftp/ftp_directory_listing_parser.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -6,10 +6,12 @@
#define NET_FTP_FTP_DIRECTORY_LISTING_PARSER_H_
#pragma once
+#include <string>
+#include <vector>
+
#include "base/basictypes.h"
#include "base/string16.h"
#include "base/time.h"
-#include "net/ftp/ftp_server_type_histograms.h"
namespace net {
@@ -21,33 +23,19 @@ struct FtpDirectoryListingEntry {
};
Type type;
- string16 name;
+ string16 name; // Name (UTF-16-encoded).
+ std::string raw_name; // Name in original character encoding.
int64 size; // File size, in bytes. -1 if not applicable.
// Last modified time, in local time zone.
base::Time last_modified;
};
-class FtpDirectoryListingParser {
- public:
- virtual ~FtpDirectoryListingParser();
-
- virtual FtpServerType GetServerType() const = 0;
-
- // Adds |line| to the internal parsing buffer. Returns true on success.
- virtual bool ConsumeLine(const string16& line) = 0;
-
- // Called after all input has been consumed. Returns true if the parser
- // recognizes all received data as a valid listing.
- virtual bool OnEndOfInput() = 0;
-
- // Returns true if there is at least one FtpDirectoryListingEntry available.
- virtual bool EntryAvailable() const = 0;
-
- // Returns the next entry. It is an error to call this function unless
- // EntryAvailable returns true.
- virtual FtpDirectoryListingEntry PopEntry() = 0;
-};
+// Parses an FTP directory listing |text|. On success fills in |entries|.
+// Returns network error code.
+int ParseFtpDirectoryListing(const std::string& text,
+ const base::Time& current_time,
+ std::vector<FtpDirectoryListingEntry>* entries);
} // namespace net
diff --git a/net/ftp/ftp_directory_listing_parser_ls.cc b/net/ftp/ftp_directory_listing_parser_ls.cc
index d6d147a..7aa1e12 100644
--- a/net/ftp/ftp_directory_listing_parser_ls.cc
+++ b/net/ftp/ftp_directory_listing_parser_ls.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -9,7 +9,9 @@
#include "base/string_number_conversions.h"
#include "base/string_split.h"
#include "base/string_util.h"
+#include "base/time.h"
#include "base/utf_string_conversions.h"
+#include "net/ftp/ftp_directory_listing_parser.h"
#include "net/ftp/ftp_util.h"
namespace {
@@ -100,136 +102,125 @@ bool DetectColumnOffset(const std::vector<string16>& columns,
namespace net {
-FtpDirectoryListingParserLs::FtpDirectoryListingParserLs(
- const base::Time& current_time)
- : received_total_line_(false),
- current_time_(current_time) {
-}
-
-FtpDirectoryListingParserLs::~FtpDirectoryListingParserLs() {}
+bool ParseFtpDirectoryListingLs(
+ const std::vector<string16>& lines,
+ const base::Time& current_time,
+ std::vector<FtpDirectoryListingEntry>* entries) {
+ // True after we have received a "total n" listing header, where n is an
+ // integer. Only one such header is allowed per listing.
+ bool received_total_line = false;
+
+ for (size_t i = 0; i < lines.size(); i++) {
+ if (lines[i].empty())
+ continue;
+
+ std::vector<string16> columns;
+ base::SplitString(CollapseWhitespace(lines[i], false), ' ', &columns);
+
+ // Some FTP servers put a "total n" line at the beginning of the listing
+ // (n is an integer). Allow such a line, but only once, and only if it's
+ // the first non-empty line. Do not match the word exactly, because it may
+ // be in different languages (at least English and German have been seen
+ // in the field).
+ if (columns.size() == 2 && !received_total_line) {
+ received_total_line = true;
+
+ int total_number;
+ if (!base::StringToInt(columns[1], &total_number))
+ return false;
+ if (total_number < 0)
+ return false;
+
+ continue;
+ }
+
+ int column_offset;
+ if (!DetectColumnOffset(columns, current_time, &column_offset)) {
+ // If we can't recognize a normal listing line, maybe it's an error?
+ // In that case, just ignore the error, but still recognize the data
+ // as valid listing.
+ if (LooksLikePermissionDeniedError(lines[i]))
+ continue;
-FtpServerType FtpDirectoryListingParserLs::GetServerType() const {
- return SERVER_LS;
-}
-
-bool FtpDirectoryListingParserLs::ConsumeLine(const string16& line) {
- if (line.empty())
- return true;
-
- std::vector<string16> columns;
- base::SplitString(CollapseWhitespace(line, false), ' ', &columns);
-
- // Some FTP servers put a "total n" line at the beginning of the listing
- // (n is an integer). Allow such a line, but only once, and only if it's
- // the first non-empty line. Do not match the word exactly, because it may be
- // in different languages (at least English and German have been seen in the
- // field).
- if (columns.size() == 2 && !received_total_line_) {
- received_total_line_ = true;
-
- int total_number;
- if (!base::StringToInt(columns[1], &total_number))
return false;
- if (total_number < 0)
+ }
+
+ // We may receive file names containing spaces, which can make the number of
+ // columns arbitrarily large. We will handle that later. For now just make
+ // sure we have all the columns that should normally be there:
+ //
+ // 1. permission listing
+ // 2. number of links (optional)
+ // 3. owner name
+ // 4. group name (optional)
+ // 5. size in bytes
+ // 6. month
+ // 7. day of month
+ // 8. year or time
+ //
+ // The number of optional columns is stored in |column_offset|
+ // and is between 0 and 2 (inclusive).
+ if (columns.size() < 6U + column_offset)
return false;
- return true;
- }
-
- int column_offset;
- if (!DetectColumnOffset(columns, current_time_, &column_offset)) {
- // If we can't recognize a normal listing line, maybe it's an error?
- // In that case, just ignore the error, but still recognize the data
- // as valid listing.
- return LooksLikePermissionDeniedError(line);
- }
-
- // We may receive file names containing spaces, which can make the number of
- // columns arbitrarily large. We will handle that later. For now just make
- // sure we have all the columns that should normally be there:
- //
- // 1. permission listing
- // 2. number of links (optional)
- // 3. owner name
- // 4. group name (optional)
- // 5. size in bytes
- // 6. month
- // 7. day of month
- // 8. year or time
- //
- // The number of optional columns is stored in |column_offset|
- // and is between 0 and 2 (inclusive).
- if (columns.size() < 6U + column_offset)
- return false;
-
- if (!LooksLikeUnixPermissionsListing(columns[0]))
- return false;
+ if (!LooksLikeUnixPermissionsListing(columns[0]))
+ return false;
- FtpDirectoryListingEntry entry;
- if (columns[0][0] == 'l') {
- entry.type = FtpDirectoryListingEntry::SYMLINK;
- } else if (columns[0][0] == 'd') {
- entry.type = FtpDirectoryListingEntry::DIRECTORY;
- } else {
- entry.type = FtpDirectoryListingEntry::FILE;
- }
+ FtpDirectoryListingEntry entry;
+ if (columns[0][0] == 'l') {
+ entry.type = FtpDirectoryListingEntry::SYMLINK;
+ } else if (columns[0][0] == 'd') {
+ entry.type = FtpDirectoryListingEntry::DIRECTORY;
+ } else {
+ entry.type = FtpDirectoryListingEntry::FILE;
+ }
+
+ if (!base::StringToInt64(columns[2 + column_offset], &entry.size)) {
+ // Some FTP servers do not separate owning group name from file size,
+ // like "group1234". We still want to display the file name for that
+ // entry, but can't really get the size (What if the group is named
+ // "group1", and the size is in fact 234? We can't distinguish between
+ // that and "group" with size 1234). Use a dummy value for the size.
+ // TODO(phajdan.jr): Use a value that means "unknown" instead of 0 bytes.
+ entry.size = 0;
+ }
+ if (entry.size < 0)
+ return false;
+ if (entry.type != FtpDirectoryListingEntry::FILE)
+ entry.size = -1;
+
+ if (!FtpUtil::LsDateListingToTime(columns[3 + column_offset],
+ columns[4 + column_offset],
+ columns[5 + column_offset],
+ current_time,
+ &entry.last_modified)) {
+ return false;
+ }
- if (!base::StringToInt64(columns[2 + column_offset], &entry.size)) {
- // Some FTP servers do not separate owning group name from file size,
- // like "group1234". We still want to display the file name for that entry,
- // but can't really get the size (What if the group is named "group1",
- // and the size is in fact 234? We can't distinguish between that
- // and "group" with size 1234). Use a dummy value for the size.
- // TODO(phajdan.jr): Use a value that means "unknown" instead of 0 bytes.
- entry.size = 0;
- }
- if (entry.size < 0)
- return false;
- if (entry.type != FtpDirectoryListingEntry::FILE)
- entry.size = -1;
-
- if (!FtpUtil::LsDateListingToTime(columns[3 + column_offset],
- columns[4 + column_offset],
- columns[5 + column_offset],
- current_time_,
- &entry.last_modified)) {
- return false;
- }
+ entry.name = FtpUtil::GetStringPartAfterColumns(lines[i],
+ 6 + column_offset);
- entry.name = FtpUtil::GetStringPartAfterColumns(line, 6 + column_offset);
+ if (entry.name.empty()) {
+ // Some FTP servers send listing entries with empty names.
+ // It's not obvious how to display such an entry, so ignore them.
+ // We don't want to make the parsing fail at this point though.
+ // Other entries can still be useful.
+ continue;
+ }
- if (entry.name.empty()) {
- // Some FTP servers send listing entries with empty names. It's not obvious
- // how to display such an entry, so we ignore them. We don't want to make
- // the parsing fail at this point though. Other entries can still be useful.
- return true;
- }
+ if (entry.type == FtpDirectoryListingEntry::SYMLINK) {
+ string16::size_type pos = entry.name.rfind(ASCIIToUTF16(" -> "));
- if (entry.type == FtpDirectoryListingEntry::SYMLINK) {
- string16::size_type pos = entry.name.rfind(ASCIIToUTF16(" -> "));
+ // We don't require the " -> " to be present. Some FTP servers don't send
+ // the symlink target, possibly for security reasons.
+ if (pos != string16::npos)
+ entry.name = entry.name.substr(0, pos);
+ }
- // We don't require the " -> " to be present. Some FTP servers don't send
- // the symlink target, possibly for security reasons.
- if (pos != string16::npos)
- entry.name = entry.name.substr(0, pos);
+ entries->push_back(entry);
}
- entries_.push(entry);
- return true;
-}
-
-bool FtpDirectoryListingParserLs::OnEndOfInput() {
return true;
}
-bool FtpDirectoryListingParserLs::EntryAvailable() const {
- return !entries_.empty();
-}
-
-FtpDirectoryListingEntry FtpDirectoryListingParserLs::PopEntry() {
- FtpDirectoryListingEntry entry = entries_.front();
- entries_.pop();
- return entry;
-}
-
} // namespace net
diff --git a/net/ftp/ftp_directory_listing_parser_ls.h b/net/ftp/ftp_directory_listing_parser_ls.h
index 49607092..d27bd91 100644
--- a/net/ftp/ftp_directory_listing_parser_ls.h
+++ b/net/ftp/ftp_directory_listing_parser_ls.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -6,41 +6,22 @@
#define NET_FTP_FTP_DIRECTORY_LISTING_PARSER_LS_H_
#pragma once
-#include <queue>
+#include <vector>
-#include "base/time.h"
-#include "net/ftp/ftp_directory_listing_parser.h"
+#include "base/string16.h"
+
+namespace base {
+class Time;
+}
namespace net {
-// Parser for "ls -l"-style directory listing.
-class FtpDirectoryListingParserLs : public FtpDirectoryListingParser {
- public:
- // Constructor. When the current time is needed to guess the year on partial
- // date strings, |current_time| will be used. This allows passing a specific
- // date during testing.
- explicit FtpDirectoryListingParserLs(const base::Time& current_time);
- virtual ~FtpDirectoryListingParserLs();
-
- // FtpDirectoryListingParser methods:
- virtual FtpServerType GetServerType() const;
- virtual bool ConsumeLine(const string16& line);
- virtual bool OnEndOfInput();
- virtual bool EntryAvailable() const;
- virtual FtpDirectoryListingEntry PopEntry();
-
- private:
- // True after we have received a "total n" listing header, where n is an
- // integer. Only one such header is allowed per listing.
- bool received_total_line_;
-
- // Store the current time. We need it to correctly parse received dates.
- const base::Time current_time_;
-
- std::queue<FtpDirectoryListingEntry> entries_;
-
- DISALLOW_COPY_AND_ASSIGN(FtpDirectoryListingParserLs);
-};
+struct FtpDirectoryListingEntry;
+
+// Parses "ls -l" FTP directory listing. Returns true on success.
+bool ParseFtpDirectoryListingLs(const std::vector<string16>& lines,
+ const base::Time& current_time,
+ std::vector<FtpDirectoryListingEntry>* entries);
} // namespace net
diff --git a/net/ftp/ftp_directory_listing_parser_ls_unittest.cc b/net/ftp/ftp_directory_listing_parser_ls_unittest.cc
index 88bbecd..8a1fe95 100644
--- a/net/ftp/ftp_directory_listing_parser_ls_unittest.cc
+++ b/net/ftp/ftp_directory_listing_parser_ls_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -9,91 +9,97 @@
#include "base/stringprintf.h"
#include "net/ftp/ftp_directory_listing_parser_ls.h"
+namespace net {
+
namespace {
-typedef net::FtpDirectoryListingParserTest FtpDirectoryListingParserLsTest;
+typedef FtpDirectoryListingParserTest FtpDirectoryListingParserLsTest;
TEST_F(FtpDirectoryListingParserLsTest, Good) {
const struct SingleLineTestData good_cases[] = {
{ "-rw-r--r-- 1 ftp ftp 528 Nov 01 2007 README",
- net::FtpDirectoryListingEntry::FILE, "README", 528,
+ FtpDirectoryListingEntry::FILE, "README", 528,
2007, 11, 1, 0, 0 },
{ "drwxr-xr-x 3 ftp ftp 4096 May 15 18:11 directory",
- net::FtpDirectoryListingEntry::DIRECTORY, "directory", -1,
+ FtpDirectoryListingEntry::DIRECTORY, "directory", -1,
1994, 5, 15, 18, 11 },
{ "lrwxrwxrwx 1 0 0 26 Sep 18 2008 pub -> vol/1/.CLUSTER/var_ftp/pub",
- net::FtpDirectoryListingEntry::SYMLINK, "pub", -1,
+ FtpDirectoryListingEntry::SYMLINK, "pub", -1,
2008, 9, 18, 0, 0 },
{ "lrwxrwxrwx 1 0 0 3 Oct 12 13:37 mirror -> pub",
- net::FtpDirectoryListingEntry::SYMLINK, "mirror", -1,
+ FtpDirectoryListingEntry::SYMLINK, "mirror", -1,
1994, 10, 12, 13, 37 },
{ "drwxrwsr-x 4 501 501 4096 Feb 20 2007 pub",
- net::FtpDirectoryListingEntry::DIRECTORY, "pub", -1,
+ FtpDirectoryListingEntry::DIRECTORY, "pub", -1,
2007, 2, 20, 0, 0 },
{ "drwxr-xr-x 4 (?) (?) 4096 Apr 8 2007 jigdo",
- net::FtpDirectoryListingEntry::DIRECTORY, "jigdo", -1,
+ FtpDirectoryListingEntry::DIRECTORY, "jigdo", -1,
2007, 4, 8, 0, 0 },
{ "drwx-wx-wt 2 root wheel 512 Jul 1 02:15 incoming",
- net::FtpDirectoryListingEntry::DIRECTORY, "incoming", -1,
+ FtpDirectoryListingEntry::DIRECTORY, "incoming", -1,
1994, 7, 1, 2, 15 },
{ "-rw-r--r-- 1 2 3 3447432 May 18 2009 Foo - Manual.pdf",
- net::FtpDirectoryListingEntry::FILE, "Foo - Manual.pdf", 3447432,
+ FtpDirectoryListingEntry::FILE, "Foo - Manual.pdf", 3447432,
2009, 5, 18, 0, 0 },
{ "d-wx-wx-wt+ 4 ftp 989 512 Dec 8 15:54 incoming",
- net::FtpDirectoryListingEntry::DIRECTORY, "incoming", -1,
+ FtpDirectoryListingEntry::DIRECTORY, "incoming", -1,
1993, 12, 8, 15, 54 },
{ "drwxrwxrwx 1 owner group 0 Sep 13 0:30 audio",
- net::FtpDirectoryListingEntry::DIRECTORY, "audio", -1,
+ FtpDirectoryListingEntry::DIRECTORY, "audio", -1,
1994, 9, 13, 0, 30 },
{ "lrwxrwxrwx 1 0 0 26 Sep 18 2008 pub",
- net::FtpDirectoryListingEntry::SYMLINK, "pub", -1,
+ FtpDirectoryListingEntry::SYMLINK, "pub", -1,
2008, 9, 18, 0, 0 },
// Tests for the wu-ftpd variant:
{ "drwxr-xr-x 2 sys 512 Mar 27 2009 pub",
- net::FtpDirectoryListingEntry::DIRECTORY, "pub", -1,
+ FtpDirectoryListingEntry::DIRECTORY, "pub", -1,
2009, 3, 27, 0, 0 },
{ "lrwxrwxrwx 0 0 26 Sep 18 2008 pub -> vol/1/.CLUSTER/var_ftp/pub",
- net::FtpDirectoryListingEntry::SYMLINK, "pub", -1,
+ FtpDirectoryListingEntry::SYMLINK, "pub", -1,
2008, 9, 18, 0, 0 },
{ "drwxr-xr-x (?) (?) 4096 Apr 8 2007 jigdo",
- net::FtpDirectoryListingEntry::DIRECTORY, "jigdo", -1,
+ FtpDirectoryListingEntry::DIRECTORY, "jigdo", -1,
2007, 4, 8, 0, 0 },
{ "-rw-r--r-- 2 3 3447432 May 18 2009 Foo - Manual.pdf",
- net::FtpDirectoryListingEntry::FILE, "Foo - Manual.pdf", 3447432,
+ FtpDirectoryListingEntry::FILE, "Foo - Manual.pdf", 3447432,
2009, 5, 18, 0, 0 },
// Tests for "ls -l" style listings sent by an OS/2 server (FtpServer):
{ "-r--r--r-- 1 ftp -A--- 13274 Mar 1 2006 UpTime.exe",
- net::FtpDirectoryListingEntry::FILE, "UpTime.exe", 13274,
+ FtpDirectoryListingEntry::FILE, "UpTime.exe", 13274,
2006, 3, 1, 0, 0 },
{ "dr--r--r-- 1 ftp ----- 0 Nov 17 17:08 kernels",
- net::FtpDirectoryListingEntry::DIRECTORY, "kernels", -1,
+ FtpDirectoryListingEntry::DIRECTORY, "kernels", -1,
1993, 11, 17, 17, 8 },
// Tests for "ls -l" style listing sent by Xplain FTP Server.
{ "drwxr-xr-x folder 0 Jul 17 2006 online",
- net::FtpDirectoryListingEntry::DIRECTORY, "online", -1,
+ FtpDirectoryListingEntry::DIRECTORY, "online", -1,
2006, 7, 17, 0, 0 },
// Tests for "ls -l" style listing with owning group name
// not separated from file size (http://crbug.com/58963).
{ "-rw-r--r-- 1 ftpadmin ftpadmin125435904 Apr 9 2008 .pureftpd-upload",
- net::FtpDirectoryListingEntry::FILE, ".pureftpd-upload", 0,
+ FtpDirectoryListingEntry::FILE, ".pureftpd-upload", 0,
2008, 4, 9, 0, 0 },
// Tests for "ls -l" style listing with number of links
// not separated from permission listing (http://crbug.com/70394).
{ "drwxr-xr-x1732 266 111 90112 Jun 21 2001 .rda_2",
- net::FtpDirectoryListingEntry::DIRECTORY, ".rda_2", -1,
+ FtpDirectoryListingEntry::DIRECTORY, ".rda_2", -1,
2001, 6, 21, 0, 0 },
};
for (size_t i = 0; i < arraysize(good_cases); i++) {
SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]: %s", i,
good_cases[i].input));
- net::FtpDirectoryListingParserLs parser(GetMockCurrentTime());
- RunSingleLineTestCase(&parser, good_cases[i]);
+ std::vector<FtpDirectoryListingEntry> entries;
+ EXPECT_TRUE(ParseFtpDirectoryListingLs(
+ GetSingleLineTestCase(good_cases[i].input),
+ GetMockCurrentTime(),
+ &entries));
+ VerifySingleLineTestCase(good_cases[i], entries);
}
}
@@ -114,11 +120,12 @@ TEST_F(FtpDirectoryListingParserLsTest, Ignored) {
SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]: %s", i,
ignored_cases[i]));
- net::FtpDirectoryListingParserLs parser(GetMockCurrentTime());
- EXPECT_TRUE(parser.ConsumeLine(UTF8ToUTF16(ignored_cases[i])));
- EXPECT_FALSE(parser.EntryAvailable());
- EXPECT_TRUE(parser.OnEndOfInput());
- EXPECT_FALSE(parser.EntryAvailable());
+ std::vector<FtpDirectoryListingEntry> entries;
+ EXPECT_TRUE(ParseFtpDirectoryListingLs(
+ GetSingleLineTestCase(ignored_cases[i]),
+ GetMockCurrentTime(),
+ &entries));
+ EXPECT_EQ(0U, entries.size());
}
}
@@ -143,9 +150,16 @@ TEST_F(FtpDirectoryListingParserLsTest, Bad) {
"-qqqqqqqqq+ 2 sys 512 Mar 27 2009 pub",
};
for (size_t i = 0; i < arraysize(bad_cases); i++) {
- net::FtpDirectoryListingParserLs parser(GetMockCurrentTime());
- EXPECT_FALSE(parser.ConsumeLine(UTF8ToUTF16(bad_cases[i]))) << bad_cases[i];
+ SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]: %s", i,
+ bad_cases[i]));
+
+ std::vector<FtpDirectoryListingEntry> entries;
+ EXPECT_FALSE(ParseFtpDirectoryListingLs(GetSingleLineTestCase(bad_cases[i]),
+ GetMockCurrentTime(),
+ &entries));
}
}
} // namespace
+
+} // namespace net
diff --git a/net/ftp/ftp_directory_listing_parser_netware.cc b/net/ftp/ftp_directory_listing_parser_netware.cc
index 1801a4a..1e8c80e 100644
--- a/net/ftp/ftp_directory_listing_parser_netware.cc
+++ b/net/ftp/ftp_directory_listing_parser_netware.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -10,6 +10,7 @@
#include "base/string_split.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
+#include "net/ftp/ftp_directory_listing_parser.h"
#include "net/ftp/ftp_util.h"
namespace {
@@ -34,80 +35,60 @@ bool LooksLikeNetwarePermissionsListing(const string16& text) {
namespace net {
-FtpDirectoryListingParserNetware::FtpDirectoryListingParserNetware(
- const base::Time& current_time)
- : received_first_line_(false),
- current_time_(current_time) {
-}
-
-FtpDirectoryListingParserNetware::~FtpDirectoryListingParserNetware() {}
-
-FtpServerType FtpDirectoryListingParserNetware::GetServerType() const {
- return SERVER_NETWARE;
-}
-
-bool FtpDirectoryListingParserNetware::ConsumeLine(const string16& line) {
- if (!received_first_line_) {
- received_first_line_ = true;
-
- return StartsWith(line, ASCIIToUTF16("total "), true);
- }
-
- std::vector<string16> columns;
- base::SplitString(CollapseWhitespace(line, false), ' ', &columns);
-
- if (columns.size() != 8)
- return false;
-
- FtpDirectoryListingEntry entry;
-
- if (columns[0].length() != 1)
- return false;
- if (columns[0][0] == 'd') {
- entry.type = FtpDirectoryListingEntry::DIRECTORY;
- } else if (columns[0][0] == '-') {
- entry.type = FtpDirectoryListingEntry::FILE;
- } else {
- return false;
- }
-
- // Note: on older Netware systems the permissions listing is in the same
- // column as the entry type (just there is no space between them). We do not
- // support the older format here for simplicity.
- if (!LooksLikeNetwarePermissionsListing(columns[1]))
+bool ParseFtpDirectoryListingNetware(
+ const std::vector<string16>& lines,
+ const base::Time& current_time,
+ std::vector<FtpDirectoryListingEntry>* entries) {
+ if (!lines.empty() && !StartsWith(lines[0], ASCIIToUTF16("total "), true))
return false;
- if (!base::StringToInt64(columns[3], &entry.size))
- return false;
- if (entry.size < 0)
- return false;
- if (entry.type != FtpDirectoryListingEntry::FILE)
- entry.size = -1;
-
- // Netware uses the same date listing format as Unix "ls -l".
- if (!FtpUtil::LsDateListingToTime(columns[4], columns[5], columns[6],
- current_time_, &entry.last_modified)) {
- return false;
+ for (size_t i = 1U; i < lines.size(); i++) {
+ if (lines[i].empty())
+ continue;
+
+ std::vector<string16> columns;
+ base::SplitString(CollapseWhitespace(lines[i], false), ' ', &columns);
+
+ if (columns.size() != 8)
+ return false;
+
+ FtpDirectoryListingEntry entry;
+
+ if (columns[0].length() != 1)
+ return false;
+ if (columns[0][0] == 'd') {
+ entry.type = FtpDirectoryListingEntry::DIRECTORY;
+ } else if (columns[0][0] == '-') {
+ entry.type = FtpDirectoryListingEntry::FILE;
+ } else {
+ return false;
+ }
+
+ // Note: on older Netware systems the permissions listing is in the same
+ // column as the entry type (just there is no space between them). We do not
+ // support the older format here for simplicity.
+ if (!LooksLikeNetwarePermissionsListing(columns[1]))
+ return false;
+
+ if (!base::StringToInt64(columns[3], &entry.size))
+ return false;
+ if (entry.size < 0)
+ return false;
+ if (entry.type != FtpDirectoryListingEntry::FILE)
+ entry.size = -1;
+
+ // Netware uses the same date listing format as Unix "ls -l".
+ if (!FtpUtil::LsDateListingToTime(columns[4], columns[5], columns[6],
+ current_time, &entry.last_modified)) {
+ return false;
+ }
+
+ entry.name = columns[7];
+
+ entries->push_back(entry);
}
- entry.name = columns[7];
-
- entries_.push(entry);
return true;
}
-bool FtpDirectoryListingParserNetware::OnEndOfInput() {
- return true;
-}
-
-bool FtpDirectoryListingParserNetware::EntryAvailable() const {
- return !entries_.empty();
-}
-
-FtpDirectoryListingEntry FtpDirectoryListingParserNetware::PopEntry() {
- FtpDirectoryListingEntry entry = entries_.front();
- entries_.pop();
- return entry;
-}
-
} // namespace net
diff --git a/net/ftp/ftp_directory_listing_parser_netware.h b/net/ftp/ftp_directory_listing_parser_netware.h
index 48aebfc..b72a49f 100644
--- a/net/ftp/ftp_directory_listing_parser_netware.h
+++ b/net/ftp/ftp_directory_listing_parser_netware.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -6,40 +6,23 @@
#define NET_FTP_FTP_DIRECTORY_LISTING_PARSER_NETWARE_H_
#pragma once
-#include <queue>
+#include <vector>
-#include "base/time.h"
-#include "net/ftp/ftp_directory_listing_parser.h"
+#include "base/string16.h"
+
+namespace base {
+class Time;
+}
namespace net {
-// Parser for Netware-style directory listing.
-class FtpDirectoryListingParserNetware : public FtpDirectoryListingParser {
- public:
- // Constructor. When the current time is needed to guess the year on partial
- // date strings, |current_time| will be used. This allows passing a specific
- // date during testing.
- explicit FtpDirectoryListingParserNetware(const base::Time& current_time);
- virtual ~FtpDirectoryListingParserNetware();
-
- // FtpDirectoryListingParser methods:
- virtual FtpServerType GetServerType() const;
- virtual bool ConsumeLine(const string16& line);
- virtual bool OnEndOfInput();
- virtual bool EntryAvailable() const;
- virtual FtpDirectoryListingEntry PopEntry();
-
- private:
- // True after we have received the first line of input.
- bool received_first_line_;
-
- // Store the current time. We need it to correctly parse received dates.
- const base::Time current_time_;
-
- std::queue<FtpDirectoryListingEntry> entries_;
-
- DISALLOW_COPY_AND_ASSIGN(FtpDirectoryListingParserNetware);
-};
+struct FtpDirectoryListingEntry;
+
+// Parses Netware FTP directory listing. Returns true on success.
+bool ParseFtpDirectoryListingNetware(
+ const std::vector<string16>& lines,
+ const base::Time& current_time,
+ std::vector<FtpDirectoryListingEntry>* entries);
} // namespace net
diff --git a/net/ftp/ftp_directory_listing_parser_netware_unittest.cc b/net/ftp/ftp_directory_listing_parser_netware_unittest.cc
index c02fb97..5f373ac 100644
--- a/net/ftp/ftp_directory_listing_parser_netware_unittest.cc
+++ b/net/ftp/ftp_directory_listing_parser_netware_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -10,27 +10,35 @@
#include "base/utf_string_conversions.h"
#include "net/ftp/ftp_directory_listing_parser_netware.h"
+namespace net {
+
namespace {
-typedef net::FtpDirectoryListingParserTest FtpDirectoryListingParserNetwareTest;
+typedef FtpDirectoryListingParserTest FtpDirectoryListingParserNetwareTest;
TEST_F(FtpDirectoryListingParserNetwareTest, Good) {
const struct SingleLineTestData good_cases[] = {
{ "d [RWCEAFMS] ftpadmin 512 Jan 29 2004 pub",
- net::FtpDirectoryListingEntry::DIRECTORY, "pub", -1,
+ FtpDirectoryListingEntry::DIRECTORY, "pub", -1,
2004, 1, 29, 0, 0 },
{ "- [RW------] ftpadmin 123 Nov 11 18:25 afile",
- net::FtpDirectoryListingEntry::FILE, "afile", 123,
+ FtpDirectoryListingEntry::FILE, "afile", 123,
1994, 11, 11, 18, 25 },
};
for (size_t i = 0; i < arraysize(good_cases); i++) {
SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]: %s", i,
good_cases[i].input));
- net::FtpDirectoryListingParserNetware parser(GetMockCurrentTime());
- // The parser requires a "total n" like before accepting regular input.
- ASSERT_TRUE(parser.ConsumeLine(UTF8ToUTF16("total 1")));
- RunSingleLineTestCase(&parser, good_cases[i]);
+ std::vector<string16> lines(GetSingleLineTestCase(good_cases[i].input));
+
+ // The parser requires a "total n" line before accepting regular input.
+ lines.insert(lines.begin(), ASCIIToUTF16("total 1"));
+
+ std::vector<FtpDirectoryListingEntry> entries;
+ EXPECT_TRUE(ParseFtpDirectoryListingNetware(lines,
+ GetMockCurrentTime(),
+ &entries));
+ VerifySingleLineTestCase(good_cases[i], entries);
}
}
@@ -45,11 +53,21 @@ TEST_F(FtpDirectoryListingParserNetwareTest, Bad) {
"l [RW------] ftpadmin 512 Jan 29 2004 pub",
};
for (size_t i = 0; i < arraysize(bad_cases); i++) {
- net::FtpDirectoryListingParserNetware parser(GetMockCurrentTime());
- // The parser requires a "total n" like before accepting regular input.
- ASSERT_TRUE(parser.ConsumeLine(UTF8ToUTF16("total 1")));
- EXPECT_FALSE(parser.ConsumeLine(UTF8ToUTF16(bad_cases[i]))) << bad_cases[i];
+ SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]: %s", i,
+ bad_cases[i]));
+
+ std::vector<string16> lines(GetSingleLineTestCase(bad_cases[i]));
+
+ // The parser requires a "total n" line before accepting regular input.
+ lines.insert(lines.begin(), ASCIIToUTF16("total 1"));
+
+ std::vector<FtpDirectoryListingEntry> entries;
+ EXPECT_FALSE(ParseFtpDirectoryListingNetware(lines,
+ GetMockCurrentTime(),
+ &entries));
}
}
} // namespace
+
+} // namespace net
diff --git a/net/ftp/ftp_directory_listing_buffer_unittest.cc b/net/ftp/ftp_directory_listing_parser_unittest.cc
index 4c71008..b8f0851 100644
--- a/net/ftp/ftp_directory_listing_buffer_unittest.cc
+++ b/net/ftp/ftp_directory_listing_parser_unittest.cc
@@ -1,8 +1,8 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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 "net/ftp/ftp_directory_listing_buffer.h"
+#include "net/ftp/ftp_directory_listing_parser.h"
#include "base/file_util.h"
#include "base/format_macros.h"
@@ -15,6 +15,8 @@
#include "net/ftp/ftp_directory_listing_parser.h"
#include "testing/gtest/include/gtest/gtest.h"
+namespace net {
+
namespace {
TEST(FtpDirectoryListingBufferTest, Parse) {
@@ -73,15 +75,14 @@ TEST(FtpDirectoryListingBufferTest, Parse) {
for (size_t i = 0; i < arraysize(test_files); i++) {
SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]: %s", i, test_files[i]));
- net::FtpDirectoryListingBuffer buffer(mock_current_time);
-
std::string test_listing;
EXPECT_TRUE(file_util::ReadFileToString(test_dir.AppendASCII(test_files[i]),
&test_listing));
- EXPECT_EQ(net::OK, buffer.ConsumeData(test_listing.data(),
- test_listing.length()));
- EXPECT_EQ(net::OK, buffer.ProcessRemainingData());
+ std::vector<FtpDirectoryListingEntry> entries;
+ EXPECT_EQ(OK, ParseFtpDirectoryListing(test_listing,
+ mock_current_time,
+ &entries));
std::string expected_listing;
ASSERT_TRUE(file_util::ReadFileToString(
@@ -92,7 +93,8 @@ TEST(FtpDirectoryListingBufferTest, Parse) {
StringTokenizer tokenizer(expected_listing, "\r\n");
while (tokenizer.GetNext())
lines.push_back(tokenizer.token());
- ASSERT_EQ(0U, lines.size() % 8);
+
+ ASSERT_EQ(8 * entries.size(), lines.size());
for (size_t i = 0; i < lines.size() / 8; i++) {
std::string type(lines[8 * i]);
@@ -109,15 +111,14 @@ TEST(FtpDirectoryListingBufferTest, Parse) {
base::StringToInt(lines[8 * i + 6], &hour);
base::StringToInt(lines[8 * i + 7], &minute);
- ASSERT_TRUE(buffer.EntryAvailable());
- net::FtpDirectoryListingEntry entry = buffer.PopEntry();
+ const FtpDirectoryListingEntry& entry = entries[i];
if (type == "d") {
- EXPECT_EQ(net::FtpDirectoryListingEntry::DIRECTORY, entry.type);
+ EXPECT_EQ(FtpDirectoryListingEntry::DIRECTORY, entry.type);
} else if (type == "-") {
- EXPECT_EQ(net::FtpDirectoryListingEntry::FILE, entry.type);
+ EXPECT_EQ(FtpDirectoryListingEntry::FILE, entry.type);
} else if (type == "l") {
- EXPECT_EQ(net::FtpDirectoryListingEntry::SYMLINK, entry.type);
+ EXPECT_EQ(FtpDirectoryListingEntry::SYMLINK, entry.type);
} else {
ADD_FAILURE() << "invalid gold test data: " << type;
}
@@ -133,8 +134,9 @@ TEST(FtpDirectoryListingBufferTest, Parse) {
EXPECT_EQ(hour, time_exploded.hour);
EXPECT_EQ(minute, time_exploded.minute);
}
- EXPECT_FALSE(buffer.EntryAvailable());
}
}
} // namespace
+
+} // namespace net
diff --git a/net/ftp/ftp_directory_listing_parser_unittest.h b/net/ftp/ftp_directory_listing_parser_unittest.h
index 5f9d6f0..11eb75b 100644
--- a/net/ftp/ftp_directory_listing_parser_unittest.h
+++ b/net/ftp/ftp_directory_listing_parser_unittest.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -6,6 +6,8 @@
#define NET_FTP_FTP_DIRECTORY_LISTING_PARSER_UNITTEST_H_
#pragma once
+#include <vector>
+
#include "base/utf_string_conversions.h"
#include "net/ftp/ftp_directory_listing_parser.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -29,11 +31,18 @@ class FtpDirectoryListingParserTest : public testing::Test {
protected:
FtpDirectoryListingParserTest() {}
- void RunSingleLineTestCase(FtpDirectoryListingParser* parser,
- const SingleLineTestData& test_case) {
- ASSERT_TRUE(parser->ConsumeLine(UTF8ToUTF16(test_case.input)));
- ASSERT_TRUE(parser->EntryAvailable());
- FtpDirectoryListingEntry entry = parser->PopEntry();
+ std::vector<string16> GetSingleLineTestCase(const std::string& text) {
+ std::vector<string16> lines;
+ lines.push_back(UTF8ToUTF16(text));
+ return lines;
+ }
+
+ void VerifySingleLineTestCase(
+ const SingleLineTestData& test_case,
+ const std::vector<FtpDirectoryListingEntry>& entries) {
+ ASSERT_FALSE(entries.empty());
+
+ FtpDirectoryListingEntry entry = entries[0];
EXPECT_EQ(test_case.type, entry.type);
EXPECT_EQ(UTF8ToUTF16(test_case.filename), entry.name);
EXPECT_EQ(test_case.size, entry.size);
@@ -47,6 +56,8 @@ class FtpDirectoryListingParserTest : public testing::Test {
EXPECT_EQ(test_case.day_of_month, time_exploded.day_of_month);
EXPECT_EQ(test_case.hour, time_exploded.hour);
EXPECT_EQ(test_case.minute, time_exploded.minute);
+
+ EXPECT_EQ(1U, entries.size());
}
base::Time GetMockCurrentTime() {
@@ -58,9 +69,6 @@ class FtpDirectoryListingParserTest : public testing::Test {
mock_current_time_exploded.minute = 45;
return base::Time::FromLocalExploded(mock_current_time_exploded);
}
-
- private:
- DISALLOW_COPY_AND_ASSIGN(FtpDirectoryListingParserTest);
};
} // namespace net
diff --git a/net/ftp/ftp_directory_listing_parser_vms.cc b/net/ftp/ftp_directory_listing_parser_vms.cc
index 65a3161..9a8a2f7 100644
--- a/net/ftp/ftp_directory_listing_parser_vms.cc
+++ b/net/ftp/ftp_directory_listing_parser_vms.cc
@@ -9,15 +9,19 @@
#include "base/string_number_conversions.h"
#include "base/string_split.h"
#include "base/string_util.h"
+#include "base/time.h"
#include "base/utf_string_conversions.h"
+#include "net/ftp/ftp_directory_listing_parser.h"
#include "net/ftp/ftp_util.h"
+namespace net {
+
namespace {
// Converts the filename component in listing to the filename we can display.
// Returns true on success.
bool ParseVmsFilename(const string16& raw_filename, string16* parsed_filename,
- bool* is_directory) {
+ FtpDirectoryListingEntry::Type* type) {
// On VMS, the files and directories are versioned. The version number is
// separated from the file name by a semicolon. Example: ANNOUNCE.TXT;2.
std::vector<string16> listing_parts;
@@ -40,10 +44,10 @@ bool ParseVmsFilename(const string16& raw_filename, string16* parsed_filename,
return false;
if (EqualsASCII(filename_parts[1], "DIR")) {
*parsed_filename = StringToLowerASCII(filename_parts[0]);
- *is_directory = true;
+ *type = FtpDirectoryListingEntry::DIRECTORY;
} else {
*parsed_filename = StringToLowerASCII(listing_parts[0]);
- *is_directory = false;
+ *type = FtpDirectoryListingEntry::FILE;
}
return true;
}
@@ -117,28 +121,42 @@ bool LooksLikeVmsUserIdentificationCode(const string16& input) {
return input[0] == '[' && input[input.length() - 1] == ']';
}
+bool LooksLikePermissionDeniedError(const string16& text) {
+ static const char* kPermissionDeniedMessages[] = {
+ "%RMS-E-PRV",
+ "privilege",
+ };
+
+ for (size_t i = 0; i < arraysize(kPermissionDeniedMessages); i++) {
+ if (text.find(ASCIIToUTF16(kPermissionDeniedMessages[i])) != string16::npos)
+ return true;
+ }
+
+ return false;
+}
+
bool VmsDateListingToTime(const std::vector<string16>& columns,
base::Time* time) {
- DCHECK_EQ(3U, columns.size());
+ DCHECK_EQ(4U, columns.size());
base::Time::Exploded time_exploded = { 0 };
// Date should be in format DD-MMM-YYYY.
std::vector<string16> date_parts;
- base::SplitString(columns[1], '-', &date_parts);
+ base::SplitString(columns[2], '-', &date_parts);
if (date_parts.size() != 3)
return false;
if (!base::StringToInt(date_parts[0], &time_exploded.day_of_month))
return false;
- if (!net::FtpUtil::AbbreviatedMonthToNumber(date_parts[1],
- &time_exploded.month))
+ if (!FtpUtil::AbbreviatedMonthToNumber(date_parts[1],
+ &time_exploded.month))
return false;
if (!base::StringToInt(date_parts[2], &time_exploded.year))
return false;
// Time can be in format HH:MM, HH:MM:SS, or HH:MM:SS.mm. Try to recognize the
// last type first. Do not parse the seconds, they will be ignored anyway.
- string16 time_column(columns[2]);
+ string16 time_column(columns[3]);
if (time_column.length() == 11 && time_column[8] == '.')
time_column = time_column.substr(0, 8);
if (time_column.length() == 8 && time_column[5] == ':')
@@ -161,144 +179,88 @@ bool VmsDateListingToTime(const std::vector<string16>& columns,
} // namespace
-namespace net {
-
-FtpDirectoryListingParserVms::FtpDirectoryListingParserVms()
- : state_(STATE_INITIAL),
- last_is_directory_(false) {
-}
-
-FtpDirectoryListingParserVms::~FtpDirectoryListingParserVms() {}
-
-FtpServerType FtpDirectoryListingParserVms::GetServerType() const {
- return SERVER_VMS;
-}
-
-bool FtpDirectoryListingParserVms::ConsumeLine(const string16& line) {
- switch (state_) {
- case STATE_INITIAL:
- DCHECK(last_filename_.empty());
- if (line.empty())
- return true;
- if (StartsWith(line, ASCIIToUTF16("Total of "), true)) {
- state_ = STATE_END;
- return true;
- }
- // We assume that the first non-empty line is the listing header. It often
- // starts with "Directory ", but not always.
- state_ = STATE_RECEIVED_HEADER;
- return true;
- case STATE_RECEIVED_HEADER:
- DCHECK(last_filename_.empty());
- if (line.empty())
- return true;
- state_ = STATE_ENTRIES;
- return ConsumeEntryLine(line);
- case STATE_ENTRIES:
- if (line.empty()) {
- if (!last_filename_.empty())
+bool ParseFtpDirectoryListingVms(
+ const std::vector<string16>& lines,
+ std::vector<FtpDirectoryListingEntry>* entries) {
+ // The first non-empty line is the listing header. It often
+ // starts with "Directory ", but not always. We set a flag after
+ // seing the header.
+ bool seen_header = false;
+
+ for (size_t i = 0; i < lines.size(); i++) {
+ if (lines[i].empty())
+ continue;
+
+ if (StartsWith(lines[i], ASCIIToUTF16("Total of "), true)) {
+ // After the "total" line, all following lines must be empty.
+ for (size_t j = i + 1; j < lines.size(); j++)
+ if (!lines[j].empty())
return false;
- state_ = STATE_RECEIVED_LAST_ENTRY;
- return true;
- }
- return ConsumeEntryLine(line);
- case STATE_RECEIVED_LAST_ENTRY:
- DCHECK(last_filename_.empty());
- if (line.empty())
- return true;
- if (!StartsWith(line, ASCIIToUTF16("Total of "), true))
- return false;
- state_ = STATE_END;
+
return true;
- case STATE_END:
- DCHECK(last_filename_.empty());
- return false;
- default:
- NOTREACHED();
- return false;
- }
-}
+ }
-bool FtpDirectoryListingParserVms::OnEndOfInput() {
- return (state_ == STATE_END);
-}
+ if (!seen_header) {
+ seen_header = true;
+ continue;
+ }
-bool FtpDirectoryListingParserVms::EntryAvailable() const {
- return !entries_.empty();
-}
+ if (LooksLikePermissionDeniedError(lines[i]))
+ continue;
-FtpDirectoryListingEntry FtpDirectoryListingParserVms::PopEntry() {
- FtpDirectoryListingEntry entry = entries_.front();
- entries_.pop();
- return entry;
-}
+ std::vector<string16> columns;
+ base::SplitString(CollapseWhitespace(lines[i], false), ' ', &columns);
-bool FtpDirectoryListingParserVms::ConsumeEntryLine(const string16& line) {
- std::vector<string16> columns;
- base::SplitString(CollapseWhitespace(line, false), ' ', &columns);
+ if (columns.size() == 1) {
+ // There can be no continuation if the current line is the last one.
+ if (i == lines.size() - 1)
+ return false;
- if (columns.size() == 1) {
- if (!last_filename_.empty())
+ // Join the current and next line and split them into columns.
+ columns.clear();
+ base::SplitString(
+ CollapseWhitespace(lines[i] + ASCIIToUTF16(" ") + lines[i + 1],
+ false),
+ ' ',
+ &columns);
+ i++;
+ }
+
+ FtpDirectoryListingEntry entry;
+ if (!ParseVmsFilename(columns[0], &entry.name, &entry.type))
return false;
- return ParseVmsFilename(columns[0], &last_filename_, &last_is_directory_);
- }
- // Recognize listing entries which generate "access denied" message even when
- // trying to list them. We don't display them in the final listing.
- static const char* kAccessDeniedMessages[] = {
- "%RMS-E-PRV",
- "privilege",
- };
- for (size_t i = 0; i < arraysize(kAccessDeniedMessages); i++) {
- if (line.find(ASCIIToUTF16(kAccessDeniedMessages[i])) != string16::npos) {
- last_filename_.clear();
- last_is_directory_ = false;
- return true;
+ // There are different variants of a VMS listing. Some display
+ // the protection listing and user identification code, some do not.
+ if (columns.size() == 6) {
+ if (!LooksLikeVmsFileProtectionListing(columns[5]))
+ return false;
+ if (!LooksLikeVmsUserIdentificationCode(columns[4]))
+ return false;
+
+ // Drop the unneeded data, so that the following code can always expect
+ // just four columns.
+ columns.resize(4);
}
- }
- string16 filename;
- bool is_directory = false;
- if (last_filename_.empty()) {
- if (!ParseVmsFilename(columns[0], &filename, &is_directory))
+ if (columns.size() != 4)
return false;
- columns.erase(columns.begin());
- } else {
- filename = last_filename_;
- is_directory = last_is_directory_;
- last_filename_.clear();
- last_is_directory_ = false;
- }
-
- if (columns.size() > 5)
- return false;
- if (columns.size() == 5) {
- if (!LooksLikeVmsFileProtectionListing(columns[4]))
+ if (!ParseVmsFilesize(columns[1], &entry.size))
return false;
- if (!LooksLikeVmsUserIdentificationCode(columns[3]))
+ if (entry.size < 0)
+ return false;
+ if (entry.type != FtpDirectoryListingEntry::FILE)
+ entry.size = -1;
+ if (!VmsDateListingToTime(columns, &entry.last_modified))
return false;
- columns.resize(3);
- }
-
- if (columns.size() != 3)
- return false;
- FtpDirectoryListingEntry entry;
- entry.name = filename;
- entry.type = is_directory ? FtpDirectoryListingEntry::DIRECTORY
- : FtpDirectoryListingEntry::FILE;
- if (!ParseVmsFilesize(columns[0], &entry.size))
- return false;
- if (entry.size < 0)
- return false;
- if (entry.type != FtpDirectoryListingEntry::FILE)
- entry.size = -1;
- if (!VmsDateListingToTime(columns, &entry.last_modified))
- return false;
+ entries->push_back(entry);
+ }
- entries_.push(entry);
- return true;
+ // The only place where we return true is after receiving the "Total" line,
+ // that should be present in every VMS listing.
+ return false;
}
} // namespace net
diff --git a/net/ftp/ftp_directory_listing_parser_vms.h b/net/ftp/ftp_directory_listing_parser_vms.h
index 6f7fb73..8986fcb 100644
--- a/net/ftp/ftp_directory_listing_parser_vms.h
+++ b/net/ftp/ftp_directory_listing_parser_vms.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -6,60 +6,18 @@
#define NET_FTP_FTP_DIRECTORY_LISTING_PARSER_VMS_H_
#pragma once
-#include <queue>
+#include <vector>
-#include "net/ftp/ftp_directory_listing_parser.h"
+#include "base/string16.h"
namespace net {
-// Parser for VMS-style directory listing (including variants).
-class FtpDirectoryListingParserVms : public FtpDirectoryListingParser {
- public:
- FtpDirectoryListingParserVms();
- virtual ~FtpDirectoryListingParserVms();
+struct FtpDirectoryListingEntry;
- // FtpDirectoryListingParser methods:
- virtual FtpServerType GetServerType() const;
- virtual bool ConsumeLine(const string16& line);
- virtual bool OnEndOfInput();
- virtual bool EntryAvailable() const;
- virtual FtpDirectoryListingEntry PopEntry();
-
- private:
- enum State {
- STATE_INITIAL,
-
- // Indicates that we have received the header, like this:
- // Directory SYS$SYSDEVICE:[ANONYMOUS]
- STATE_RECEIVED_HEADER,
-
- // Indicates that we have received the first listing entry, like this:
- // MADGOAT.DIR;1 2 9-MAY-2001 22:23:44.85
- STATE_ENTRIES,
-
- // Indicates that we have received the last listing entry.
- STATE_RECEIVED_LAST_ENTRY,
-
- // Indicates that we have successfully received all parts of the listing.
- STATE_END,
- };
-
- // Consumes listing line which is expected to be a directory listing entry
- // (and not a comment etc). Returns true on success.
- bool ConsumeEntryLine(const string16& line);
-
- State state_;
-
- // VMS can use two physical lines if the filename is long. The first line will
- // contain the filename, and the second line everything else. Store the
- // filename until we receive the next line.
- string16 last_filename_;
- bool last_is_directory_;
-
- std::queue<FtpDirectoryListingEntry> entries_;
-
- DISALLOW_COPY_AND_ASSIGN(FtpDirectoryListingParserVms);
-};
+// Parses VMS FTP directory listing. Returns true on success.
+bool ParseFtpDirectoryListingVms(
+ const std::vector<string16>& lines,
+ std::vector<FtpDirectoryListingEntry>* entries);
} // namespace net
diff --git a/net/ftp/ftp_directory_listing_parser_vms_unittest.cc b/net/ftp/ftp_directory_listing_parser_vms_unittest.cc
index eb1554d..6643e16 100644
--- a/net/ftp/ftp_directory_listing_parser_vms_unittest.cc
+++ b/net/ftp/ftp_directory_listing_parser_vms_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -11,113 +11,154 @@
#include "base/utf_string_conversions.h"
#include "net/ftp/ftp_directory_listing_parser_vms.h"
+namespace net {
+
namespace {
-typedef net::FtpDirectoryListingParserTest FtpDirectoryListingParserVmsTest;
+typedef FtpDirectoryListingParserTest FtpDirectoryListingParserVmsTest;
TEST_F(FtpDirectoryListingParserVmsTest, Good) {
const struct SingleLineTestData good_cases[] = {
{ "README.TXT;4 2 18-APR-2000 10:40:39.90",
- net::FtpDirectoryListingEntry::FILE, "readme.txt", 1024,
+ FtpDirectoryListingEntry::FILE, "readme.txt", 1024,
2000, 4, 18, 10, 40 },
{ ".WELCOME;1 2 13-FEB-2002 23:32:40.47",
- net::FtpDirectoryListingEntry::FILE, ".welcome", 1024,
+ FtpDirectoryListingEntry::FILE, ".welcome", 1024,
2002, 2, 13, 23, 32 },
{ "FILE.;1 2 13-FEB-2002 23:32:40.47",
- net::FtpDirectoryListingEntry::FILE, "file.", 1024,
+ FtpDirectoryListingEntry::FILE, "file.", 1024,
2002, 2, 13, 23, 32 },
{ "EXAMPLE.TXT;1 1 4-NOV-2009 06:02 [JOHNDOE] (RWED,RWED,,)",
- net::FtpDirectoryListingEntry::FILE, "example.txt", 512,
+ FtpDirectoryListingEntry::FILE, "example.txt", 512,
2009, 11, 4, 6, 2 },
{ "ANNOUNCE.TXT;2 1/16 12-MAR-2005 08:44:57 [SYSTEM] (RWED,RWED,RE,RE)",
- net::FtpDirectoryListingEntry::FILE, "announce.txt", 512,
+ FtpDirectoryListingEntry::FILE, "announce.txt", 512,
2005, 3, 12, 8, 44 },
{ "TEST.DIR;1 1 4-MAR-1999 22:14:34 [UCX$NOBO,ANONYMOUS] (RWE,RWE,RWE,RWE)",
- net::FtpDirectoryListingEntry::DIRECTORY, "test", -1,
+ FtpDirectoryListingEntry::DIRECTORY, "test", -1,
1999, 3, 4, 22, 14 },
{ "ANNOUNCE.TXT;2 1 12-MAR-2005 08:44:57 [X] (,,,)",
- net::FtpDirectoryListingEntry::FILE, "announce.txt", 512,
+ FtpDirectoryListingEntry::FILE, "announce.txt", 512,
2005, 3, 12, 8, 44 },
{ "ANNOUNCE.TXT;2 1 12-MAR-2005 08:44:57 [X] (R,RW,RWD,RE)",
- net::FtpDirectoryListingEntry::FILE, "announce.txt", 512,
+ FtpDirectoryListingEntry::FILE, "announce.txt", 512,
2005, 3, 12, 8, 44 },
{ "ANNOUNCE.TXT;2 1 12-MAR-2005 08:44:57 [X] (ED,RED,WD,WED)",
- net::FtpDirectoryListingEntry::FILE, "announce.txt", 512,
+ FtpDirectoryListingEntry::FILE, "announce.txt", 512,
2005, 3, 12, 8, 44 },
};
for (size_t i = 0; i < arraysize(good_cases); i++) {
SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]: %s", i,
good_cases[i].input));
- net::FtpDirectoryListingParserVms parser;
- ASSERT_TRUE(
- parser.ConsumeLine(ASCIIToUTF16("Directory ANONYMOUS_ROOT:[000000]")));
- RunSingleLineTestCase(&parser, good_cases[i]);
+ std::vector<string16> lines(GetSingleLineTestCase(good_cases[i].input));
+
+ // The parser requires a directory header before accepting regular input.
+ lines.insert(lines.begin(),
+ ASCIIToUTF16("Directory ANONYMOUS_ROOT:[000000]"));
+
+ // A valid listing must also have a "Total" line at the end.
+ lines.insert(lines.end(),
+ ASCIIToUTF16("Total of 1 file, 2 blocks."));
+
+ std::vector<FtpDirectoryListingEntry> entries;
+ EXPECT_TRUE(ParseFtpDirectoryListingVms(lines,
+ &entries));
+ VerifySingleLineTestCase(good_cases[i], entries);
}
}
TEST_F(FtpDirectoryListingParserVmsTest, Bad) {
const char* bad_cases[] = {
- "Directory ROOT|garbage",
+ "garbage",
// Missing file version number.
- "Directory ROOT|README.TXT 2 18-APR-2000 10:40:39",
+ "README.TXT 2 18-APR-2000 10:40:39",
// Missing extension.
- "Directory ROOT|README;1 2 18-APR-2000 10:40:39",
+ "README;1 2 18-APR-2000 10:40:39",
// Malformed file size.
- "Directory ROOT|README.TXT;1 garbage 18-APR-2000 10:40:39",
- "Directory ROOT|README.TXT;1 -2 18-APR-2000 10:40:39",
+ "README.TXT;1 garbage 18-APR-2000 10:40:39",
+ "README.TXT;1 -2 18-APR-2000 10:40:39",
// Malformed date.
- "Directory ROOT|README.TXT;1 2 APR-2000 10:40:39",
- "Directory ROOT|README.TXT;1 2 -18-APR-2000 10:40:39",
- "Directory ROOT|README.TXT;1 2 18-APR 10:40:39",
- "Directory ROOT|README.TXT;1 2 18-APR-2000 10",
- "Directory ROOT|README.TXT;1 2 18-APR-2000 10:40.25",
- "Directory ROOT|README.TXT;1 2 18-APR-2000 10:40.25.25",
-
- // Empty line inside the listing.
- "Directory ROOT|README.TXT;1 2 18-APR-2000 10:40:42"
- "||README.TXT;1 2 18-APR-2000 10:40:42",
-
- // Data after footer.
- "Directory ROOT|README.TXT;4 2 18-APR-2000 10:40:39"
- "||Total of 1 file|",
- "Directory ROOT|README.TXT;4 2 18-APR-2000 10:40:39"
- "||Total of 1 file|garbage",
- "Directory ROOT|README.TXT;4 2 18-APR-2000 10:40:39"
- "||Total of 1 file|Total of 1 file",
+ "README.TXT;1 2 APR-2000 10:40:39",
+ "README.TXT;1 2 -18-APR-2000 10:40:39",
+ "README.TXT;1 2 18-APR 10:40:39",
+ "README.TXT;1 2 18-APR-2000 10",
+ "README.TXT;1 2 18-APR-2000 10:40.25",
+ "README.TXT;1 2 18-APR-2000 10:40.25.25",
// Malformed security information.
- "Directory ROOT|X.TXT;2 1 12-MAR-2005 08:44:57 (RWED,RWED,RE,RE)",
- "Directory ROOT|X.TXT;2 1 12-MAR-2005 08:44:57 [SYSTEM]",
- "Directory ROOT|X.TXT;2 1 12-MAR-2005 08:44:57 (SYSTEM) (RWED,RWED,RE,RE)",
- "Directory ROOT|X.TXT;2 1 12-MAR-2005 08:44:57 [SYSTEM] [RWED,RWED,RE,RE]",
- "Directory ROOT|X.TXT;2 1 12-MAR-2005 08:44:57 [X] (RWED)",
- "Directory ROOT|X.TXT;2 1 12-MAR-2005 08:44:57 [X] (RWED,RWED,RE,RE,RE)",
- "Directory ROOT|X.TXT;2 1 12-MAR-2005 08:44:57 [X] (RWED,RWEDRWED,RE,RE)",
- "Directory ROOT|X.TXT;2 1 12-MAR-2005 08:44:57 [X] (RWED,DEWR,RE,RE)",
- "Directory ROOT|X.TXT;2 1 12-MAR-2005 08:44:57 [X] (RWED,RWED,Q,RE)",
- "Directory ROOT|X.TXT;2 1 12-MAR-2005 08:44:57 [X] (RWED,RRWWEEDD,RE,RE)",
+ "X.TXT;2 1 12-MAR-2005 08:44:57 (RWED,RWED,RE,RE)",
+ "X.TXT;2 1 12-MAR-2005 08:44:57 [SYSTEM]",
+ "X.TXT;2 1 12-MAR-2005 08:44:57 (SYSTEM) (RWED,RWED,RE,RE)",
+ "X.TXT;2 1 12-MAR-2005 08:44:57 [SYSTEM] [RWED,RWED,RE,RE]",
+ "X.TXT;2 1 12-MAR-2005 08:44:57 [X] (RWED)",
+ "X.TXT;2 1 12-MAR-2005 08:44:57 [X] (RWED,RWED,RE,RE,RE)",
+ "X.TXT;2 1 12-MAR-2005 08:44:57 [X] (RWED,RWEDRWED,RE,RE)",
+ "X.TXT;2 1 12-MAR-2005 08:44:57 [X] (RWED,DEWR,RE,RE)",
+ "X.TXT;2 1 12-MAR-2005 08:44:57 [X] (RWED,RWED,Q,RE)",
+ "X.TXT;2 1 12-MAR-2005 08:44:57 [X] (RWED,RRWWEEDD,RE,RE)",
};
for (size_t i = 0; i < arraysize(bad_cases); i++) {
SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]: %s", i, bad_cases[i]));
- std::vector<std::string> lines;
- base::SplitString(bad_cases[i], '|', &lines);
- net::FtpDirectoryListingParserVms parser;
- bool failed = false;
- for (std::vector<std::string>::const_iterator i = lines.begin();
- i != lines.end(); ++i) {
- if (!parser.ConsumeLine(UTF8ToUTF16(*i))) {
- failed = true;
- break;
- }
+ std::vector<string16> lines(GetSingleLineTestCase(bad_cases[i]));
+
+ // The parser requires a directory header before accepting regular input.
+ lines.insert(lines.begin(),
+ ASCIIToUTF16("Directory ANONYMOUS_ROOT:[000000]"));
+
+ // A valid listing must also have a "Total" line at the end.
+ lines.insert(lines.end(),
+ ASCIIToUTF16("Total of 1 file, 2 blocks."));
+
+ std::vector<FtpDirectoryListingEntry> entries;
+ EXPECT_FALSE(ParseFtpDirectoryListingVms(lines,
+ &entries));
+ }
+}
+
+TEST_F(FtpDirectoryListingParserVmsTest, BadDataAfterFooter) {
+ const char* bad_cases[] = {
+ "garbage",
+ "Total of 1 file, 2 blocks.",
+ "Directory ANYNYMOUS_ROOT:[000000]",
+ };
+ for (size_t i = 0; i < arraysize(bad_cases); i++) {
+ SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]: %s", i, bad_cases[i]));
+
+ std::vector<string16> lines(
+ GetSingleLineTestCase("README.TXT;4 2 18-APR-2000 10:40:39.90"));
+
+ // The parser requires a directory header before accepting regular input.
+ lines.insert(lines.begin(),
+ ASCIIToUTF16("Directory ANONYMOUS_ROOT:[000000]"));
+
+ // A valid listing must also have a "Total" line at the end.
+ lines.insert(lines.end(),
+ ASCIIToUTF16("Total of 1 file, 2 blocks."));
+
+ {
+ // Make sure the listing is valid before we add data after footer.
+ std::vector<FtpDirectoryListingEntry> entries;
+ EXPECT_TRUE(ParseFtpDirectoryListingVms(lines,
+ &entries));
+ }
+
+ {
+ // Insert a line at the end of the listing that should make it invalid.
+ lines.insert(lines.end(),
+ ASCIIToUTF16(bad_cases[i]));
+ std::vector<FtpDirectoryListingEntry> entries;
+ EXPECT_FALSE(ParseFtpDirectoryListingVms(lines,
+ &entries));
}
- EXPECT_TRUE(failed);
}
}
} // namespace
+
+} // namespace net
diff --git a/net/ftp/ftp_directory_listing_parser_windows.cc b/net/ftp/ftp_directory_listing_parser_windows.cc
index ef733d5..2317fa3 100644
--- a/net/ftp/ftp_directory_listing_parser_windows.cc
+++ b/net/ftp/ftp_directory_listing_parser_windows.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -9,6 +9,8 @@
#include "base/string_number_conversions.h"
#include "base/string_split.h"
#include "base/string_util.h"
+#include "base/time.h"
+#include "net/ftp/ftp_directory_listing_parser.h"
#include "net/ftp/ftp_util.h"
namespace {
@@ -72,68 +74,55 @@ bool WindowsDateListingToTime(const std::vector<string16>& columns,
namespace net {
-FtpDirectoryListingParserWindows::FtpDirectoryListingParserWindows() {}
-
-FtpDirectoryListingParserWindows::~FtpDirectoryListingParserWindows() {}
-
-FtpServerType FtpDirectoryListingParserWindows::GetServerType() const {
- return SERVER_WINDOWS;
-}
-
-bool FtpDirectoryListingParserWindows::ConsumeLine(const string16& line) {
- std::vector<string16> columns;
- base::SplitString(CollapseWhitespace(line, false), ' ', &columns);
-
- // Every line of the listing consists of the following:
- //
- // 1. date
- // 2. time
- // 3. size in bytes (or "<DIR>" for directories)
- // 4. filename (may be empty or contain spaces)
- //
- // For now, make sure we have 1-3, and handle 4 later.
- if (columns.size() < 3)
- return false;
-
- FtpDirectoryListingEntry entry;
- if (EqualsASCII(columns[2], "<DIR>")) {
- entry.type = FtpDirectoryListingEntry::DIRECTORY;
- entry.size = -1;
- } else {
- entry.type = FtpDirectoryListingEntry::FILE;
- if (!base::StringToInt64(columns[2], &entry.size))
+bool ParseFtpDirectoryListingWindows(
+ const std::vector<string16>& lines,
+ std::vector<FtpDirectoryListingEntry>* entries) {
+ for (size_t i = 0; i < lines.size(); i++) {
+ if (lines[i].empty())
+ continue;
+
+ std::vector<string16> columns;
+ base::SplitString(CollapseWhitespace(lines[i], false), ' ', &columns);
+
+ // Every line of the listing consists of the following:
+ //
+ // 1. date
+ // 2. time
+ // 3. size in bytes (or "<DIR>" for directories)
+ // 4. filename (may be empty or contain spaces)
+ //
+ // For now, make sure we have 1-3, and handle 4 later.
+ if (columns.size() < 3)
return false;
- if (entry.size < 0)
+
+ FtpDirectoryListingEntry entry;
+ if (EqualsASCII(columns[2], "<DIR>")) {
+ entry.type = FtpDirectoryListingEntry::DIRECTORY;
+ entry.size = -1;
+ } else {
+ entry.type = FtpDirectoryListingEntry::FILE;
+ if (!base::StringToInt64(columns[2], &entry.size))
+ return false;
+ if (entry.size < 0)
+ return false;
+ }
+
+ if (!WindowsDateListingToTime(columns, &entry.last_modified))
return false;
- }
- if (!WindowsDateListingToTime(columns, &entry.last_modified))
- return false;
+ entry.name = FtpUtil::GetStringPartAfterColumns(lines[i], 3);
+ if (entry.name.empty()) {
+ // Some FTP servers send listing entries with empty names.
+ // It's not obvious how to display such an entry, so ignore them.
+ // We don't want to make the parsing fail at this point though.
+ // Other entries can still be useful.
+ continue;
+ }
- entry.name = FtpUtil::GetStringPartAfterColumns(line, 3);
- if (entry.name.empty()) {
- // Some FTP servers send listing entries with empty names. It's not obvious
- // how to display such an entry, so we ignore them. We don't want to make
- // the parsing fail at this point though. Other entries can still be useful.
- return true;
+ entries->push_back(entry);
}
- entries_.push(entry);
- return true;
-}
-
-bool FtpDirectoryListingParserWindows::OnEndOfInput() {
return true;
}
-bool FtpDirectoryListingParserWindows::EntryAvailable() const {
- return !entries_.empty();
-}
-
-FtpDirectoryListingEntry FtpDirectoryListingParserWindows::PopEntry() {
- FtpDirectoryListingEntry entry = entries_.front();
- entries_.pop();
- return entry;
-}
-
} // namespace net
diff --git a/net/ftp/ftp_directory_listing_parser_windows.h b/net/ftp/ftp_directory_listing_parser_windows.h
index 1f029af..581e08c 100644
--- a/net/ftp/ftp_directory_listing_parser_windows.h
+++ b/net/ftp/ftp_directory_listing_parser_windows.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -6,29 +6,18 @@
#define NET_FTP_FTP_DIRECTORY_LISTING_PARSER_WINDOWS_H_
#pragma once
-#include <queue>
+#include <vector>
-#include "net/ftp/ftp_directory_listing_parser.h"
+#include "base/string16.h"
namespace net {
-class FtpDirectoryListingParserWindows : public FtpDirectoryListingParser {
- public:
- FtpDirectoryListingParserWindows();
- virtual ~FtpDirectoryListingParserWindows();
+struct FtpDirectoryListingEntry;
- // FtpDirectoryListingParser methods:
- virtual FtpServerType GetServerType() const;
- virtual bool ConsumeLine(const string16& line);
- virtual bool OnEndOfInput();
- virtual bool EntryAvailable() const;
- virtual FtpDirectoryListingEntry PopEntry();
-
- private:
- std::queue<FtpDirectoryListingEntry> entries_;
-
- DISALLOW_COPY_AND_ASSIGN(FtpDirectoryListingParserWindows);
-};
+// Parses Windows FTP directory listing. Returns true on success.
+bool ParseFtpDirectoryListingWindows(
+ const std::vector<string16>& lines,
+ std::vector<FtpDirectoryListingEntry>* entries);
} // namespace net
diff --git a/net/ftp/ftp_directory_listing_parser_windows_unittest.cc b/net/ftp/ftp_directory_listing_parser_windows_unittest.cc
index aeb64e7..7517baf 100644
--- a/net/ftp/ftp_directory_listing_parser_windows_unittest.cc
+++ b/net/ftp/ftp_directory_listing_parser_windows_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -9,58 +9,63 @@
#include "base/stringprintf.h"
#include "net/ftp/ftp_directory_listing_parser_windows.h"
+namespace net {
+
namespace {
-typedef net::FtpDirectoryListingParserTest FtpDirectoryListingParserWindowsTest;
+typedef FtpDirectoryListingParserTest FtpDirectoryListingParserWindowsTest;
TEST_F(FtpDirectoryListingParserWindowsTest, Good) {
const struct SingleLineTestData good_cases[] = {
{ "11-02-09 05:32PM <DIR> NT",
- net::FtpDirectoryListingEntry::DIRECTORY, "NT", -1,
+ FtpDirectoryListingEntry::DIRECTORY, "NT", -1,
2009, 11, 2, 17, 32 },
{ "01-06-09 02:42PM 458 Readme.txt",
- net::FtpDirectoryListingEntry::FILE, "Readme.txt", 458,
+ FtpDirectoryListingEntry::FILE, "Readme.txt", 458,
2009, 1, 6, 14, 42 },
{ "01-06-09 02:42AM 1 Readme.txt",
- net::FtpDirectoryListingEntry::FILE, "Readme.txt", 1,
+ FtpDirectoryListingEntry::FILE, "Readme.txt", 1,
2009, 1, 6, 2, 42 },
{ "01-06-01 02:42AM 458 Readme.txt",
- net::FtpDirectoryListingEntry::FILE, "Readme.txt", 458,
+ FtpDirectoryListingEntry::FILE, "Readme.txt", 458,
2001, 1, 6, 2, 42 },
{ "01-06-00 02:42AM 458 Corner1.txt",
- net::FtpDirectoryListingEntry::FILE, "Corner1.txt", 458,
+ FtpDirectoryListingEntry::FILE, "Corner1.txt", 458,
2000, 1, 6, 2, 42 },
{ "01-06-99 02:42AM 458 Corner2.txt",
- net::FtpDirectoryListingEntry::FILE, "Corner2.txt", 458,
+ FtpDirectoryListingEntry::FILE, "Corner2.txt", 458,
1999, 1, 6, 2, 42 },
{ "01-06-80 02:42AM 458 Corner3.txt",
- net::FtpDirectoryListingEntry::FILE, "Corner3.txt", 458,
+ FtpDirectoryListingEntry::FILE, "Corner3.txt", 458,
1980, 1, 6, 2, 42 },
#if !defined(OS_LINUX)
// TODO(phajdan.jr): Re-enable when 2038-year problem is fixed on Linux.
{ "01-06-79 02:42AM 458 Corner4",
- net::FtpDirectoryListingEntry::FILE, "Corner4", 458,
+ FtpDirectoryListingEntry::FILE, "Corner4", 458,
2079, 1, 6, 2, 42 },
#endif // !defined (OS_LINUX)
{ "01-06-1979 02:42AM 458 Readme.txt",
- net::FtpDirectoryListingEntry::FILE, "Readme.txt", 458,
+ FtpDirectoryListingEntry::FILE, "Readme.txt", 458,
1979, 1, 6, 2, 42 },
{ "11-02-09 05:32PM <DIR> My Directory",
- net::FtpDirectoryListingEntry::DIRECTORY, "My Directory", -1,
+ FtpDirectoryListingEntry::DIRECTORY, "My Directory", -1,
2009, 11, 2, 17, 32 },
{ "12-25-10 12:00AM <DIR> Christmas Midnight",
- net::FtpDirectoryListingEntry::DIRECTORY, "Christmas Midnight", -1,
+ FtpDirectoryListingEntry::DIRECTORY, "Christmas Midnight", -1,
2010, 12, 25, 0, 0 },
{ "12-25-10 12:00PM <DIR> Christmas Midday",
- net::FtpDirectoryListingEntry::DIRECTORY, "Christmas Midday", -1,
+ FtpDirectoryListingEntry::DIRECTORY, "Christmas Midday", -1,
2010, 12, 25, 12, 0 },
};
for (size_t i = 0; i < arraysize(good_cases); i++) {
SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]: %s", i,
good_cases[i].input));
- net::FtpDirectoryListingParserWindows parser;
- RunSingleLineTestCase(&parser, good_cases[i]);
+ std::vector<FtpDirectoryListingEntry> entries;
+ EXPECT_TRUE(ParseFtpDirectoryListingWindows(
+ GetSingleLineTestCase(good_cases[i].input),
+ &entries));
+ VerifySingleLineTestCase(good_cases[i], entries);
}
}
@@ -73,17 +78,16 @@ TEST_F(FtpDirectoryListingParserWindowsTest, Ignored) {
SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]: %s", i,
ignored_cases[i]));
- net::FtpDirectoryListingParserWindows parser;
- EXPECT_TRUE(parser.ConsumeLine(UTF8ToUTF16(ignored_cases[i])));
- EXPECT_FALSE(parser.EntryAvailable());
- EXPECT_TRUE(parser.OnEndOfInput());
- EXPECT_FALSE(parser.EntryAvailable());
+ std::vector<FtpDirectoryListingEntry> entries;
+ EXPECT_TRUE(ParseFtpDirectoryListingWindows(
+ GetSingleLineTestCase(ignored_cases[i]),
+ &entries));
+ EXPECT_EQ(0U, entries.size());
}
}
TEST_F(FtpDirectoryListingParserWindowsTest, Bad) {
const char* bad_cases[] = {
- "",
"garbage",
"11-02-09 05:32PM <GARBAGE>",
"11-02-09 05:32PM <GARBAGE> NT",
@@ -107,9 +111,16 @@ TEST_F(FtpDirectoryListingParserWindowsTest, Bad) {
"12-25-10 12:00ZM 0 what does ZM mean",
};
for (size_t i = 0; i < arraysize(bad_cases); i++) {
- net::FtpDirectoryListingParserWindows parser;
- EXPECT_FALSE(parser.ConsumeLine(UTF8ToUTF16(bad_cases[i]))) << bad_cases[i];
+ SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]: %s", i,
+ bad_cases[i]));
+
+ std::vector<FtpDirectoryListingEntry> entries;
+ EXPECT_FALSE(ParseFtpDirectoryListingWindows(
+ GetSingleLineTestCase(bad_cases[i]),
+ &entries));
}
}
} // namespace
+
+} // namespace net
diff --git a/net/net.gyp b/net/net.gyp
index 4bb30b5..e3837de 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -426,8 +426,6 @@
'ftp/ftp_auth_cache.h',
'ftp/ftp_ctrl_response_buffer.cc',
'ftp/ftp_ctrl_response_buffer.h',
- 'ftp/ftp_directory_listing_buffer.cc',
- 'ftp/ftp_directory_listing_buffer.h',
'ftp/ftp_directory_listing_parser.cc',
'ftp/ftp_directory_listing_parser.h',
'ftp/ftp_directory_listing_parser_ls.cc',
@@ -931,9 +929,9 @@
'disk_cache/storage_block_unittest.cc',
'ftp/ftp_auth_cache_unittest.cc',
'ftp/ftp_ctrl_response_buffer_unittest.cc',
- 'ftp/ftp_directory_listing_buffer_unittest.cc',
'ftp/ftp_directory_listing_parser_ls_unittest.cc',
'ftp/ftp_directory_listing_parser_netware_unittest.cc',
+ 'ftp/ftp_directory_listing_parser_unittest.cc',
'ftp/ftp_directory_listing_parser_vms_unittest.cc',
'ftp/ftp_directory_listing_parser_windows_unittest.cc',
'ftp/ftp_network_transaction_unittest.cc',