OLD | NEW |
| (Empty) |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "net/ftp/ftp_directory_listing_parser_mlsd.h" | |
6 | |
7 #include <map> | |
8 #include <vector> | |
9 | |
10 #include "base/stl_util-inl.h" | |
11 #include "base/string_number_conversions.h" | |
12 #include "base/string_split.h" | |
13 #include "base/string_util.h" | |
14 #include "base/utf_string_conversions.h" | |
15 | |
16 // You can read the specification of the MLSD format at | |
17 // http://tools.ietf.org/html/rfc3659#page-23. | |
18 | |
19 namespace { | |
20 | |
21 // The MLSD date listing is specified at | |
22 // http://tools.ietf.org/html/rfc3659#page-6. | |
23 bool MlsdDateListingToTime(const string16& text, base::Time* time) { | |
24 base::Time::Exploded time_exploded = { 0 }; | |
25 | |
26 // We will only test 12 characters, but RFC-3659 requires 14 (we ignore the | |
27 // last two digits, which contain the number of seconds). | |
28 if (text.length() < 14) | |
29 return false; | |
30 | |
31 if (!base::StringToInt(text.begin(), text.begin() + 4, &time_exploded.year)) | |
32 return false; | |
33 if (!base::StringToInt(text.begin() + 4, | |
34 text.begin() + 6, | |
35 &time_exploded.month)) | |
36 return false; | |
37 if (!base::StringToInt(text.begin() + 6, | |
38 text.begin() + 8, | |
39 &time_exploded.day_of_month)) | |
40 return false; | |
41 if (!base::StringToInt(text.begin() + 8, | |
42 text.begin() + 10, | |
43 &time_exploded.hour)) | |
44 return false; | |
45 if (!base::StringToInt(text.begin() + 10, | |
46 text.begin() + 12, | |
47 &time_exploded.minute)) | |
48 return false; | |
49 | |
50 // We don't know the time zone of the server, so just use local time. | |
51 *time = base::Time::FromLocalExploded(time_exploded); | |
52 return true; | |
53 } | |
54 | |
55 } // namespace | |
56 | |
57 namespace net { | |
58 | |
59 FtpDirectoryListingParserMlsd::FtpDirectoryListingParserMlsd() {} | |
60 | |
61 FtpDirectoryListingParserMlsd::~FtpDirectoryListingParserMlsd() {} | |
62 | |
63 bool FtpDirectoryListingParserMlsd::ConsumeLine(const string16& line) { | |
64 // The first space indicates where the filename begins. | |
65 string16::size_type first_space_pos = line.find(' '); | |
66 if (first_space_pos == string16::npos || first_space_pos < 1) | |
67 return false; | |
68 | |
69 string16 facts_string = line.substr(0, first_space_pos - 1); | |
70 string16 filename = line.substr(first_space_pos + 1); | |
71 std::vector<string16> facts_split; | |
72 base::SplitString(facts_string, ';', &facts_split); | |
73 | |
74 const char* keys[] = { | |
75 "modify", | |
76 "size", | |
77 "type", | |
78 }; | |
79 | |
80 std::map<std::string, string16> facts; | |
81 for (std::vector<string16>::const_iterator i = facts_split.begin(); | |
82 i != facts_split.end(); ++i) { | |
83 string16::size_type equal_sign_pos = i->find('='); | |
84 if (equal_sign_pos == string16::npos) | |
85 return false; | |
86 string16 key = i->substr(0, equal_sign_pos); | |
87 string16 value = i->substr(equal_sign_pos + 1); | |
88 | |
89 // If we're interested in a key, record its value. Note that we don't detect | |
90 // a case when the server is sending duplicate keys. We're not validating | |
91 // the input, just parsing it. | |
92 for (size_t j = 0; j < arraysize(keys); j++) | |
93 if (LowerCaseEqualsASCII(key, keys[j])) | |
94 facts[keys[j]] = value; | |
95 } | |
96 if (!ContainsKey(facts, "type")) | |
97 return false; | |
98 | |
99 FtpDirectoryListingEntry entry; | |
100 entry.name = filename; | |
101 | |
102 if (LowerCaseEqualsASCII(facts["type"], "dir")) { | |
103 entry.type = FtpDirectoryListingEntry::DIRECTORY; | |
104 entry.size = -1; | |
105 } else if (LowerCaseEqualsASCII(facts["type"], "file")) { | |
106 entry.type = FtpDirectoryListingEntry::FILE; | |
107 if (!ContainsKey(facts, "size")) | |
108 return false; | |
109 if (!base::StringToInt64(facts["size"], &entry.size)) | |
110 return false; | |
111 } else { | |
112 // Ignore other types of entries. They are either not interesting for us | |
113 // (cdir, pdir), or not regular files (OS-specific types). There is no | |
114 // specific type for symlink. Symlinks get a type of their target. | |
115 return true; | |
116 } | |
117 | |
118 if (!ContainsKey(facts, "modify")) | |
119 return false; | |
120 if (!MlsdDateListingToTime(facts["modify"], &entry.last_modified)) | |
121 return false; | |
122 | |
123 entries_.push(entry); | |
124 return true; | |
125 } | |
126 | |
127 bool FtpDirectoryListingParserMlsd::OnEndOfInput() { | |
128 return true; | |
129 } | |
130 | |
131 bool FtpDirectoryListingParserMlsd::EntryAvailable() const { | |
132 return !entries_.empty(); | |
133 } | |
134 | |
135 FtpDirectoryListingEntry FtpDirectoryListingParserMlsd::PopEntry() { | |
136 DCHECK(EntryAvailable()); | |
137 FtpDirectoryListingEntry entry = entries_.front(); | |
138 entries_.pop(); | |
139 return entry; | |
140 } | |
141 | |
142 } // namespace net | |
OLD | NEW |