| // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/ftp/ftp_util.h" |
| |
| #include <vector> |
| |
| #include "base/logging.h" |
| #include "base/string_number_conversions.h" |
| #include "base/string_tokenizer.h" |
| #include "base/string_util.h" |
| #include "base/time.h" |
| #include "base/utf_string_conversions.h" |
| |
| // For examples of Unix<->VMS path conversions, see the unit test file. On VMS |
| // a path looks differently depending on whether it's a file or directory. |
| |
| namespace net { |
| |
| // static |
| std::string FtpUtil::UnixFilePathToVMS(const std::string& unix_path) { |
| if (unix_path.empty()) |
| return std::string(); |
| |
| StringTokenizer tokenizer(unix_path, "/"); |
| std::vector<std::string> tokens; |
| while (tokenizer.GetNext()) |
| tokens.push_back(tokenizer.token()); |
| |
| if (unix_path[0] == '/') { |
| // It's an absolute path. |
| |
| if (tokens.empty()) { |
| DCHECK_EQ(1U, unix_path.length()); |
| return "[]"; |
| } |
| |
| if (tokens.size() == 1) |
| return unix_path.substr(1); // Drop the leading slash. |
| |
| std::string result(tokens[0] + ":["); |
| if (tokens.size() == 2) { |
| // Don't ask why, it just works that way on VMS. |
| result.append("000000"); |
| } else { |
| result.append(tokens[1]); |
| for (size_t i = 2; i < tokens.size() - 1; i++) |
| result.append("." + tokens[i]); |
| } |
| result.append("]" + tokens[tokens.size() - 1]); |
| return result; |
| } |
| |
| if (tokens.size() == 1) |
| return unix_path; |
| |
| std::string result("["); |
| for (size_t i = 0; i < tokens.size() - 1; i++) |
| result.append("." + tokens[i]); |
| result.append("]" + tokens[tokens.size() - 1]); |
| return result; |
| } |
| |
| // static |
| std::string FtpUtil::UnixDirectoryPathToVMS(const std::string& unix_path) { |
| if (unix_path.empty()) |
| return std::string(); |
| |
| std::string path(unix_path); |
| |
| if (path[path.length() - 1] != '/') |
| path.append("/"); |
| |
| // Reuse logic from UnixFilePathToVMS by appending a fake file name to the |
| // real path and removing it after conversion. |
| path.append("x"); |
| path = UnixFilePathToVMS(path); |
| return path.substr(0, path.length() - 1); |
| } |
| |
| // static |
| std::string FtpUtil::VMSPathToUnix(const std::string& vms_path) { |
| if (vms_path.empty()) |
| return "."; |
| |
| if (vms_path == "[]") |
| return "/"; |
| |
| std::string result(vms_path); |
| if (vms_path[0] == '[') { |
| // It's a relative path. |
| ReplaceFirstSubstringAfterOffset(&result, 0, "[.", ""); |
| } else { |
| // It's an absolute path. |
| result.insert(0, "/"); |
| ReplaceSubstringsAfterOffset(&result, 0, ":[000000]", "/"); |
| ReplaceSubstringsAfterOffset(&result, 0, ":[", "/"); |
| } |
| std::replace(result.begin(), result.end(), '.', '/'); |
| std::replace(result.begin(), result.end(), ']', '/'); |
| |
| // Make sure the result doesn't end with a slash. |
| if (result.length() && result[result.length() - 1] == '/') |
| result = result.substr(0, result.length() - 1); |
| |
| return result; |
| } |
| |
| // static |
| bool FtpUtil::ThreeLetterMonthToNumber(const string16& text, int* number) { |
| const static char* months[] = { "jan", "feb", "mar", "apr", "may", "jun", |
| "jul", "aug", "sep", "oct", "nov", "dec" }; |
| |
| for (size_t i = 0; i < arraysize(months); i++) { |
| if (LowerCaseEqualsASCII(text, months[i])) { |
| *number = i + 1; |
| return true; |
| } |
| } |
| |
| // Special cases for directory listings in German (other three-letter month |
| // abbreviations are the same as in English). Note that we don't need to do |
| // a case-insensitive compare here. Only "ls -l" style listings may use |
| // localized month names, and they will always start capitalized. Also, |
| // converting non-ASCII characters to lowercase would be more complicated. |
| if (text == UTF8ToUTF16("M\xc3\xa4r")) { |
| // The full month name is M-(a-umlaut)-rz (March), which is M-(a-umlaut)r |
| // when abbreviated. |
| *number = 3; |
| return true; |
| } |
| if (text == ASCIIToUTF16("Mai")) { |
| *number = 5; |
| return true; |
| } |
| if (text == ASCIIToUTF16("Okt")) { |
| *number = 10; |
| return true; |
| } |
| if (text == ASCIIToUTF16("Dez")) { |
| *number = 12; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| // static |
| bool FtpUtil::LsDateListingToTime(const string16& month, const string16& day, |
| const string16& rest, |
| const base::Time& current_time, |
| base::Time* result) { |
| base::Time::Exploded time_exploded = { 0 }; |
| |
| if (!ThreeLetterMonthToNumber(month, &time_exploded.month)) |
| return false; |
| |
| if (!base::StringToInt(day, &time_exploded.day_of_month)) |
| return false; |
| |
| if (!base::StringToInt(rest, &time_exploded.year)) { |
| // Maybe it's time. Does it look like time (HH:MM)? |
| if (rest.length() == 5 && rest[2] == ':') { |
| if (!base::StringToInt(rest.begin(), |
| rest.begin() + 2, |
| &time_exploded.hour)) |
| return false; |
| |
| if (!base::StringToInt(rest.begin() + 3, |
| rest.begin() + 5, |
| &time_exploded.minute)) |
| return false; |
| } else if (rest.length() == 4 && rest[1] == ':') { |
| // Sometimes it's just H:MM. |
| if (!base::StringToInt(rest.begin(), |
| rest.begin() + 1, |
| &time_exploded.hour)) |
| return false; |
| |
| if (!base::StringToInt(rest.begin() + 2, |
| rest.begin() + 4, |
| &time_exploded.minute)) |
| return false; |
| } else { |
| return false; |
| } |
| |
| // Guess the year. |
| base::Time::Exploded current_exploded; |
| current_time.LocalExplode(¤t_exploded); |
| |
| // If it's not possible for the parsed date to be in the current year, |
| // use the previous year. |
| if (time_exploded.month > current_exploded.month || |
| (time_exploded.month == current_exploded.month && |
| time_exploded.day_of_month > current_exploded.day_of_month)) { |
| time_exploded.year = current_exploded.year - 1; |
| } else { |
| time_exploded.year = current_exploded.year; |
| } |
| } |
| |
| // We don't know the time zone of the listing, so just use local time. |
| *result = base::Time::FromLocalExploded(time_exploded); |
| return true; |
| } |
| |
| // static |
| string16 FtpUtil::GetStringPartAfterColumns(const string16& text, int columns) { |
| size_t pos = 0; |
| |
| for (int i = 0; i < columns; i++) { |
| // Skip the leading whitespace. |
| while (pos < text.length() && isspace(text[pos])) |
| pos++; |
| |
| // Skip the actual text of i-th column. |
| while (pos < text.length() && !isspace(text[pos])) |
| pos++; |
| } |
| |
| string16 result(text.substr(pos)); |
| TrimWhitespace(result, TRIM_ALL, &result); |
| return result; |
| } |
| |
| } // namespace |