| // 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_hprc.h" |
| #include "net/ftp/ftp_directory_listing_parser_ls.h" |
| #include "net/ftp/ftp_directory_listing_parser_mlsd.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 FtpDirectoryListingParserHprc(current_time)); |
| parsers_.insert(new FtpDirectoryListingParserLs(current_time)); |
| parsers_.insert(new FtpDirectoryListingParserMlsd()); |
| 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 = ExtractFullLinesFromBuffer(); |
| if (rv != OK) |
| return rv; |
| } |
| |
| return ParseLines(); |
| } |
| |
| int FtpDirectoryListingBuffer::ProcessRemainingData() { |
| int rv = ExtractFullLinesFromBuffer(); |
| if (rv != OK) |
| return rv; |
| |
| if (!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); |
| } |
| |
| bool FtpDirectoryListingBuffer::ConvertToDetectedEncoding( |
| const std::string& from, string16* to) { |
| std::string encoding(encoding_.empty() ? "ascii" : encoding_); |
| return base::CodepageToUTF16(from, encoding.c_str(), |
| base::OnStringConversionError::FAIL, to); |
| } |
| |
| int FtpDirectoryListingBuffer::ExtractFullLinesFromBuffer() { |
| if (encoding_.empty()) { |
| if (!base::DetectEncoding(buffer_, &encoding_)) |
| return ERR_ENCODING_DETECTION_FAILED; |
| } |
| |
| 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 < buffer_.length(); ++i) { |
| if (buffer_[i] != '\n') |
| continue; |
| int line_length = i - cut_pos; |
| if (i >= 1 && buffer_[i - 1] == '\r') |
| line_length--; |
| std::string line(buffer_.substr(cut_pos, line_length)); |
| cut_pos = i + 1; |
| string16 line_converted; |
| if (!ConvertToDetectedEncoding(line, &line_converted)) { |
| buffer_.erase(0, cut_pos); |
| return ERR_ENCODING_CONVERSION_FAILED; |
| } |
| lines_.push_back(line_converted); |
| } |
| buffer_.erase(0, cut_pos); |
| 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 |