diff options
Diffstat (limited to 'net')
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', |