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 |