Home | History | Annotate | Download | only in ftp
      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