1 // Copyright (c) 2011 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.h" 6 7 #include "base/bind.h" 8 #include "base/callback.h" 9 #include "base/i18n/icu_encoding_detection.h" 10 #include "base/i18n/icu_string_conversions.h" 11 #include "base/stl_util.h" 12 #include "base/strings/string_util.h" 13 #include "base/strings/string_split.h" 14 #include "base/strings/utf_string_conversions.h" 15 #include "net/base/net_errors.h" 16 #include "net/ftp/ftp_directory_listing_parser_ls.h" 17 #include "net/ftp/ftp_directory_listing_parser_netware.h" 18 #include "net/ftp/ftp_directory_listing_parser_os2.h" 19 #include "net/ftp/ftp_directory_listing_parser_vms.h" 20 #include "net/ftp/ftp_directory_listing_parser_windows.h" 21 #include "net/ftp/ftp_server_type_histograms.h" 22 23 namespace net { 24 25 namespace { 26 27 // Fills in |raw_name| for all |entries| using |encoding|. Returns network 28 // error code. 29 int FillInRawName(const std::string& encoding, 30 std::vector<FtpDirectoryListingEntry>* entries) { 31 for (size_t i = 0; i < entries->size(); i++) { 32 if (!base::UTF16ToCodepage(entries->at(i).name, encoding.c_str(), 33 base::OnStringConversionError::FAIL, 34 &entries->at(i).raw_name)) { 35 return ERR_ENCODING_CONVERSION_FAILED; 36 } 37 } 38 39 return OK; 40 } 41 42 // Parses |text| as an FTP directory listing. Fills in |entries| 43 // and |server_type| and returns network error code. 44 int ParseListing(const base::string16& text, 45 const base::string16& newline_separator, 46 const std::string& encoding, 47 const base::Time& current_time, 48 std::vector<FtpDirectoryListingEntry>* entries, 49 FtpServerType* server_type) { 50 std::vector<base::string16> lines; 51 base::SplitStringUsingSubstr(text, newline_separator, &lines); 52 53 struct { 54 base::Callback<bool(void)> callback; 55 FtpServerType server_type; 56 } parsers[] = { 57 { 58 base::Bind(&ParseFtpDirectoryListingLs, lines, current_time, entries), 59 SERVER_LS 60 }, 61 { 62 base::Bind(&ParseFtpDirectoryListingWindows, lines, entries), 63 SERVER_WINDOWS 64 }, 65 { 66 base::Bind(&ParseFtpDirectoryListingVms, lines, entries), 67 SERVER_VMS 68 }, 69 { 70 base::Bind(&ParseFtpDirectoryListingNetware, 71 lines, current_time, entries), 72 SERVER_NETWARE 73 }, 74 { 75 base::Bind(&ParseFtpDirectoryListingOS2, lines, entries), 76 SERVER_OS2 77 } 78 }; 79 80 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(parsers); i++) { 81 entries->clear(); 82 if (parsers[i].callback.Run()) { 83 *server_type = parsers[i].server_type; 84 return FillInRawName(encoding, entries); 85 } 86 } 87 88 entries->clear(); 89 return ERR_UNRECOGNIZED_FTP_DIRECTORY_LISTING_FORMAT; 90 } 91 92 // Detects encoding of |text| and parses it as an FTP directory listing. 93 // Fills in |entries| and |server_type| and returns network error code. 94 int DecodeAndParse(const std::string& text, 95 const base::Time& current_time, 96 std::vector<FtpDirectoryListingEntry>* entries, 97 FtpServerType* server_type) { 98 const char* kNewlineSeparators[] = { "\n", "\r\n" }; 99 100 std::vector<std::string> encodings; 101 if (!base::DetectAllEncodings(text, &encodings)) 102 return ERR_ENCODING_DETECTION_FAILED; 103 104 // Use first encoding that can be used to decode the text. 105 for (size_t i = 0; i < encodings.size(); i++) { 106 base::string16 converted_text; 107 if (base::CodepageToUTF16(text, 108 encodings[i].c_str(), 109 base::OnStringConversionError::FAIL, 110 &converted_text)) { 111 for (size_t j = 0; j < arraysize(kNewlineSeparators); j++) { 112 int rv = ParseListing(converted_text, 113 base::ASCIIToUTF16(kNewlineSeparators[j]), 114 encodings[i], 115 current_time, 116 entries, 117 server_type); 118 if (rv == OK) 119 return rv; 120 } 121 } 122 } 123 124 entries->clear(); 125 *server_type = SERVER_UNKNOWN; 126 return ERR_UNRECOGNIZED_FTP_DIRECTORY_LISTING_FORMAT; 127 } 128 129 } // namespace 130 131 FtpDirectoryListingEntry::FtpDirectoryListingEntry() 132 : type(UNKNOWN), 133 size(-1) { 134 } 135 136 int ParseFtpDirectoryListing(const std::string& text, 137 const base::Time& current_time, 138 std::vector<FtpDirectoryListingEntry>* entries) { 139 FtpServerType server_type = SERVER_UNKNOWN; 140 int rv = DecodeAndParse(text, current_time, entries, &server_type); 141 UpdateFtpServerTypeHistograms(server_type); 142 return rv; 143 } 144 145 } // namespace net 146