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
16namespace {
17
18bool 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
75namespace net {
76
77bool 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