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

Unified 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 side-by-side diff with in-line comments
Download patch
« 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 »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
new file mode 100644
index 0000000000000000000000000000000000000000..bfb14d4847d6b36e949e886ea10271f9fb4f9171
--- /dev/null
+++ b/net/ftp/ftp_directory_listing_parser_vms.cc
@@ -0,0 +1,295 @@
+// Copyright (c) 2009 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.
+
+#include "net/ftp/ftp_directory_listing_parser_vms.h"
+
+#include <vector>
+
+#include "base/string_util.h"
+#include "net/ftp/ftp_util.h"
+
+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) {
+ // 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;
+ SplitString(raw_filename, ';', &listing_parts);
+ if (listing_parts.size() != 2)
+ return false;
+ int version_number;
+ if (!StringToInt(listing_parts[1], &version_number))
+ return false;
+ if (version_number < 0)
+ return false;
+
+ // Even directories have extensions in the listings. Don't display extensions
+ // for directories; it's awkward for non-VMS users. Also, VMS is
+ // case-insensitive, but generally uses uppercase characters. This may look
+ // awkward, so we convert them to lower case.
+ std::vector<string16> filename_parts;
+ SplitString(listing_parts[0], '.', &filename_parts);
+ if (filename_parts.size() != 2)
+ return false;
+ if (EqualsASCII(filename_parts[1], "DIR")) {
+ *parsed_filename = StringToLowerASCII(filename_parts[0]);
+ *is_directory = true;
+ } else {
+ *parsed_filename = StringToLowerASCII(listing_parts[0]);
+ *is_directory = false;
+ }
+ return true;
+}
+
+bool ParseVmsFilesize(const string16& input, int64* size) {
+ // VMS's directory listing gives us file size in blocks. We assume that
+ // the block size is 512 bytes. It doesn't give accurate file size, but is the
+ // best information we have.
+ const int kBlockSize = 512;
+
+ if (StringToInt64(input, size)) {
+ *size *= kBlockSize;
+ return true;
+ }
+
+ std::vector<string16> parts;
+ SplitString(input, '/', &parts);
+ if (parts.size() != 2)
+ return false;
+
+ int64 blocks_used, blocks_allocated;
+ if (!StringToInt64(parts[0], &blocks_used))
+ return false;
+ if (!StringToInt64(parts[1], &blocks_allocated))
+ return false;
+ if (blocks_used > blocks_allocated)
+ return false;
+
+ *size = blocks_used * kBlockSize;
+ return true;
+}
+
+bool LooksLikeVmsFileProtectionListingPart(const string16& input) {
+ if (input.length() > 4)
+ return false;
+
+ // On VMS there are four different permission bits: Read, Write, Execute,
+ // and Delete. They appear in that order in the permission listing.
+ std::string pattern("RWED");
+ string16 match(input);
+ while (!match.empty() && !pattern.empty()) {
+ if (match[0] == pattern[0])
+ match = match.substr(1);
+ pattern = pattern.substr(1);
+ }
+ return match.empty();
+}
+
+bool LooksLikeVmsFileProtectionListing(const string16& input) {
+ if (input.length() < 2)
+ return false;
+ if (input[0] != '(' || input[input.length() - 1] != ')')
+ return false;
+
+ // We expect four parts of the file protection listing: for System, Owner,
+ // Group, and World.
+ std::vector<string16> parts;
+ SplitString(input.substr(1, input.length() - 2), ',', &parts);
+ if (parts.size() != 4)
+ return false;
+
+ return LooksLikeVmsFileProtectionListingPart(parts[0]) &&
+ LooksLikeVmsFileProtectionListingPart(parts[1]) &&
+ LooksLikeVmsFileProtectionListingPart(parts[2]) &&
+ LooksLikeVmsFileProtectionListingPart(parts[3]);
+}
+
+bool LooksLikeVmsUserIdentificationCode(const string16& input) {
+ if (input.length() < 2)
+ return false;
+ return input[0] == '[' && input[input.length() - 1] == ']';
+}
+
+bool VmsDateListingToTime(const std::vector<string16>& columns,
+ base::Time* time) {
+ DCHECK_EQ(3U, columns.size());
+
+ base::Time::Exploded time_exploded = { 0 };
+
+ // Date should be in format DD-MMM-YYYY.
+ std::vector<string16> date_parts;
+ SplitString(columns[1], '-', &date_parts);
+ if (date_parts.size() != 3)
+ return false;
+ if (!StringToInt(date_parts[0], &time_exploded.day_of_month))
+ return false;
+ if (!net::FtpUtil::ThreeLetterMonthToNumber(date_parts[1],
+ &time_exploded.month))
+ return false;
+ if (!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]);
+ if (time_column.length() == 11 && time_column[8] == '.')
+ time_column = time_column.substr(0, 8);
+ if (time_column.length() == 8 && time_column[5] == ':')
+ time_column = time_column.substr(0, 5);
+ if (time_column.length() != 5)
+ return false;
+ std::vector<string16> time_parts;
+ SplitString(time_column, ':', &time_parts);
+ if (time_parts.size() != 2)
+ return false;
+ if (!StringToInt(time_parts[0], &time_exploded.hour))
+ return false;
+ if (!StringToInt(time_parts[1], &time_exploded.minute))
+ return false;
+
+ // We don't know the time zone of the server, so just use local time.
+ *time = base::Time::FromLocalExploded(time_exploded);
+ return true;
+}
+
+} // namespace
+
+namespace net {
+
+FtpDirectoryListingParserVms::FtpDirectoryListingParserVms()
+ : state_(STATE_INITIAL),
+ last_is_directory_(false) {
+}
+
+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())
+ 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);
+}
+
+bool FtpDirectoryListingParserVms::EntryAvailable() const {
+ return !entries_.empty();
+}
+
+FtpDirectoryListingEntry FtpDirectoryListingParserVms::PopEntry() {
+ FtpDirectoryListingEntry entry = entries_.front();
+ entries_.pop();
+ return entry;
+}
+
+bool FtpDirectoryListingParserVms::ConsumeEntryLine(const string16& line) {
+ std::vector<string16> columns;
+ SplitString(CollapseWhitespace(line, false), ' ', &columns);
+
+ if (columns.size() == 1) {
+ if (!last_filename_.empty())
+ 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;
+ }
+ }
+
+ string16 filename;
+ bool is_directory = false;
+ if (last_filename_.empty()) {
+ if (!ParseVmsFilename(columns[0], &filename, &is_directory))
+ 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]))
+ return false;
+ if (!LooksLikeVmsUserIdentificationCode(columns[3]))
+ 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(entry);
+ return true;
+}
+
+} // namespace net
« 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