diff options
author | phajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-12-10 19:39:01 +0000 |
---|---|---|
committer | phajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-12-10 19:39:01 +0000 |
commit | 9772b1c37ba3b261b06e91bbd8d7b27b51b9ca03 (patch) | |
tree | 4c8d08cb265f5c0a3b55cca0502d5658fa90923c /net/ftp | |
parent | 04970c3e67a87051974762ea3c545256ac1e8696 (diff) | |
download | chromium_src-9772b1c37ba3b261b06e91bbd8d7b27b51b9ca03.zip chromium_src-9772b1c37ba3b261b06e91bbd8d7b27b51b9ca03.tar.gz chromium_src-9772b1c37ba3b261b06e91bbd8d7b27b51b9ca03.tar.bz2 |
Add support for MLSD FTP directory listing (RFC-3659)
At least the format is specified... that's a good thing.
TEST=Covered by net_unittests.
BUG=25520, 29691
Review URL: http://codereview.chromium.org/479004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@34268 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/ftp')
-rw-r--r-- | net/ftp/ftp_directory_listing_buffer.cc | 2 | ||||
-rw-r--r-- | net/ftp/ftp_directory_listing_buffer_unittest.cc | 2 | ||||
-rw-r--r-- | net/ftp/ftp_directory_listing_parser_mlsd.cc | 130 | ||||
-rw-r--r-- | net/ftp/ftp_directory_listing_parser_mlsd.h | 35 | ||||
-rw-r--r-- | net/ftp/ftp_directory_listing_parser_mlsd_unittest.cc | 67 | ||||
-rw-r--r-- | net/ftp/ftp_server_type_histograms.h | 1 |
6 files changed, 237 insertions, 0 deletions
diff --git a/net/ftp/ftp_directory_listing_buffer.cc b/net/ftp/ftp_directory_listing_buffer.cc index 644f59e..2376b8f 100644 --- a/net/ftp/ftp_directory_listing_buffer.cc +++ b/net/ftp/ftp_directory_listing_buffer.cc @@ -9,6 +9,7 @@ #include "base/string_util.h" #include "net/base/net_errors.h" #include "net/ftp/ftp_directory_listing_parser_ls.h" +#include "net/ftp/ftp_directory_listing_parser_mlsd.h" #include "net/ftp/ftp_directory_listing_parser_netware.h" #include "net/ftp/ftp_directory_listing_parser_vms.h" #include "net/ftp/ftp_directory_listing_parser_windows.h" @@ -45,6 +46,7 @@ namespace net { FtpDirectoryListingBuffer::FtpDirectoryListingBuffer() : current_parser_(NULL) { parsers_.insert(new FtpDirectoryListingParserLs()); + parsers_.insert(new FtpDirectoryListingParserMlsd()); parsers_.insert(new FtpDirectoryListingParserNetware()); parsers_.insert(new FtpDirectoryListingParserVms()); parsers_.insert(new FtpDirectoryListingParserWindows()); diff --git a/net/ftp/ftp_directory_listing_buffer_unittest.cc b/net/ftp/ftp_directory_listing_buffer_unittest.cc index b9ca7a2..cb24489 100644 --- a/net/ftp/ftp_directory_listing_buffer_unittest.cc +++ b/net/ftp/ftp_directory_listing_buffer_unittest.cc @@ -34,6 +34,8 @@ TEST(FtpDirectoryListingBufferTest, Parse) { "dir-listing-ls-14", "dir-listing-ls-15", "dir-listing-ls-16", + "dir-listing-mlsd-1", + "dir-listing-mlsd-2", "dir-listing-netware-1", "dir-listing-netware-2", "dir-listing-vms-1", diff --git a/net/ftp/ftp_directory_listing_parser_mlsd.cc b/net/ftp/ftp_directory_listing_parser_mlsd.cc new file mode 100644 index 0000000..3929bf2 --- /dev/null +++ b/net/ftp/ftp_directory_listing_parser_mlsd.cc @@ -0,0 +1,130 @@ +// 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_mlsd.h" + +#include <map> +#include <vector> + +#include "base/stl_util-inl.h" +#include "base/string_util.h" + +// You can read the specification of the MLSD format at +// http://tools.ietf.org/html/rfc3659#page-23. + +namespace { + +// The MLSD date listing is specified at +// http://tools.ietf.org/html/rfc3659#page-6. +bool MlsdDateListingToTime(const string16& text, base::Time* time) { + base::Time::Exploded time_exploded = { 0 }; + + // We will only test 12 characters, but RFC-3659 requires 14 (we ignore the + // last two digits, which contain the number of seconds). + if (text.length() < 14) + return false; + + if (!StringToInt(text.substr(0, 4), &time_exploded.year)) + return false; + if (!StringToInt(text.substr(4, 2), &time_exploded.month)) + return false; + if (!StringToInt(text.substr(6, 2), &time_exploded.day_of_month)) + return false; + if (!StringToInt(text.substr(8, 2), &time_exploded.hour)) + return false; + if (!StringToInt(text.substr(10, 2), &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 { + +FtpDirectoryListingParserMlsd::FtpDirectoryListingParserMlsd() { +} + +bool FtpDirectoryListingParserMlsd::ConsumeLine(const string16& line) { + // The first space indicates where the filename begins. + string16::size_type first_space_pos = line.find(' '); + if (first_space_pos == string16::npos || first_space_pos < 1) + return false; + + string16 facts_string = line.substr(0, first_space_pos - 1); + string16 filename = line.substr(first_space_pos + 1); + std::vector<string16> facts_split; + SplitString(facts_string, ';', &facts_split); + + const char* keys[] = { + "modify", + "size", + "type", + }; + + std::map<std::string, string16> facts; + for (std::vector<string16>::const_iterator i = facts_split.begin(); + i != facts_split.end(); ++i) { + string16::size_type equal_sign_pos = i->find('='); + if (equal_sign_pos == string16::npos) + return false; + string16 key = i->substr(0, equal_sign_pos); + string16 value = i->substr(equal_sign_pos + 1); + + // If we're interested in a key, record its value. Note that we don't detect + // a case when the server is sending duplicate keys. We're not validating + // the input, just parsing it. + for (size_t j = 0; j < arraysize(keys); j++) + if (LowerCaseEqualsASCII(key, keys[j])) + facts[keys[j]] = value; + } + if (!ContainsKey(facts, "type")) + return false; + + FtpDirectoryListingEntry entry; + entry.name = filename; + + if (LowerCaseEqualsASCII(facts["type"], "dir")) { + entry.type = FtpDirectoryListingEntry::DIRECTORY; + entry.size = -1; + } else if (LowerCaseEqualsASCII(facts["type"], "file")) { + entry.type = FtpDirectoryListingEntry::FILE; + if (!ContainsKey(facts, "size")) + return false; + if (!StringToInt64(facts["size"], &entry.size)) + return false; + } else { + // Ignore other types of entries. They are either not interesting for us + // (cdir, pdir), or not regular files (OS-specific types). There is no + // specific type for symlink. Symlinks get a type of their target. + return true; + } + + if (!ContainsKey(facts, "modify")) + return false; + if (!MlsdDateListingToTime(facts["modify"], &entry.last_modified)) + return false; + + entries_.push(entry); + return true; +} + +bool FtpDirectoryListingParserMlsd::OnEndOfInput() { + return true; +} + +bool FtpDirectoryListingParserMlsd::EntryAvailable() const { + return !entries_.empty(); +} + +FtpDirectoryListingEntry FtpDirectoryListingParserMlsd::PopEntry() { + DCHECK(EntryAvailable()); + FtpDirectoryListingEntry entry = entries_.front(); + entries_.pop(); + return entry; +} + +} // namespace net diff --git a/net/ftp/ftp_directory_listing_parser_mlsd.h b/net/ftp/ftp_directory_listing_parser_mlsd.h new file mode 100644 index 0000000..8d0e3b7 --- /dev/null +++ b/net/ftp/ftp_directory_listing_parser_mlsd.h @@ -0,0 +1,35 @@ +// 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. + +#ifndef NET_FTP_FTP_DIRECTORY_LISTING_PARSER_MLSD_H_ +#define NET_FTP_FTP_DIRECTORY_LISTING_PARSER_MLSD_H_ + +#include <queue> + +#include "net/ftp/ftp_directory_listing_parser.h" + +namespace net { + +// Parser for MLSD directory listing (RFC-3659). For more info see +// http://tools.ietf.org/html/rfc3659#page-23. +class FtpDirectoryListingParserMlsd : public FtpDirectoryListingParser { + public: + FtpDirectoryListingParserMlsd(); + + // FtpDirectoryListingParser methods: + virtual FtpServerType GetServerType() const { return SERVER_MLSD; } + virtual bool ConsumeLine(const string16& line); + virtual bool OnEndOfInput(); + virtual bool EntryAvailable() const; + virtual FtpDirectoryListingEntry PopEntry(); + + private: + std::queue<FtpDirectoryListingEntry> entries_; + + DISALLOW_COPY_AND_ASSIGN(FtpDirectoryListingParserMlsd); +}; + +} // namespace net + +#endif // NET_FTP_FTP_DIRECTORY_LISTING_PARSER_MLSD_H_ diff --git a/net/ftp/ftp_directory_listing_parser_mlsd_unittest.cc b/net/ftp/ftp_directory_listing_parser_mlsd_unittest.cc new file mode 100644 index 0000000..34c2481 --- /dev/null +++ b/net/ftp/ftp_directory_listing_parser_mlsd_unittest.cc @@ -0,0 +1,67 @@ +// 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_unittest.h" + +#include "base/format_macros.h" +#include "net/ftp/ftp_directory_listing_parser_mlsd.h" + +namespace { + +typedef net::FtpDirectoryListingParserTest FtpDirectoryListingParserMlsdTest; + +TEST_F(FtpDirectoryListingParserMlsdTest, Good) { + base::Time::Exploded now_exploded; + base::Time::Now().LocalExplode(&now_exploded); + + const struct SingleLineTestData good_cases[] = { + { "type=file;size=380565;modify=20030606190749; README", + net::FtpDirectoryListingEntry::FILE, "README", 380565, + 2003, 6, 6, 19, 7 }, + { "type=dir;sizd=512;modify=20031021200128; pub", + net::FtpDirectoryListingEntry::DIRECTORY, "pub", -1, + 2003, 10, 21, 20, 1 }, + { "type=dir;sizd=512;modify=20091009080706;UNIX.mode=0755; pub", + net::FtpDirectoryListingEntry::DIRECTORY, "pub", -1, + 2009, 10, 9, 8, 7 }, + { "type=dir;modify=20010414155237;UNIX.mode=0555;unique=6ag5b4e400; etc", + net::FtpDirectoryListingEntry::DIRECTORY, "etc", -1, + 2001, 4, 14, 15, 52 }, + }; + for (size_t i = 0; i < arraysize(good_cases); i++) { + SCOPED_TRACE(StringPrintf("Test[%" PRIuS "]: %s", i, good_cases[i].input)); + + net::FtpDirectoryListingParserMlsd parser; + RunSingleLineTestCase(&parser, good_cases[i]); + } +} + +TEST_F(FtpDirectoryListingParserMlsdTest, Bad) { + const char* bad_cases[] = { + "", + " ", + " ", + ";", + "; ", + " ;", + "garbage", + "total 5", + "type=file;size=380565;modify=20030606190749;README", + "type=file;size=380565;modify=20030606190749;", + "type=file;size=380565;modify=20030606190749", + "size=380565;modify=20030606190749; README", + "type=file;modify=20030606190749; README", + "type=file;size=380565; README", + "type=file; size=380565; modify=20030606190749; README", + " type=file;size=380565;modify=20030606190749; README", + "type=file;size=garbage;modify=20030606190749; README", + "type=file;size=380565;modify=garbage; README", + }; + for (size_t i = 0; i < arraysize(bad_cases); i++) { + net::FtpDirectoryListingParserMlsd parser; + EXPECT_FALSE(parser.ConsumeLine(UTF8ToUTF16(bad_cases[i]))) << bad_cases[i]; + } +} + +} // namespace diff --git a/net/ftp/ftp_server_type_histograms.h b/net/ftp/ftp_server_type_histograms.h index ca9f6e4..c425006 100644 --- a/net/ftp/ftp_server_type_histograms.h +++ b/net/ftp/ftp_server_type_histograms.h @@ -36,6 +36,7 @@ enum FtpServerType { SERVER_WINDOWS = 10, // Server using Windows listing style. SERVER_VMS = 11, // Server using VMS listing style. SERVER_NETWARE = 12, // Server using Netware listing style. + SERVER_MLSD = 13, // Server using MLSD listing (RFC-3659). NUM_OF_SERVER_TYPES }; |