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/i18n/icu_encoding_detection.h"
8#include "base/i18n/icu_string_conversions.h"
9#include "base/stl_util-inl.h"
10#include "base/string_split.h"
11#include "base/string_util.h"
12#include "net/base/net_errors.h"
13#include "net/ftp/ftp_directory_listing_parser_ls.h"
14#include "net/ftp/ftp_directory_listing_parser_netware.h"
15#include "net/ftp/ftp_directory_listing_parser_vms.h"
16#include "net/ftp/ftp_directory_listing_parser_windows.h"
17#include "net/ftp/ftp_server_type_histograms.h"
18
19namespace net {
20
21namespace {
22
23// Fills in |raw_name| for all |entries| using |encoding|. Returns network
24// error code.
25int FillInRawName(const std::string& encoding,
26                  std::vector<FtpDirectoryListingEntry>* entries) {
27  for (size_t i = 0; i < entries->size(); i++) {
28    if (!base::UTF16ToCodepage(entries->at(i).name, encoding.c_str(),
29                               base::OnStringConversionError::FAIL,
30                               &entries->at(i).raw_name)) {
31      return ERR_ENCODING_CONVERSION_FAILED;
32    }
33  }
34
35  return OK;
36}
37
38// Parses |text| as an FTP directory listing. Fills in |entries|
39// and |server_type| and returns network error code.
40int ParseListing(const string16& text,
41                 const std::string& encoding,
42                 const base::Time& current_time,
43                 std::vector<FtpDirectoryListingEntry>* entries,
44                 FtpServerType* server_type) {
45  std::vector<string16> lines;
46  base::SplitString(text, '\n', &lines);
47
48  // TODO(phajdan.jr): Use a table of callbacks instead of repeating code.
49
50  entries->clear();
51  if (ParseFtpDirectoryListingLs(lines, current_time, entries)) {
52    *server_type = SERVER_LS;
53    return FillInRawName(encoding, entries);
54  }
55
56  entries->clear();
57  if (ParseFtpDirectoryListingWindows(lines, entries)) {
58    *server_type = SERVER_WINDOWS;
59    return FillInRawName(encoding, entries);
60  }
61
62  entries->clear();
63  if (ParseFtpDirectoryListingVms(lines, entries)) {
64    *server_type = SERVER_VMS;
65    return FillInRawName(encoding, entries);
66  }
67
68  entries->clear();
69  if (ParseFtpDirectoryListingNetware(lines, current_time, entries)) {
70    *server_type = SERVER_NETWARE;
71    return FillInRawName(encoding, entries);
72  }
73
74  entries->clear();
75  return ERR_UNRECOGNIZED_FTP_DIRECTORY_LISTING_FORMAT;
76}
77
78// Detects encoding of |text| and parses it as an FTP directory listing.
79// Fills in |entries| and |server_type| and returns network error code.
80int DecodeAndParse(const std::string& text,
81                   const base::Time& current_time,
82                   std::vector<FtpDirectoryListingEntry>* entries,
83                   FtpServerType* server_type) {
84  std::vector<std::string> encodings;
85  if (!base::DetectAllEncodings(text, &encodings))
86    return ERR_ENCODING_DETECTION_FAILED;
87
88  // Use first encoding that can be used to decode the text.
89  for (size_t i = 0; i < encodings.size(); i++) {
90    string16 converted_text;
91    if (base::CodepageToUTF16(text,
92                              encodings[i].c_str(),
93                              base::OnStringConversionError::FAIL,
94                              &converted_text)) {
95      int rv = ParseListing(converted_text,
96                            encodings[i],
97                            current_time,
98                            entries,
99                            server_type);
100      if (rv == OK)
101        return rv;
102    }
103  }
104
105  entries->clear();
106  *server_type = SERVER_UNKNOWN;
107  return ERR_UNRECOGNIZED_FTP_DIRECTORY_LISTING_FORMAT;
108}
109
110}  // namespace
111
112FtpDirectoryListingEntry::FtpDirectoryListingEntry() {
113}
114
115int ParseFtpDirectoryListing(const std::string& text,
116                             const base::Time& current_time,
117                             std::vector<FtpDirectoryListingEntry>* entries) {
118  FtpServerType server_type = SERVER_UNKNOWN;
119  int rv = DecodeAndParse(text, current_time, entries, &server_type);
120  UpdateFtpServerTypeHistograms(server_type);
121  return rv;
122}
123
124}  // namespace net
125