| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "net/ftp/ftp_directory_listing_buffer.h" | |
| 6 | |
| 7 #include "base/i18n/icu_encoding_detection.h" | |
| 8 #include "base/i18n/icu_string_conversions.h" | |
| 9 #include "base/stl_util-inl.h" | |
| 10 #include "base/string_util.h" | |
| 11 #include "net/base/net_errors.h" | |
| 12 #include "net/ftp/ftp_directory_listing_parser_ls.h" | |
| 13 #include "net/ftp/ftp_directory_listing_parser_netware.h" | |
| 14 #include "net/ftp/ftp_directory_listing_parser_vms.h" | |
| 15 #include "net/ftp/ftp_directory_listing_parser_windows.h" | |
| 16 | |
| 17 namespace net { | |
| 18 | |
| 19 FtpDirectoryListingBuffer::FtpDirectoryListingBuffer( | |
| 20 const base::Time& current_time) | |
| 21 : current_parser_(NULL) { | |
| 22 parsers_.insert(new FtpDirectoryListingParserLs(current_time)); | |
| 23 parsers_.insert(new FtpDirectoryListingParserNetware(current_time)); | |
| 24 parsers_.insert(new FtpDirectoryListingParserVms()); | |
| 25 parsers_.insert(new FtpDirectoryListingParserWindows()); | |
| 26 } | |
| 27 | |
| 28 FtpDirectoryListingBuffer::~FtpDirectoryListingBuffer() { | |
| 29 STLDeleteElements(&parsers_); | |
| 30 } | |
| 31 | |
| 32 int FtpDirectoryListingBuffer::ConsumeData(const char* data, int data_length) { | |
| 33 buffer_.append(data, data_length); | |
| 34 | |
| 35 if (!encoding_.empty() || buffer_.length() > 1024) { | |
| 36 int rv = ConsumeBuffer(); | |
| 37 if (rv != OK) | |
| 38 return rv; | |
| 39 } | |
| 40 | |
| 41 return ParseLines(); | |
| 42 } | |
| 43 | |
| 44 int FtpDirectoryListingBuffer::ProcessRemainingData() { | |
| 45 int rv = ConsumeBuffer(); | |
| 46 if (rv != OK) | |
| 47 return rv; | |
| 48 | |
| 49 DCHECK(buffer_.empty()); | |
| 50 if (!converted_buffer_.empty()) | |
| 51 return ERR_INVALID_RESPONSE; | |
| 52 | |
| 53 rv = ParseLines(); | |
| 54 if (rv != OK) | |
| 55 return rv; | |
| 56 | |
| 57 rv = OnEndOfInput(); | |
| 58 if (rv != OK) | |
| 59 return rv; | |
| 60 | |
| 61 return OK; | |
| 62 } | |
| 63 | |
| 64 bool FtpDirectoryListingBuffer::EntryAvailable() const { | |
| 65 return (current_parser_ ? current_parser_->EntryAvailable() : false); | |
| 66 } | |
| 67 | |
| 68 FtpDirectoryListingEntry FtpDirectoryListingBuffer::PopEntry() { | |
| 69 DCHECK(EntryAvailable()); | |
| 70 return current_parser_->PopEntry(); | |
| 71 } | |
| 72 | |
| 73 FtpServerType FtpDirectoryListingBuffer::GetServerType() const { | |
| 74 return (current_parser_ ? current_parser_->GetServerType() : SERVER_UNKNOWN); | |
| 75 } | |
| 76 | |
| 77 int FtpDirectoryListingBuffer::DecodeBufferUsingEncoding( | |
| 78 const std::string& encoding) { | |
| 79 string16 converted; | |
| 80 if (!base::CodepageToUTF16(buffer_, | |
| 81 encoding.c_str(), | |
| 82 base::OnStringConversionError::FAIL, | |
| 83 &converted)) | |
| 84 return ERR_ENCODING_CONVERSION_FAILED; | |
| 85 | |
| 86 buffer_.clear(); | |
| 87 converted_buffer_ += converted; | |
| 88 return OK; | |
| 89 } | |
| 90 | |
| 91 int FtpDirectoryListingBuffer::ConvertBufferToUTF16() { | |
| 92 if (encoding_.empty()) { | |
| 93 std::vector<std::string> encodings; | |
| 94 if (!base::DetectAllEncodings(buffer_, &encodings)) | |
| 95 return ERR_ENCODING_DETECTION_FAILED; | |
| 96 | |
| 97 // Use first encoding that can be used to decode the buffer. | |
| 98 for (size_t i = 0; i < encodings.size(); i++) { | |
| 99 if (DecodeBufferUsingEncoding(encodings[i]) == OK) { | |
| 100 encoding_ = encodings[i]; | |
| 101 return OK; | |
| 102 } | |
| 103 } | |
| 104 | |
| 105 return ERR_ENCODING_DETECTION_FAILED; | |
| 106 } | |
| 107 | |
| 108 return DecodeBufferUsingEncoding(encoding_); | |
| 109 } | |
| 110 | |
| 111 void FtpDirectoryListingBuffer::ExtractFullLinesFromBuffer() { | |
| 112 int cut_pos = 0; | |
| 113 // TODO(phajdan.jr): This code accepts all endlines matching \r*\n. Should it | |
| 114 // be more strict, or enforce consistent line endings? | |
| 115 for (size_t i = 0; i < converted_buffer_.length(); ++i) { | |
| 116 if (converted_buffer_[i] != '\n') | |
| 117 continue; | |
| 118 int line_length = i - cut_pos; | |
| 119 if (i >= 1 && converted_buffer_[i - 1] == '\r') | |
| 120 line_length--; | |
| 121 lines_.push_back(converted_buffer_.substr(cut_pos, line_length)); | |
| 122 cut_pos = i + 1; | |
| 123 } | |
| 124 converted_buffer_.erase(0, cut_pos); | |
| 125 } | |
| 126 | |
| 127 int FtpDirectoryListingBuffer::ConsumeBuffer() { | |
| 128 int rv = ConvertBufferToUTF16(); | |
| 129 if (rv != OK) | |
| 130 return rv; | |
| 131 | |
| 132 ExtractFullLinesFromBuffer(); | |
| 133 return OK; | |
| 134 } | |
| 135 | |
| 136 int FtpDirectoryListingBuffer::ParseLines() { | |
| 137 while (!lines_.empty()) { | |
| 138 string16 line = lines_.front(); | |
| 139 lines_.pop_front(); | |
| 140 if (current_parser_) { | |
| 141 if (!current_parser_->ConsumeLine(line)) | |
| 142 return ERR_FAILED; | |
| 143 } else { | |
| 144 ParserSet::iterator i = parsers_.begin(); | |
| 145 while (i != parsers_.end()) { | |
| 146 if ((*i)->ConsumeLine(line)) { | |
| 147 i++; | |
| 148 } else { | |
| 149 delete *i; | |
| 150 parsers_.erase(i++); | |
| 151 } | |
| 152 } | |
| 153 if (parsers_.empty()) | |
| 154 return ERR_UNRECOGNIZED_FTP_DIRECTORY_LISTING_FORMAT; | |
| 155 if (parsers_.size() == 1) | |
| 156 current_parser_ = *parsers_.begin(); | |
| 157 } | |
| 158 } | |
| 159 | |
| 160 return OK; | |
| 161 } | |
| 162 | |
| 163 int FtpDirectoryListingBuffer::OnEndOfInput() { | |
| 164 ParserSet::iterator i = parsers_.begin(); | |
| 165 while (i != parsers_.end()) { | |
| 166 if ((*i)->OnEndOfInput()) { | |
| 167 i++; | |
| 168 } else { | |
| 169 delete *i; | |
| 170 parsers_.erase(i++); | |
| 171 } | |
| 172 } | |
| 173 | |
| 174 if (parsers_.size() != 1) { | |
| 175 current_parser_ = NULL; | |
| 176 | |
| 177 // We may hit an ambiguity in case of listings which have no entries. That's | |
| 178 // fine, as long as all remaining parsers agree that the listing is empty. | |
| 179 bool all_listings_empty = true; | |
| 180 for (ParserSet::iterator i = parsers_.begin(); i != parsers_.end(); ++i) { | |
| 181 if ((*i)->EntryAvailable()) | |
| 182 all_listings_empty = false; | |
| 183 } | |
| 184 if (all_listings_empty) | |
| 185 return OK; | |
| 186 | |
| 187 return ERR_UNRECOGNIZED_FTP_DIRECTORY_LISTING_FORMAT; | |
| 188 } | |
| 189 | |
| 190 current_parser_ = *parsers_.begin(); | |
| 191 return OK; | |
| 192 } | |
| 193 | |
| 194 } // namespace net | |
| OLD | NEW |