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