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_windows.h"
      6 
      7 #include <vector>
      8 
      9 #include "base/string_number_conversions.h"
     10 #include "base/string_split.h"
     11 #include "base/string_util.h"
     12 #include "base/time.h"
     13 #include "net/ftp/ftp_directory_listing_parser.h"
     14 #include "net/ftp/ftp_util.h"
     15 
     16 namespace {
     17 
     18 bool WindowsDateListingToTime(const std::vector<string16>& columns,
     19                               base::Time* time) {
     20   DCHECK_LE(3U, columns.size());
     21 
     22   base::Time::Exploded time_exploded = { 0 };
     23 
     24   // Date should be in format MM-DD-YY[YY].
     25   std::vector<string16> date_parts;
     26   base::SplitString(columns[0], '-', &date_parts);
     27   if (date_parts.size() != 3)
     28     return false;
     29   if (!base::StringToInt(date_parts[0], &time_exploded.month))
     30     return false;
     31   if (!base::StringToInt(date_parts[1], &time_exploded.day_of_month))
     32     return false;
     33   if (!base::StringToInt(date_parts[2], &time_exploded.year))
     34     return false;
     35   if (time_exploded.year < 0)
     36     return false;
     37   // If year has only two digits then assume that 00-79 is 2000-2079,
     38   // and 80-99 is 1980-1999.
     39   if (time_exploded.year < 80)
     40     time_exploded.year += 2000;
     41   else if (time_exploded.year < 100)
     42     time_exploded.year += 1900;
     43 
     44   // Time should be in format HH:MM(AM|PM)
     45   if (columns[1].length() != 7)
     46     return false;
     47   std::vector<string16> time_parts;
     48   base::SplitString(columns[1].substr(0, 5), ':', &time_parts);
     49   if (time_parts.size() != 2)
     50     return false;
     51   if (!base::StringToInt(time_parts[0], &time_exploded.hour))
     52     return false;
     53   if (!base::StringToInt(time_parts[1], &time_exploded.minute))
     54     return false;
     55   if (!time_exploded.HasValidValues())
     56     return false;
     57   string16 am_or_pm(columns[1].substr(5, 2));
     58   if (EqualsASCII(am_or_pm, "PM")) {
     59     if (time_exploded.hour < 12)
     60       time_exploded.hour += 12;
     61   } else if (EqualsASCII(am_or_pm, "AM")) {
     62     if (time_exploded.hour == 12)
     63       time_exploded.hour = 0;
     64   } else {
     65     return false;
     66   }
     67 
     68   // We don't know the time zone of the server, so just use local time.
     69   *time = base::Time::FromLocalExploded(time_exploded);
     70   return true;
     71 }
     72 
     73 }  // namespace
     74 
     75 namespace net {
     76 
     77 bool ParseFtpDirectoryListingWindows(
     78     const std::vector<string16>& lines,
     79     std::vector<FtpDirectoryListingEntry>* entries) {
     80   for (size_t i = 0; i < lines.size(); i++) {
     81     if (lines[i].empty())
     82       continue;
     83 
     84     std::vector<string16> columns;
     85     base::SplitString(CollapseWhitespace(lines[i], false), ' ', &columns);
     86 
     87     // Every line of the listing consists of the following:
     88     //
     89     //   1. date
     90     //   2. time
     91     //   3. size in bytes (or "<DIR>" for directories)
     92     //   4. filename (may be empty or contain spaces)
     93     //
     94     // For now, make sure we have 1-3, and handle 4 later.
     95     if (columns.size() < 3)
     96       return false;
     97 
     98     FtpDirectoryListingEntry entry;
     99     if (EqualsASCII(columns[2], "<DIR>")) {
    100       entry.type = FtpDirectoryListingEntry::DIRECTORY;
    101       entry.size = -1;
    102     } else {
    103       entry.type = FtpDirectoryListingEntry::FILE;
    104       if (!base::StringToInt64(columns[2], &entry.size))
    105         return false;
    106       if (entry.size < 0)
    107         return false;
    108     }
    109 
    110     if (!WindowsDateListingToTime(columns, &entry.last_modified))
    111       return false;
    112 
    113     entry.name = FtpUtil::GetStringPartAfterColumns(lines[i], 3);
    114     if (entry.name.empty()) {
    115       // Some FTP servers send listing entries with empty names.
    116       // It's not obvious how to display such an entry, so ignore them.
    117       // We don't want to make the parsing fail at this point though.
    118       // Other entries can still be useful.
    119       continue;
    120     }
    121 
    122     entries->push_back(entry);
    123   }
    124 
    125   return true;
    126 }
    127 
    128 }  // namespace net
    129