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