1ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Use of this source code is governed by a BSD-style license that can be
3c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// found in the LICENSE file.
4c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
5c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/ftp/ftp_util.h"
6c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
7c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include <vector>
8c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
9ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/i18n/char_iterator.h"
10c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/logging.h"
113345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/string_number_conversions.h"
12c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/string_tokenizer.h"
13c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/string_util.h"
14c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/time.h"
15c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/utf_string_conversions.h"
16ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "unicode/datefmt.h"
17ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "unicode/dtfmtsym.h"
18ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "unicode/uchar.h"
19c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
20c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// For examples of Unix<->VMS path conversions, see the unit test file. On VMS
21c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// a path looks differently depending on whether it's a file or directory.
22c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
23c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottnamespace net {
24c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
25c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
26c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottstd::string FtpUtil::UnixFilePathToVMS(const std::string& unix_path) {
27c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (unix_path.empty())
28c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return std::string();
29c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
30c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  StringTokenizer tokenizer(unix_path, "/");
31c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::vector<std::string> tokens;
32c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  while (tokenizer.GetNext())
33c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    tokens.push_back(tokenizer.token());
34c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
35c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (unix_path[0] == '/') {
36c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // It's an absolute path.
37c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
38c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (tokens.empty()) {
39c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      DCHECK_EQ(1U, unix_path.length());
40c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return "[]";
41c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
42c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
43c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (tokens.size() == 1)
44c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return unix_path.substr(1);  // Drop the leading slash.
45c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
46c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    std::string result(tokens[0] + ":[");
47c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (tokens.size() == 2) {
48c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      // Don't ask why, it just works that way on VMS.
49c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      result.append("000000");
50c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    } else {
51c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      result.append(tokens[1]);
52c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      for (size_t i = 2; i < tokens.size() - 1; i++)
53c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        result.append("." + tokens[i]);
54c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
55c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    result.append("]" + tokens[tokens.size() - 1]);
56c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return result;
57c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
58c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
59c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (tokens.size() == 1)
60c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return unix_path;
61c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
62c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string result("[");
63c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  for (size_t i = 0; i < tokens.size() - 1; i++)
64c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    result.append("." + tokens[i]);
65c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  result.append("]" + tokens[tokens.size() - 1]);
66c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return result;
67c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
68c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
69c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
70c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottstd::string FtpUtil::UnixDirectoryPathToVMS(const std::string& unix_path) {
71c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (unix_path.empty())
72c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return std::string();
73c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
74c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string path(unix_path);
75c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
76c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (path[path.length() - 1] != '/')
77c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    path.append("/");
78c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
79c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Reuse logic from UnixFilePathToVMS by appending a fake file name to the
80c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // real path and removing it after conversion.
81c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  path.append("x");
82c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  path = UnixFilePathToVMS(path);
83c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return path.substr(0, path.length() - 1);
84c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
85c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
86c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
87c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottstd::string FtpUtil::VMSPathToUnix(const std::string& vms_path) {
88c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (vms_path.empty())
89c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return ".";
90c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
91c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (vms_path == "[]")
92c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return "/";
93c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
94c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string result(vms_path);
95c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (vms_path[0] == '[') {
96c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // It's a relative path.
97c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    ReplaceFirstSubstringAfterOffset(&result, 0, "[.", "");
98c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  } else {
99c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // It's an absolute path.
100c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    result.insert(0, "/");
101c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    ReplaceSubstringsAfterOffset(&result, 0, ":[000000]", "/");
102c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    ReplaceSubstringsAfterOffset(&result, 0, ":[", "/");
103c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
104c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::replace(result.begin(), result.end(), '.', '/');
105c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::replace(result.begin(), result.end(), ']', '/');
106c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
107c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Make sure the result doesn't end with a slash.
108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (result.length() && result[result.length() - 1] == '/')
109c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    result = result.substr(0, result.length() - 1);
110c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
111c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return result;
112c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
113c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
114c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
115ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenbool FtpUtil::AbbreviatedMonthToNumber(const string16& text, int* number) {
116ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  icu::UnicodeString unicode_text(text.data(), text.size());
117ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
118ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  int32_t locales_count;
119ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  const icu::Locale* locales =
120ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      icu::DateFormat::getAvailableLocales(locales_count);
121ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
122ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Some FTP servers localize the date listings. To guess the locale,
123ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // we loop over all available ones.
124ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  for (int32_t locale = 0; locale < locales_count; locale++) {
125ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    UErrorCode status(U_ZERO_ERROR);
126ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
127ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    icu::DateFormatSymbols format_symbols(locales[locale], status);
128ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
129ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // If we cannot get format symbols for some locale, it's not a fatal error.
130ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // Just try another one.
131ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (U_FAILURE(status))
132ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      continue;
133ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
134ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    int32_t months_count;
135ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    const icu::UnicodeString* months =
136ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        format_symbols.getShortMonths(months_count);
137ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
138ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // Loop over all abbreviated month names in given locale.
139ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // An alternative solution (to parse |text| in given locale) is more
140ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // lenient, and may accept more than we want even with setLenient(false).
141ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    for (int32_t month = 0; month < months_count; month++) {
142ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      // Compare (case-insensitive), but just first three characters. Sometimes
143ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      // ICU returns longer strings (for example for Russian locale), and in FTP
144ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      // listings they are abbreviated to just three characters.
145ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      // Note: ICU may also return strings shorter than three characters,
146ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      // and those also should be accepted.
147ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      if (months[month].caseCompare(0, 3, unicode_text, 0) == 0) {
148ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        *number = month + 1;
149ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        return true;
150ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      }
151c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
152c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
153c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
154c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return false;
155c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
156c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
157c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
158c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool FtpUtil::LsDateListingToTime(const string16& month, const string16& day,
159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                  const string16& rest,
160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                  const base::Time& current_time,
161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                  base::Time* result) {
162c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  base::Time::Exploded time_exploded = { 0 };
163c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
164ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (!AbbreviatedMonthToNumber(month, &time_exploded.month))
165c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
166c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1673345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (!base::StringToInt(day, &time_exploded.day_of_month))
168c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
169ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (time_exploded.day_of_month > 31)
170ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return false;
171c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
1723345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (!base::StringToInt(rest, &time_exploded.year)) {
1733345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    // Maybe it's time. Does it look like time (HH:MM)?
1743345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    if (rest.length() == 5 && rest[2] == ':') {
175513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch      if (!base::StringToInt(rest.begin(),
176513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                             rest.begin() + 2,
177513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                             &time_exploded.hour))
1783345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        return false;
1793345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
180513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch      if (!base::StringToInt(rest.begin() + 3,
181513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                             rest.begin() + 5,
182513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                             &time_exploded.minute))
1833345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        return false;
1843345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    } else if (rest.length() == 4 && rest[1] == ':') {
1853345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      // Sometimes it's just H:MM.
186513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch      if (!base::StringToInt(rest.begin(),
187513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                             rest.begin() + 1,
188513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                             &time_exploded.hour))
1893345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        return false;
1903345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
191513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch      if (!base::StringToInt(rest.begin() + 2,
192513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                             rest.begin() + 4,
193513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                             &time_exploded.minute))
1943345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        return false;
1953345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    } else {
196c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return false;
1973345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    }
198c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
199c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Guess the year.
200c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    base::Time::Exploded current_exploded;
201c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    current_time.LocalExplode(&current_exploded);
202c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
203c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // If it's not possible for the parsed date to be in the current year,
204c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // use the previous year.
205c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (time_exploded.month > current_exploded.month ||
206c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        (time_exploded.month == current_exploded.month &&
207c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch         time_exploded.day_of_month > current_exploded.day_of_month)) {
208c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      time_exploded.year = current_exploded.year - 1;
209c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else {
210c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      time_exploded.year = current_exploded.year;
211c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
212c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
213c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
214c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // We don't know the time zone of the listing, so just use local time.
215c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  *result = base::Time::FromLocalExploded(time_exploded);
216c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return true;
217c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
218c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
219c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
220c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottstring16 FtpUtil::GetStringPartAfterColumns(const string16& text, int columns) {
221ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  base::i18n::UTF16CharIterator iter(&text);
222c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
223ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // TODO(jshin): Is u_isspace the right function to use here?
224c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  for (int i = 0; i < columns; i++) {
225c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // Skip the leading whitespace.
226ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    while (!iter.end() && u_isspace(iter.get()))
227ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      iter.Advance();
228c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
229c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // Skip the actual text of i-th column.
230ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    while (!iter.end() && !u_isspace(iter.get()))
231ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      iter.Advance();
232c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
233c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
234ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  string16 result(text.substr(iter.array_pos()));
235c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  TrimWhitespace(result, TRIM_ALL, &result);
236c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return result;
237c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
238c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
239c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}  // namespace
240