| 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 |