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.
FillInRawName(const std::string & encoding,std::vector<FtpDirectoryListingEntry> * entries)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.
ParseListing(const base::string16 & text,const base::string16 & newline_separator,const std::string & encoding,const base::Time & current_time,std::vector<FtpDirectoryListingEntry> * entries,FtpServerType * server_type)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.
DecodeAndParse(const std::string & text,const base::Time & current_time,std::vector<FtpDirectoryListingEntry> * entries,FtpServerType * server_type)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
FtpDirectoryListingEntry()131 FtpDirectoryListingEntry::FtpDirectoryListingEntry()
132 : type(UNKNOWN),
133 size(-1) {
134 }
135
ParseFtpDirectoryListing(const std::string & text,const base::Time & current_time,std::vector<FtpDirectoryListingEntry> * entries)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