1ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
23345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// Use of this source code is governed by a BSD-style license that can be
33345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// found in the LICENSE file.
4c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
5c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/ftp/ftp_directory_listing_parser_windows.h"
6c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
7c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include <vector>
8c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
93345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/string_number_conversions.h"
103345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/string_split.h"
11c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/string_util.h"
12ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/time.h"
13ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "net/ftp/ftp_directory_listing_parser.h"
14c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/ftp/ftp_util.h"
15c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
16c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottnamespace {
17c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
18c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool WindowsDateListingToTime(const std::vector<string16>& columns,
19c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                              base::Time* time) {
2021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  DCHECK_LE(3U, columns.size());
21c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
22c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  base::Time::Exploded time_exploded = { 0 };
23c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
24c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Date should be in format MM-DD-YY[YY].
25c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::vector<string16> date_parts;
26731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  base::SplitString(columns[0], '-', &date_parts);
27c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (date_parts.size() != 3)
28c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
293345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (!base::StringToInt(date_parts[0], &time_exploded.month))
30c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
313345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (!base::StringToInt(date_parts[1], &time_exploded.day_of_month))
32c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
333345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (!base::StringToInt(date_parts[2], &time_exploded.year))
34c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
35c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (time_exploded.year < 0)
36c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
37c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // If year has only two digits then assume that 00-79 is 2000-2079,
38c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // and 80-99 is 1980-1999.
39c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (time_exploded.year < 80)
40c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    time_exploded.year += 2000;
41c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  else if (time_exploded.year < 100)
42c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    time_exploded.year += 1900;
43c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
44c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Time should be in format HH:MM(AM|PM)
45c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (columns[1].length() != 7)
46c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
47c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::vector<string16> time_parts;
48731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  base::SplitString(columns[1].substr(0, 5), ':', &time_parts);
49c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (time_parts.size() != 2)
50c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
513345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (!base::StringToInt(time_parts[0], &time_exploded.hour))
52c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
533345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (!base::StringToInt(time_parts[1], &time_exploded.minute))
543345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    return false;
553345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (!time_exploded.HasValidValues())
56c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
57c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  string16 am_or_pm(columns[1].substr(5, 2));
583345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (EqualsASCII(am_or_pm, "PM")) {
593345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    if (time_exploded.hour < 12)
603345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      time_exploded.hour += 12;
613345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  } else if (EqualsASCII(am_or_pm, "AM")) {
623345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    if (time_exploded.hour == 12)
633345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      time_exploded.hour = 0;
643345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  } else {
65c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
663345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
67c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
68c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // We don't know the time zone of the server, so just use local time.
69c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  *time = base::Time::FromLocalExploded(time_exploded);
70c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return true;
71c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
72c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
73c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}  // namespace
74c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
75c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottnamespace net {
76c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
77ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenbool ParseFtpDirectoryListingWindows(
78ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    const std::vector<string16>& lines,
79ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    std::vector<FtpDirectoryListingEntry>* entries) {
80ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  for (size_t i = 0; i < lines.size(); i++) {
81ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (lines[i].empty())
82ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      continue;
83ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
84ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    std::vector<string16> columns;
85ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    base::SplitString(CollapseWhitespace(lines[i], false), ' ', &columns);
86ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
87ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // Every line of the listing consists of the following:
88ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    //
89ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    //   1. date
90ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    //   2. time
91ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    //   3. size in bytes (or "<DIR>" for directories)
92ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    //   4. filename (may be empty or contain spaces)
93ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    //
94ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // For now, make sure we have 1-3, and handle 4 later.
95ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (columns.size() < 3)
96c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return false;
97ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
98ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    FtpDirectoryListingEntry entry;
99ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (EqualsASCII(columns[2], "<DIR>")) {
100ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      entry.type = FtpDirectoryListingEntry::DIRECTORY;
101ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      entry.size = -1;
102ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    } else {
103ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      entry.type = FtpDirectoryListingEntry::FILE;
104ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      if (!base::StringToInt64(columns[2], &entry.size))
105ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        return false;
106ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      if (entry.size < 0)
107ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        return false;
108ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
109ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
110ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (!WindowsDateListingToTime(columns, &entry.last_modified))
111c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return false;
112c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
113ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    entry.name = FtpUtil::GetStringPartAfterColumns(lines[i], 3);
114ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (entry.name.empty()) {
115ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      // Some FTP servers send listing entries with empty names.
116ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      // It's not obvious how to display such an entry, so ignore them.
117ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      // We don't want to make the parsing fail at this point though.
118ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      // Other entries can still be useful.
119ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      continue;
120ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
121c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
122ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    entries->push_back(entry);
12321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  }
12421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
125c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return true;
126c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
127c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
128c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}  // namespace net
129