| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. Use of this |
| 2 // source code is governed by a BSD-style license that can be found in the |
| 3 // LICENSE file. |
| 4 |
| 5 #include "net/ftp/ftp_directory_listing_parser_ls.h" |
| 6 |
| 7 #include <vector> |
| 8 |
| 9 #include "base/string_util.h" |
| 10 #include "net/ftp/ftp_util.h" |
| 11 |
| 12 namespace { |
| 13 |
| 14 bool LooksLikeUnixPermission(const string16& text) { |
| 15 if (text.length() != 3) |
| 16 return false; |
| 17 |
| 18 // Meaning of the flags: |
| 19 // r - file is readable |
| 20 // w - file is writable |
| 21 // x - file is executable |
| 22 // s or S - setuid/setgid bit set |
| 23 // t or T - "sticky" bit set |
| 24 return ((text[0] == 'r' || text[0] == '-') && |
| 25 (text[1] == 'w' || text[1] == '-') && |
| 26 (text[2] == 'x' || text[2] == 's' || text[2] == 'S' || |
| 27 text[2] == 't' || text[2] == 'T' || text[2] == '-')); |
| 28 } |
| 29 |
| 30 bool LooksLikeUnixPermissionsListing(const string16& text) { |
| 31 if (text.length() != 10) |
| 32 return false; |
| 33 |
| 34 if (text[0] != 'b' && text[0] != 'c' && text[0] != 'd' && |
| 35 text[0] != 'l' && text[0] != 'p' && text[0] != 's' && |
| 36 text[0] != '-') |
| 37 return false; |
| 38 |
| 39 return (LooksLikeUnixPermission(text.substr(1, 3)) && |
| 40 LooksLikeUnixPermission(text.substr(4, 3)) && |
| 41 LooksLikeUnixPermission(text.substr(7, 3))); |
| 42 } |
| 43 |
| 44 string16 GetStringPartAfterColumns(const string16& text, int columns) { |
| 45 DCHECK_LE(1, columns); |
| 46 int columns_so_far = 0; |
| 47 size_t last = 0; |
| 48 for (size_t i = 1; i < text.length(); ++i) { |
| 49 if (!isspace(text[i - 1]) && isspace(text[i])) { |
| 50 last = i; |
| 51 if (++columns_so_far == columns) |
| 52 break; |
| 53 } |
| 54 } |
| 55 string16 result(text.substr(last)); |
| 56 TrimWhitespace(result, TRIM_ALL, &result); |
| 57 return result; |
| 58 } |
| 59 |
| 60 bool UnixDateListingToTime(const std::vector<string16>& columns, |
| 61 base::Time* time) { |
| 62 DCHECK_LE(9U, columns.size()); |
| 63 |
| 64 base::Time::Exploded time_exploded = { 0 }; |
| 65 |
| 66 if (!net::FtpUtil::ThreeLetterMonthToNumber(columns[5], &time_exploded.month)) |
| 67 return false; |
| 68 |
| 69 if (!StringToInt(columns[6], &time_exploded.day_of_month)) |
| 70 return false; |
| 71 |
| 72 if (!StringToInt(columns[7], &time_exploded.year)) { |
| 73 // Maybe it's time. Does it look like time (MM:HH)? |
| 74 if (columns[7].length() != 5 || columns[7][2] != ':') |
| 75 return false; |
| 76 |
| 77 if (!StringToInt(columns[7].substr(0, 2), &time_exploded.hour)) |
| 78 return false; |
| 79 |
| 80 if (!StringToInt(columns[7].substr(3, 2), &time_exploded.minute)) |
| 81 return false; |
| 82 |
| 83 // Use current year. |
| 84 base::Time::Exploded now_exploded; |
| 85 base::Time::Now().LocalExplode(&now_exploded); |
| 86 time_exploded.year = now_exploded.year; |
| 87 } |
| 88 |
| 89 // We don't know the time zone of the server, so just use local time. |
| 90 *time = base::Time::FromLocalExploded(time_exploded); |
| 91 return true; |
| 92 } |
| 93 |
| 94 } // namespace |
| 95 |
| 96 namespace net { |
| 97 |
| 98 FtpDirectoryListingParserLs::FtpDirectoryListingParserLs() |
| 99 : received_nonempty_line_(false) { |
| 100 } |
| 101 |
| 102 bool FtpDirectoryListingParserLs::ConsumeLine(const string16& line) { |
| 103 if (StartsWith(line, ASCIIToUTF16("total "), true) || |
| 104 StartsWith(line, ASCIIToUTF16("Gesamt "), true)) { |
| 105 // Some FTP servers put a "total n" line at the beginning of the listing |
| 106 // (n is an integer). Allow such a line, but only once, and only if it's |
| 107 // the first non-empty line. |
| 108 // |
| 109 // Note: "Gesamt" is a German word for "total". The case is important here: |
| 110 // for "ls -l" style listings, "total" will be lowercase, and Gesamt will be |
| 111 // capitalized. This helps us distinguish that from a VMS-style listing, |
| 112 // which would use "Total" (note the uppercase first letter). |
| 113 |
| 114 if (received_nonempty_line_) |
| 115 return false; |
| 116 |
| 117 received_nonempty_line_ = true; |
| 118 return true; |
| 119 } |
| 120 if (line.empty() && !received_nonempty_line_) { |
| 121 // Allow empty lines only at the beginning of the listing. For example VMS |
| 122 // systems in Unix emulation mode add an empty line before the first listing |
| 123 // entry. |
| 124 return true; |
| 125 } |
| 126 received_nonempty_line_ = true; |
| 127 |
| 128 std::vector<string16> columns; |
| 129 SplitString(CollapseWhitespace(line, false), ' ', &columns); |
| 130 |
| 131 // We may receive file names containing spaces, which can make the number of |
| 132 // columns arbitrarily large. We will handle that later. For now just make |
| 133 // sure we have all the columns that should normally be there. |
| 134 if (columns.size() < 9) |
| 135 return false; |
| 136 |
| 137 if (!LooksLikeUnixPermissionsListing(columns[0])) |
| 138 return false; |
| 139 |
| 140 FtpDirectoryListingEntry entry; |
| 141 if (columns[0][0] == 'l') { |
| 142 entry.type = FtpDirectoryListingEntry::SYMLINK; |
| 143 } else if (columns[0][0] == 'd') { |
| 144 entry.type = FtpDirectoryListingEntry::DIRECTORY; |
| 145 } else { |
| 146 entry.type = FtpDirectoryListingEntry::FILE; |
| 147 } |
| 148 |
| 149 int number_of_links; |
| 150 if (!StringToInt(columns[1], &number_of_links)) |
| 151 return false; |
| 152 if (number_of_links < 0) |
| 153 return false; |
| 154 |
| 155 if (!StringToInt64(columns[4], &entry.size)) |
| 156 return false; |
| 157 if (entry.size < 0) |
| 158 return false; |
| 159 if (entry.type != FtpDirectoryListingEntry::FILE) |
| 160 entry.size = -1; |
| 161 |
| 162 if (!UnixDateListingToTime(columns, &entry.last_modified)) |
| 163 return false; |
| 164 |
| 165 entry.name = GetStringPartAfterColumns(line, 8); |
| 166 if (entry.type == FtpDirectoryListingEntry::SYMLINK) { |
| 167 string16::size_type pos = entry.name.rfind(ASCIIToUTF16(" -> ")); |
| 168 if (pos == string16::npos) |
| 169 return false; |
| 170 entry.name = entry.name.substr(0, pos); |
| 171 } |
| 172 |
| 173 entries_.push(entry); |
| 174 return true; |
| 175 } |
| 176 |
| 177 bool FtpDirectoryListingParserLs::OnEndOfInput() { |
| 178 return true; |
| 179 } |
| 180 |
| 181 bool FtpDirectoryListingParserLs::EntryAvailable() const { |
| 182 return !entries_.empty(); |
| 183 } |
| 184 |
| 185 FtpDirectoryListingEntry FtpDirectoryListingParserLs::PopEntry() { |
| 186 FtpDirectoryListingEntry entry = entries_.front(); |
| 187 entries_.pop(); |
| 188 return entry; |
| 189 } |
| 190 |
| 191 } // namespace net |
| OLD | NEW |