summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorphajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-12-10 19:39:01 +0000
committerphajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-12-10 19:39:01 +0000
commit9772b1c37ba3b261b06e91bbd8d7b27b51b9ca03 (patch)
tree4c8d08cb265f5c0a3b55cca0502d5658fa90923c
parent04970c3e67a87051974762ea3c545256ac1e8696 (diff)
downloadchromium_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
-rw-r--r--net/data/ftp/dir-listing-mlsd-15
-rw-r--r--net/data/ftp/dir-listing-mlsd-1.expected44
-rw-r--r--net/data/ftp/dir-listing-mlsd-21
-rw-r--r--net/data/ftp/dir-listing-mlsd-2.expected8
-rw-r--r--net/ftp/ftp_directory_listing_buffer.cc2
-rw-r--r--net/ftp/ftp_directory_listing_buffer_unittest.cc2
-rw-r--r--net/ftp/ftp_directory_listing_parser_mlsd.cc130
-rw-r--r--net/ftp/ftp_directory_listing_parser_mlsd.h35
-rw-r--r--net/ftp/ftp_directory_listing_parser_mlsd_unittest.cc67
-rw-r--r--net/ftp/ftp_server_type_histograms.h1
-rw-r--r--net/net.gyp3
11 files changed, 298 insertions, 0 deletions
diff --git a/net/data/ftp/dir-listing-mlsd-1 b/net/data/ftp/dir-listing-mlsd-1
new file mode 100644
index 0000000..9463845
--- /dev/null
+++ b/net/data/ftp/dir-listing-mlsd-1
@@ -0,0 +1,5 @@
+type=dir;sizd=512;modify=20010414155043;UNIX.mode=0555;unique=6ag5b4e3ff; bin
+type=dir;sizd=512;modify=20010414155237;UNIX.mode=0555;unique=6ag5b4e400; etc
+type=dir;sizd=512;modify=20010705213550;UNIX.mode=0711;unique=6ag5b4e401; hidden
+type=dir;sizd=512;modify=20050422001005;UNIX.mode=0755;unique=100ff03g6164000; j
+type=dir;sizd=512;modify=20031021200128;UNIX.mode=0755;unique=6ag5b4e403; pub
diff --git a/net/data/ftp/dir-listing-mlsd-1.expected b/net/data/ftp/dir-listing-mlsd-1.expected
new file mode 100644
index 0000000..7720451
--- /dev/null
+++ b/net/data/ftp/dir-listing-mlsd-1.expected
@@ -0,0 +1,44 @@
+d
+bin
+-1
+2001
+4
+14
+15
+50
+
+d
+etc
+-1
+2001
+4
+14
+15
+52
+
+d
+hidden
+-1
+2001
+7
+5
+21
+35
+
+d
+j
+-1
+2005
+4
+22
+0
+10
+
+d
+pub
+-1
+2003
+10
+21
+20
+1
diff --git a/net/data/ftp/dir-listing-mlsd-2 b/net/data/ftp/dir-listing-mlsd-2
new file mode 100644
index 0000000..939638a
--- /dev/null
+++ b/net/data/ftp/dir-listing-mlsd-2
@@ -0,0 +1 @@
+type=file;size=839;modify=19980908010757;UNIX.mode=0644;unique=6ag5b4e47d; ftpmotd
diff --git a/net/data/ftp/dir-listing-mlsd-2.expected b/net/data/ftp/dir-listing-mlsd-2.expected
new file mode 100644
index 0000000..33659ea
--- /dev/null
+++ b/net/data/ftp/dir-listing-mlsd-2.expected
@@ -0,0 +1,8 @@
+-
+ftpmotd
+839
+1998
+9
+8
+1
+7
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
};
diff --git a/net/net.gyp b/net/net.gyp
index 3f12c697..5118981 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -294,6 +294,8 @@
'ftp/ftp_directory_listing_parser.h',
'ftp/ftp_directory_listing_parser_ls.cc',
'ftp/ftp_directory_listing_parser_ls.h',
+ 'ftp/ftp_directory_listing_parser_mlsd.cc',
+ 'ftp/ftp_directory_listing_parser_mlsd.h',
'ftp/ftp_directory_listing_parser_netware.cc',
'ftp/ftp_directory_listing_parser_netware.h',
'ftp/ftp_directory_listing_parser_vms.cc',
@@ -614,6 +616,7 @@
'ftp/ftp_ctrl_response_buffer_unittest.cc',
'ftp/ftp_directory_listing_buffer_unittest.cc',
'ftp/ftp_directory_listing_parser_ls_unittest.cc',
+ 'ftp/ftp_directory_listing_parser_mlsd_unittest.cc',
'ftp/ftp_directory_listing_parser_netware_unittest.cc',
'ftp/ftp_directory_listing_parser_vms_unittest.cc',
'ftp/ftp_directory_listing_parser_windows_unittest.cc',