15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2011 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/ftp/ftp_util.h" 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <map> 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <vector> 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/i18n/case_conversion.h" 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/i18n/char_iterator.h" 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h" 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/singleton.h" 145e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)#include "base/strings/string_number_conversions.h" 15c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/strings/string_piece.h" 162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_split.h" 172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_tokenizer.h" 18eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/strings/string_util.h" 19868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h" 20eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/time/time.h" 21ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include "third_party/icu/source/common/unicode/uchar.h" 22ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include "third_party/icu/source/i18n/unicode/datefmt.h" 23ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include "third_party/icu/source/i18n/unicode/dtfmtsym.h" 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)using base::ASCIIToUTF16; 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using base::StringPiece16; 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// For examples of Unix<->VMS path conversions, see the unit test file. On VMS 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// a path looks differently depending on whether it's a file or directory. 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace net { 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::string FtpUtil::UnixFilePathToVMS(const std::string& unix_path) { 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (unix_path.empty()) 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return std::string(); 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::StringTokenizer tokenizer(unix_path, "/"); 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::vector<std::string> tokens; 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while (tokenizer.GetNext()) 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) tokens.push_back(tokenizer.token()); 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (unix_path[0] == '/') { 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // It's an absolute path. 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (tokens.empty()) { 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK_EQ(1U, unix_path.length()); 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "[]"; 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (tokens.size() == 1) 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return unix_path.substr(1); // Drop the leading slash. 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string result(tokens[0] + ":["); 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (tokens.size() == 2) { 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Don't ask why, it just works that way on VMS. 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) result.append("000000"); 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) result.append(tokens[1]); 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (size_t i = 2; i < tokens.size() - 1; i++) 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) result.append("." + tokens[i]); 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) result.append("]" + tokens[tokens.size() - 1]); 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return result; 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (tokens.size() == 1) 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return unix_path; 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string result("["); 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (size_t i = 0; i < tokens.size() - 1; i++) 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) result.append("." + tokens[i]); 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) result.append("]" + tokens[tokens.size() - 1]); 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return result; 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::string FtpUtil::UnixDirectoryPathToVMS(const std::string& unix_path) { 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (unix_path.empty()) 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return std::string(); 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string path(unix_path); 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (path[path.length() - 1] != '/') 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) path.append("/"); 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Reuse logic from UnixFilePathToVMS by appending a fake file name to the 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // real path and removing it after conversion. 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) path.append("x"); 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) path = UnixFilePathToVMS(path); 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return path.substr(0, path.length() - 1); 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::string FtpUtil::VMSPathToUnix(const std::string& vms_path) { 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (vms_path.empty()) 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "."; 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (vms_path[0] == '/') { 1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // This is not really a VMS path. Most likely the server is emulating UNIX. 1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Return path as-is. 1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return vms_path; 1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (vms_path == "[]") 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "/"; 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string result(vms_path); 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (vms_path[0] == '[') { 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // It's a relative path. 111c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) ReplaceFirstSubstringAfterOffset(&result, 0, "[.", std::string()); 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // It's an absolute path. 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) result.insert(0, "/"); 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ReplaceSubstringsAfterOffset(&result, 0, ":[000000]", "/"); 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ReplaceSubstringsAfterOffset(&result, 0, ":[", "/"); 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::replace(result.begin(), result.end(), '.', '/'); 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::replace(result.begin(), result.end(), ']', '/'); 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Make sure the result doesn't end with a slash. 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (result.length() && result[result.length() - 1] == '/') 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) result = result.substr(0, result.length() - 1); 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return result; 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace { 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Lazy-initialized map of abbreviated month names. 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class AbbreviatedMonthsMap { 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public: 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) static AbbreviatedMonthsMap* GetInstance() { 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return Singleton<AbbreviatedMonthsMap>::get(); 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Converts abbreviated month name |text| to its number (in range 1-12). 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // On success returns true and puts the number in |number|. 139c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) bool GetMonthNumber(const base::string16& text, int* number) { 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Ignore the case of the month names. The simplest way to handle that 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // is to make everything lowercase. 142c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) base::string16 text_lower(base::i18n::ToLower(text)); 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (map_.find(text_lower) == map_.end()) 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *number = map_[text_lower]; 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private: 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) friend struct DefaultSingletonTraits<AbbreviatedMonthsMap>; 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Constructor, initializes the map based on ICU data. It is much faster 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // to do that just once. 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) AbbreviatedMonthsMap() { 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int32_t locales_count; 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const icu::Locale* locales = 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icu::DateFormat::getAvailableLocales(locales_count); 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (int32_t locale = 0; locale < locales_count; locale++) { 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) UErrorCode status(U_ZERO_ERROR); 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icu::DateFormatSymbols format_symbols(locales[locale], status); 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If we cannot get format symbols for some locale, it's not a fatal 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // error. Just try another one. 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (U_FAILURE(status)) 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue; 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int32_t months_count; 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const icu::UnicodeString* months = 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) format_symbols.getShortMonths(months_count); 1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (int32_t month = 0; month < months_count; month++) { 176c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) base::string16 month_name(months[month].getBuffer(), 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) static_cast<size_t>(months[month].length())); 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Ignore the case of the month names. The simplest way to handle that 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // is to make everything lowercase. 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) month_name = base::i18n::ToLower(month_name); 1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) map_[month_name] = month + 1; 1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Sometimes ICU returns longer strings, but in FTP listings a shorter 1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // abbreviation is used (for example for the Russian locale). Make sure 1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // we always have a map entry for a three-letter abbreviation. 1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) map_[month_name.substr(0, 3)] = month + 1; 1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 191c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 192c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // Fail loudly if the data returned by ICU is obviously incomplete. 193c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // This is intended to catch cases like http://crbug.com/177428 194c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // much earlier. Note that the issue above turned out to be non-trivial 195c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // to reproduce - crash data is much better indicator of a problem 196c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // than incomplete bug reports. 197c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) CHECK_EQ(1, map_[ASCIIToUTF16("jan")]); 198c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) CHECK_EQ(2, map_[ASCIIToUTF16("feb")]); 199c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) CHECK_EQ(3, map_[ASCIIToUTF16("mar")]); 200c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) CHECK_EQ(4, map_[ASCIIToUTF16("apr")]); 201c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) CHECK_EQ(5, map_[ASCIIToUTF16("may")]); 202c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) CHECK_EQ(6, map_[ASCIIToUTF16("jun")]); 203c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) CHECK_EQ(7, map_[ASCIIToUTF16("jul")]); 204c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) CHECK_EQ(8, map_[ASCIIToUTF16("aug")]); 205c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) CHECK_EQ(9, map_[ASCIIToUTF16("sep")]); 206c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) CHECK_EQ(10, map_[ASCIIToUTF16("oct")]); 207c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) CHECK_EQ(11, map_[ASCIIToUTF16("nov")]); 208c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) CHECK_EQ(12, map_[ASCIIToUTF16("dec")]); 2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Maps lowercase month names to numbers in range 1-12. 212c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) std::map<base::string16, int> map_; 2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DISALLOW_COPY_AND_ASSIGN(AbbreviatedMonthsMap); 2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace 2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static 220c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool FtpUtil::AbbreviatedMonthToNumber(const base::string16& text, 221c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) int* number) { 2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return AbbreviatedMonthsMap::GetInstance()->GetMonthNumber(text, number); 2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static 226c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool FtpUtil::LsDateListingToTime(const base::string16& month, 227c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) const base::string16& day, 228c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) const base::string16& rest, 2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const base::Time& current_time, 2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::Time* result) { 2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::Time::Exploded time_exploded = { 0 }; 2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!AbbreviatedMonthToNumber(month, &time_exploded.month)) { 2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Work around garbage sent by some servers in the same column 2352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // as the month. Take just last 3 characters of the string. 2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (month.length() < 3 || 2372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) !AbbreviatedMonthToNumber(month.substr(month.length() - 3), 2382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) &time_exploded.month)) { 2392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return false; 2402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!base::StringToInt(day, &time_exploded.day_of_month)) 2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (time_exploded.day_of_month > 31) 2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!base::StringToInt(rest, &time_exploded.year)) { 2490f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) // Maybe it's time. Does it look like time? Note that it can be any of 2500f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) // "HH:MM", "H:MM", "HH:M" or maybe even "H:M". 2510f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) if (rest.length() > 5) 2520f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) return false; 2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2540f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) size_t colon_pos = rest.find(':'); 255a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) if (colon_pos == base::string16::npos) 2560f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) return false; 2570f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) if (colon_pos > 2) 2580f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) return false; 2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2600f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) if (!base::StringToInt( 2610f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) StringPiece16(rest.begin(), rest.begin() + colon_pos), 2620f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) &time_exploded.hour)) { 2630f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) return false; 2640f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) } 2650f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) if (!base::StringToInt( 2660f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) StringPiece16(rest.begin() + colon_pos + 1, rest.end()), 2670f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) &time_exploded.minute)) { 2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Guess the year. 2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::Time::Exploded current_exploded; 2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) current_time.LocalExplode(¤t_exploded); 2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If it's not possible for the parsed date to be in the current year, 2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // use the previous year. 2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (time_exploded.month > current_exploded.month || 2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) (time_exploded.month == current_exploded.month && 2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) time_exploded.day_of_month > current_exploded.day_of_month)) { 2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) time_exploded.year = current_exploded.year - 1; 2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) time_exploded.year = current_exploded.year; 2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // We don't know the time zone of the listing, so just use local time. 2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *result = base::Time::FromLocalExploded(time_exploded); 2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static 292c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool FtpUtil::WindowsDateListingToTime(const base::string16& date, 293c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) const base::string16& time, 2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::Time* result) { 2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::Time::Exploded time_exploded = { 0 }; 2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Date should be in format MM-DD-YY[YY]. 298c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) std::vector<base::string16> date_parts; 2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::SplitString(date, '-', &date_parts); 3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (date_parts.size() != 3) 3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!base::StringToInt(date_parts[0], &time_exploded.month)) 3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!base::StringToInt(date_parts[1], &time_exploded.day_of_month)) 3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!base::StringToInt(date_parts[2], &time_exploded.year)) 3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (time_exploded.year < 0) 3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If year has only two digits then assume that 00-79 is 2000-2079, 3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // and 80-99 is 1980-1999. 3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (time_exploded.year < 80) 3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) time_exploded.year += 2000; 3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else if (time_exploded.year < 100) 3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) time_exploded.year += 1900; 3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Time should be in format HH:MM[(AM|PM)] 3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (time.length() < 5) 3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 321c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) std::vector<base::string16> time_parts; 3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::SplitString(time.substr(0, 5), ':', &time_parts); 3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (time_parts.size() != 2) 3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!base::StringToInt(time_parts[0], &time_exploded.hour)) 3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!base::StringToInt(time_parts[1], &time_exploded.minute)) 3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!time_exploded.HasValidValues()) 3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (time.length() > 5) { 3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (time.length() != 7) 3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 335c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) base::string16 am_or_pm(time.substr(5, 2)); 3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (EqualsASCII(am_or_pm, "PM")) { 3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (time_exploded.hour < 12) 3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) time_exploded.hour += 12; 3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else if (EqualsASCII(am_or_pm, "AM")) { 3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (time_exploded.hour == 12) 3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) time_exploded.hour = 0; 3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // We don't know the time zone of the server, so just use local time. 3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *result = base::Time::FromLocalExploded(time_exploded); 3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static 353c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)base::string16 FtpUtil::GetStringPartAfterColumns(const base::string16& text, 354c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) int columns) { 3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::i18n::UTF16CharIterator iter(&text); 3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // TODO(jshin): Is u_isspace the right function to use here? 3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (int i = 0; i < columns; i++) { 3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Skip the leading whitespace. 3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while (!iter.end() && u_isspace(iter.get())) 3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) iter.Advance(); 3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Skip the actual text of i-th column. 3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while (!iter.end() && !u_isspace(iter.get())) 3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) iter.Advance(); 3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 368c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) base::string16 result(text.substr(iter.array_pos())); 369a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) base::TrimWhitespace(result, base::TRIM_ALL, &result); 3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return result; 3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace 374