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(&current_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