Chromium Code Reviews| Index: net/ftp/ftp_directory_listing_parser_vms.cc |
| diff --git a/net/ftp/ftp_directory_listing_parser_vms.cc b/net/ftp/ftp_directory_listing_parser_vms.cc |
| index c74dad031c2fd85df5a5d7d0a090e3b2835301ac..4924fe6e33596966cbc2341377b2b13203efeb26 100644 |
| --- a/net/ftp/ftp_directory_listing_parser_vms.cc |
| +++ b/net/ftp/ftp_directory_listing_parser_vms.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 { |
| @@ -17,7 +19,7 @@ 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) { |
| + net::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 +42,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 = net::FtpDirectoryListingEntry::DIRECTORY; |
| } else { |
| *parsed_filename = StringToLowerASCII(listing_parts[0]); |
| - *is_directory = false; |
| + *type = net::FtpDirectoryListingEntry::FILE; |
| } |
| return true; |
| } |
| @@ -117,15 +119,29 @@ 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)) |
| @@ -138,7 +154,7 @@ bool VmsDateListingToTime(const std::vector<string16>& columns, |
| // 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] == ':') |
| @@ -163,142 +179,88 @@ bool VmsDateListingToTime(const std::vector<string16>& columns, |
| namespace net { |
|
eroman
2011/03/24 23:09:35
nit: i suggest moving this up, so the anonymous na
Paweł Hajdan Jr.
2011/03/26 09:47:50
Done.
|
| -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; |
| + |
| + // 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++; |
| + } |
| - if (columns.size() == 1) { |
| - if (!last_filename_.empty()) |
| + 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 |