Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(61)

Side by Side Diff: net/ftp/ftp_directory_listing_parser_vms.cc

Issue 465035: Split FTP LIST parsing code into individual files for each listing style. (Closed)
Patch Set: fix Created 11 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. Use of this
2 // source code is governed by a BSD-style license that can be found in the
3 // LICENSE file.
4
5 #include "net/ftp/ftp_directory_listing_parser_vms.h"
6
7 #include <vector>
8
9 #include "base/string_util.h"
10 #include "net/ftp/ftp_util.h"
11
12 namespace {
13
14 // Converts the filename component in listing to the filename we can display.
15 // Returns true on success.
16 bool ParseVmsFilename(const string16& raw_filename, string16* parsed_filename,
17 bool* is_directory) {
18 // On VMS, the files and directories are versioned. The version number is
19 // separated from the file name by a semicolon. Example: ANNOUNCE.TXT;2.
20 std::vector<string16> listing_parts;
21 SplitString(raw_filename, ';', &listing_parts);
22 if (listing_parts.size() != 2)
23 return false;
24 int version_number;
25 if (!StringToInt(listing_parts[1], &version_number))
26 return false;
27 if (version_number < 0)
28 return false;
29
30 // Even directories have extensions in the listings. Don't display extensions
31 // for directories; it's awkward for non-VMS users. Also, VMS is
32 // case-insensitive, but generally uses uppercase characters. This may look
33 // awkward, so we convert them to lower case.
34 std::vector<string16> filename_parts;
35 SplitString(listing_parts[0], '.', &filename_parts);
36 if (filename_parts.size() != 2)
37 return false;
38 if (EqualsASCII(filename_parts[1], "DIR")) {
39 *parsed_filename = StringToLowerASCII(filename_parts[0]);
40 *is_directory = true;
41 } else {
42 *parsed_filename = StringToLowerASCII(listing_parts[0]);
43 *is_directory = false;
44 }
45 return true;
46 }
47
48 bool ParseVmsFilesize(const string16& input, int64* size) {
49 // VMS's directory listing gives us file size in blocks. We assume that
50 // the block size is 512 bytes. It doesn't give accurate file size, but is the
51 // best information we have.
52 const int kBlockSize = 512;
53
54 if (StringToInt64(input, size)) {
55 *size *= kBlockSize;
56 return true;
57 }
58
59 std::vector<string16> parts;
60 SplitString(input, '/', &parts);
61 if (parts.size() != 2)
62 return false;
63
64 int64 blocks_used, blocks_allocated;
65 if (!StringToInt64(parts[0], &blocks_used))
66 return false;
67 if (!StringToInt64(parts[1], &blocks_allocated))
68 return false;
69 if (blocks_used > blocks_allocated)
70 return false;
71
72 *size = blocks_used * kBlockSize;
73 return true;
74 }
75
76 bool LooksLikeVmsFileProtectionListingPart(const string16& input) {
77 if (input.length() > 4)
78 return false;
79
80 // On VMS there are four different permission bits: Read, Write, Execute,
81 // and Delete. They appear in that order in the permission listing.
82 std::string pattern("RWED");
83 string16 match(input);
84 while (!match.empty() && !pattern.empty()) {
85 if (match[0] == pattern[0])
86 match = match.substr(1);
87 pattern = pattern.substr(1);
88 }
89 return match.empty();
90 }
91
92 bool LooksLikeVmsFileProtectionListing(const string16& input) {
93 if (input.length() < 2)
94 return false;
95 if (input[0] != '(' || input[input.length() - 1] != ')')
96 return false;
97
98 // We expect four parts of the file protection listing: for System, Owner,
99 // Group, and World.
100 std::vector<string16> parts;
101 SplitString(input.substr(1, input.length() - 2), ',', &parts);
102 if (parts.size() != 4)
103 return false;
104
105 return LooksLikeVmsFileProtectionListingPart(parts[0]) &&
106 LooksLikeVmsFileProtectionListingPart(parts[1]) &&
107 LooksLikeVmsFileProtectionListingPart(parts[2]) &&
108 LooksLikeVmsFileProtectionListingPart(parts[3]);
109 }
110
111 bool LooksLikeVmsUserIdentificationCode(const string16& input) {
112 if (input.length() < 2)
113 return false;
114 return input[0] == '[' && input[input.length() - 1] == ']';
115 }
116
117 bool VmsDateListingToTime(const std::vector<string16>& columns,
118 base::Time* time) {
119 DCHECK_EQ(3U, columns.size());
120
121 base::Time::Exploded time_exploded = { 0 };
122
123 // Date should be in format DD-MMM-YYYY.
124 std::vector<string16> date_parts;
125 SplitString(columns[1], '-', &date_parts);
126 if (date_parts.size() != 3)
127 return false;
128 if (!StringToInt(date_parts[0], &time_exploded.day_of_month))
129 return false;
130 if (!net::FtpUtil::ThreeLetterMonthToNumber(date_parts[1],
131 &time_exploded.month))
132 return false;
133 if (!StringToInt(date_parts[2], &time_exploded.year))
134 return false;
135
136 // Time can be in format HH:MM, HH:MM:SS, or HH:MM:SS.mm. Try to recognize the
137 // last type first. Do not parse the seconds, they will be ignored anyway.
138 string16 time_column(columns[2]);
139 if (time_column.length() == 11 && time_column[8] == '.')
140 time_column = time_column.substr(0, 8);
141 if (time_column.length() == 8 && time_column[5] == ':')
142 time_column = time_column.substr(0, 5);
143 if (time_column.length() != 5)
144 return false;
145 std::vector<string16> time_parts;
146 SplitString(time_column, ':', &time_parts);
147 if (time_parts.size() != 2)
148 return false;
149 if (!StringToInt(time_parts[0], &time_exploded.hour))
150 return false;
151 if (!StringToInt(time_parts[1], &time_exploded.minute))
152 return false;
153
154 // We don't know the time zone of the server, so just use local time.
155 *time = base::Time::FromLocalExploded(time_exploded);
156 return true;
157 }
158
159 } // namespace
160
161 namespace net {
162
163 FtpDirectoryListingParserVms::FtpDirectoryListingParserVms()
164 : state_(STATE_INITIAL),
165 last_is_directory_(false) {
166 }
167
168 bool FtpDirectoryListingParserVms::ConsumeLine(const string16& line) {
169 switch (state_) {
170 case STATE_INITIAL:
171 DCHECK(last_filename_.empty());
172 if (line.empty())
173 return true;
174 if (StartsWith(line, ASCIIToUTF16("Total of "), true)) {
175 state_ = STATE_END;
176 return true;
177 }
178 // We assume that the first non-empty line is the listing header. It often
179 // starts with "Directory ", but not always.
180 state_ = STATE_RECEIVED_HEADER;
181 return true;
182 case STATE_RECEIVED_HEADER:
183 DCHECK(last_filename_.empty());
184 if (line.empty())
185 return true;
186 state_ = STATE_ENTRIES;
187 return ConsumeEntryLine(line);
188 case STATE_ENTRIES:
189 if (line.empty()) {
190 if (!last_filename_.empty())
191 return false;
192 state_ = STATE_RECEIVED_LAST_ENTRY;
193 return true;
194 }
195 return ConsumeEntryLine(line);
196 case STATE_RECEIVED_LAST_ENTRY:
197 DCHECK(last_filename_.empty());
198 if (line.empty())
199 return true;
200 if (!StartsWith(line, ASCIIToUTF16("Total of "), true))
201 return false;
202 state_ = STATE_END;
203 return true;
204 case STATE_END:
205 DCHECK(last_filename_.empty());
206 return false;
207 default:
208 NOTREACHED();
209 return false;
210 }
211 }
212
213 bool FtpDirectoryListingParserVms::OnEndOfInput() {
214 return (state_ == STATE_END);
215 }
216
217 bool FtpDirectoryListingParserVms::EntryAvailable() const {
218 return !entries_.empty();
219 }
220
221 FtpDirectoryListingEntry FtpDirectoryListingParserVms::PopEntry() {
222 FtpDirectoryListingEntry entry = entries_.front();
223 entries_.pop();
224 return entry;
225 }
226
227 bool FtpDirectoryListingParserVms::ConsumeEntryLine(const string16& line) {
228 std::vector<string16> columns;
229 SplitString(CollapseWhitespace(line, false), ' ', &columns);
230
231 if (columns.size() == 1) {
232 if (!last_filename_.empty())
233 return false;
234 return ParseVmsFilename(columns[0], &last_filename_, &last_is_directory_);
235 }
236
237 // Recognize listing entries which generate "access denied" message even when
238 // trying to list them. We don't display them in the final listing.
239 static const char* kAccessDeniedMessages[] = {
240 "%RMS-E-PRV",
241 "privilege",
242 };
243 for (size_t i = 0; i < arraysize(kAccessDeniedMessages); i++) {
244 if (line.find(ASCIIToUTF16(kAccessDeniedMessages[i])) != string16::npos) {
245 last_filename_.clear();
246 last_is_directory_ = false;
247 return true;
248 }
249 }
250
251 string16 filename;
252 bool is_directory = false;
253 if (last_filename_.empty()) {
254 if (!ParseVmsFilename(columns[0], &filename, &is_directory))
255 return false;
256 columns.erase(columns.begin());
257 } else {
258 filename = last_filename_;
259 is_directory = last_is_directory_;
260 last_filename_.clear();
261 last_is_directory_ = false;
262 }
263
264 if (columns.size() > 5)
265 return false;
266
267 if (columns.size() == 5) {
268 if (!LooksLikeVmsFileProtectionListing(columns[4]))
269 return false;
270 if (!LooksLikeVmsUserIdentificationCode(columns[3]))
271 return false;
272 columns.resize(3);
273 }
274
275 if (columns.size() != 3)
276 return false;
277
278 FtpDirectoryListingEntry entry;
279 entry.name = filename;
280 entry.type = is_directory ? FtpDirectoryListingEntry::DIRECTORY
281 : FtpDirectoryListingEntry::FILE;
282 if (!ParseVmsFilesize(columns[0], &entry.size))
283 return false;
284 if (entry.size < 0)
285 return false;
286 if (entry.type != FtpDirectoryListingEntry::FILE)
287 entry.size = -1;
288 if (!VmsDateListingToTime(columns, &entry.last_modified))
289 return false;
290
291 entries_.push(entry);
292 return true;
293 }
294
295 } // namespace net
OLDNEW
« no previous file with comments | « net/ftp/ftp_directory_listing_parser_vms.h ('k') | net/ftp/ftp_directory_listing_parser_vms_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698