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 |