| 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 65a3161cd721d91c30bb892867201d96b565e0b6..9a8a2f7e7a6589bc67ccd0fc405185cf97890210 100644
|
| --- a/net/ftp/ftp_directory_listing_parser_vms.cc
|
| +++ b/net/ftp/ftp_directory_listing_parser_vms.cc
|
| @@ -9,15 +9,19 @@
|
| #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 net {
|
| +
|
| 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) {
|
| + 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 +44,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 = FtpDirectoryListingEntry::DIRECTORY;
|
| } else {
|
| *parsed_filename = StringToLowerASCII(listing_parts[0]);
|
| - *is_directory = false;
|
| + *type = FtpDirectoryListingEntry::FILE;
|
| }
|
| return true;
|
| }
|
| @@ -117,28 +121,42 @@ 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))
|
| return false;
|
| - if (!net::FtpUtil::AbbreviatedMonthToNumber(date_parts[1],
|
| - &time_exploded.month))
|
| + if (!FtpUtil::AbbreviatedMonthToNumber(date_parts[1],
|
| + &time_exploded.month))
|
| return false;
|
| if (!base::StringToInt(date_parts[2], &time_exploded.year))
|
| return false;
|
|
|
| // 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] == ':')
|
| @@ -161,144 +179,88 @@ bool VmsDateListingToTime(const std::vector<string16>& columns,
|
|
|
| } // namespace
|
|
|
| -namespace net {
|
| -
|
| -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;
|
|
|
| - if (columns.size() == 1) {
|
| - if (!last_filename_.empty())
|
| + // 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++;
|
| + }
|
| +
|
| + 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
|
|
|