summaryrefslogtreecommitdiffstats
path: root/net/ftp
diff options
context:
space:
mode:
authorphajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-09-24 21:46:50 +0000
committerphajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-09-24 21:46:50 +0000
commitb1c7cc42d026a073d1db2e7fa293be525e936738 (patch)
treeff24b7ee9676f5b6ab1f49106bddd329ef3da7e2 /net/ftp
parent63cdfb96a08d384963ee792c4a6687009b0dc10b (diff)
downloadchromium_src-b1c7cc42d026a073d1db2e7fa293be525e936738.zip
chromium_src-b1c7cc42d026a073d1db2e7fa293be525e936738.tar.gz
chromium_src-b1c7cc42d026a073d1db2e7fa293be525e936738.tar.bz2
Correctly talk to VMS servers (translate UNIX paths to VMS and vice versa).
This way we get a native VMS listing and not a UNIX emulation mode, which is slightly different than native UNIX and confuses the parser. TEST=Covered by net_unittests. BUG=22193 Review URL: http://codereview.chromium.org/215058 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@27129 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/ftp')
-rw-r--r--net/ftp/ftp_network_transaction.cc77
-rw-r--r--net/ftp/ftp_network_transaction.h20
-rw-r--r--net/ftp/ftp_network_transaction_unittest.cc123
-rw-r--r--net/ftp/ftp_util.cc107
-rw-r--r--net/ftp/ftp_util.h26
-rw-r--r--net/ftp/ftp_util_unittest.cc101
6 files changed, 442 insertions, 12 deletions
diff --git a/net/ftp/ftp_network_transaction.cc b/net/ftp/ftp_network_transaction.cc
index 5a90885..79ad77e 100644
--- a/net/ftp/ftp_network_transaction.cc
+++ b/net/ftp/ftp_network_transaction.cc
@@ -13,6 +13,7 @@
#include "net/base/net_util.h"
#include "net/ftp/ftp_network_session.h"
#include "net/ftp/ftp_request_info.h"
+#include "net/ftp/ftp_util.h"
#include "net/socket/client_socket.h"
#include "net/socket/client_socket_factory.h"
@@ -60,6 +61,7 @@ FtpNetworkTransaction::FtpNetworkTransaction(
file_data_len_(0),
write_command_buf_written_(0),
last_error_(OK),
+ system_type_(SYSTEM_TYPE_UNKNOWN),
retr_failed_(false),
data_connection_port_(0),
socket_factory_(socket_factory),
@@ -323,13 +325,28 @@ void FtpNetworkTransaction::OnIOComplete(int result) {
DoCallback(rv);
}
-std::string FtpNetworkTransaction::GetRequestPathForFtpCommand() const {
- std::string path = (request_->url.has_path() ? request_->url.path() : "/");
+std::string FtpNetworkTransaction::GetRequestPathForFtpCommand(
+ bool is_directory) const {
+ std::string path(current_remote_directory_);
+ if (request_->url.has_path())
+ path.append(request_->url.path());
+ // Make sure that if the path is expected to be a file, it won't end
+ // with a trailing slash.
+ if (!is_directory && path.length() > 1 && path[path.length() - 1] == '/')
+ path.erase(path.length() - 1);
UnescapeRule::Type unescape_rules = UnescapeRule::SPACES |
UnescapeRule::URL_SPECIAL_CHARS;
// This may unescape to non-ASCII characters, but we allow that. See the
// comment for IsValidFTPCommandString.
path = UnescapeURLComponent(path, unescape_rules);
+
+ if (system_type_ == SYSTEM_TYPE_VMS) {
+ if (is_directory)
+ path = FtpUtil::UnixDirectoryPathToVMS(path);
+ else
+ path = FtpUtil::UnixFilePathToVMS(path);
+ }
+
DCHECK(IsValidFTPCommandString(path));
return path;
}
@@ -634,10 +651,32 @@ int FtpNetworkTransaction::ProcessResponseSYST(
switch (GetErrorClass(response.status_code)) {
case ERROR_CLASS_INITIATED:
return Stop(ERR_INVALID_RESPONSE);
- case ERROR_CLASS_OK:
- // TODO(ibrar): Process SYST response properly.
+ case ERROR_CLASS_OK: {
+ // All important info should be on the first line.
+ std::string line = response.lines[0];
+ // The response should be ASCII, which allows us to do case-insensitive
+ // comparisons easily. If it is not ASCII, we leave the system type
+ // as unknown.
+ if (IsStringASCII(line)) {
+ line = StringToLowerASCII(line);
+ // The "magic" strings we test for below have been gathered by an
+ // empirical study.
+ if (line.find("l8") != std::string::npos ||
+ line.find("unix") != std::string::npos ||
+ line.find("bsd") != std::string::npos) {
+ system_type_ = SYSTEM_TYPE_UNIX;
+ } else if (line.find("win32") != std::string::npos ||
+ line.find("windows") != std::string::npos) {
+ system_type_ = SYSTEM_TYPE_WINDOWS;
+ } else if (line.find("os/2") != std::string::npos) {
+ system_type_ = SYSTEM_TYPE_OS2;
+ } else if (line.find("vms") != std::string::npos) {
+ system_type_ = SYSTEM_TYPE_VMS;
+ }
+ }
next_state_ = STATE_CTRL_WRITE_PWD;
break;
+ }
case ERROR_CLASS_INFO_NEEDED:
return Stop(ERR_INVALID_RESPONSE);
case ERROR_CLASS_TRANSIENT_ERROR:
@@ -664,9 +703,27 @@ int FtpNetworkTransaction::ProcessResponsePWD(const FtpCtrlResponse& response) {
switch (GetErrorClass(response.status_code)) {
case ERROR_CLASS_INITIATED:
return Stop(ERR_INVALID_RESPONSE);
- case ERROR_CLASS_OK:
+ case ERROR_CLASS_OK: {
+ // The info we look for should be on the first line.
+ std::string line = response.lines[0];
+ if (line.empty())
+ return Stop(ERR_INVALID_RESPONSE);
+ std::string::size_type quote_pos = line.find('"');
+ if (quote_pos != std::string::npos) {
+ line = line.substr(quote_pos + 1);
+ quote_pos = line.find('"');
+ if (quote_pos == std::string::npos)
+ return Stop(ERR_INVALID_RESPONSE);
+ line = line.substr(0, quote_pos);
+ }
+ if (system_type_ == SYSTEM_TYPE_VMS)
+ line = FtpUtil::VMSPathToUnix(line);
+ if (line[line.length() - 1] == '/')
+ line.erase(line.length() - 1);
+ current_remote_directory_ = line;
next_state_ = STATE_CTRL_WRITE_TYPE;
break;
+ }
case ERROR_CLASS_INFO_NEEDED:
return Stop(ERR_INVALID_RESPONSE);
case ERROR_CLASS_TRANSIENT_ERROR:
@@ -800,7 +857,7 @@ int FtpNetworkTransaction::ProcessResponsePASV(
// SIZE command
int FtpNetworkTransaction::DoCtrlWriteSIZE() {
- std::string command = "SIZE " + GetRequestPathForFtpCommand();
+ std::string command = "SIZE " + GetRequestPathForFtpCommand(false);
next_state_ = STATE_CTRL_READ;
return SendFtpCommand(command, COMMAND_SIZE);
}
@@ -834,7 +891,7 @@ int FtpNetworkTransaction::ProcessResponseSIZE(
// RETR command
int FtpNetworkTransaction::DoCtrlWriteRETR() {
- std::string command = "RETR " + GetRequestPathForFtpCommand();
+ std::string command = "RETR " + GetRequestPathForFtpCommand(false);
next_state_ = STATE_CTRL_READ;
return SendFtpCommand(command, COMMAND_RETR);
}
@@ -881,7 +938,7 @@ int FtpNetworkTransaction::ProcessResponseRETR(
// MDMT command
int FtpNetworkTransaction::DoCtrlWriteMDTM() {
- std::string command = "MDTM " + GetRequestPathForFtpCommand();
+ std::string command = "MDTM " + GetRequestPathForFtpCommand(false);
next_state_ = STATE_CTRL_READ;
return SendFtpCommand(command, COMMAND_MDTM);
}
@@ -911,7 +968,7 @@ int FtpNetworkTransaction::ProcessResponseMDTM(
// CWD command
int FtpNetworkTransaction::DoCtrlWriteCWD() {
- std::string command = "CWD " + GetRequestPathForFtpCommand();
+ std::string command = "CWD " + GetRequestPathForFtpCommand(true);
next_state_ = STATE_CTRL_READ;
return SendFtpCommand(command, COMMAND_CWD);
}
@@ -945,7 +1002,7 @@ int FtpNetworkTransaction::ProcessResponseCWD(const FtpCtrlResponse& response) {
// LIST command
int FtpNetworkTransaction::DoCtrlWriteLIST() {
- std::string command = "LIST";
+ std::string command(system_type_ == SYSTEM_TYPE_VMS ? "LIST *.*;0" : "LIST");
next_state_ = STATE_CTRL_READ;
return SendFtpCommand(command, COMMAND_LIST);
}
diff --git a/net/ftp/ftp_network_transaction.h b/net/ftp/ftp_network_transaction.h
index 3366c71..e621a9b 100644
--- a/net/ftp/ftp_network_transaction.h
+++ b/net/ftp/ftp_network_transaction.h
@@ -85,6 +85,15 @@ class FtpNetworkTransaction : public FtpTransaction {
ERROR_CLASS_PERMANENT_ERROR,
};
+ // Major categories of remote system types, as returned by SYST command.
+ enum SystemType {
+ SYSTEM_TYPE_UNKNOWN,
+ SYSTEM_TYPE_UNIX,
+ SYSTEM_TYPE_WINDOWS,
+ SYSTEM_TYPE_OS2,
+ SYSTEM_TYPE_VMS,
+ };
+
// Resets the members of the transaction so it can be restarted.
void ResetStateForRestart();
@@ -101,8 +110,9 @@ class FtpNetworkTransaction : public FtpTransaction {
// code to be in range 100-599.
static ErrorClass GetErrorClass(int response_code);
- // Returns request path suitable to be included in an FTP command.
- std::string GetRequestPathForFtpCommand() const;
+ // Returns request path suitable to be included in an FTP command. If the path
+ // will be used as a directory, |is_directory| should be true.
+ std::string GetRequestPathForFtpCommand(bool is_directory) const;
// Runs the state transition loop.
int DoLoop(int result);
@@ -190,11 +200,17 @@ class FtpNetworkTransaction : public FtpTransaction {
int last_error_;
+ SystemType system_type_;
+
// We get username and password as wstrings in RestartWithAuth, so they are
// also kept as wstrings here.
std::wstring username_;
std::wstring password_;
+ // Current directory on the remote server, as returned by last PWD command,
+ // with any trailing slash removed.
+ std::string current_remote_directory_;
+
bool retr_failed_;
int data_connection_port_;
diff --git a/net/ftp/ftp_network_transaction_unittest.cc b/net/ftp/ftp_network_transaction_unittest.cc
index 54b0501..ba765a7 100644
--- a/net/ftp/ftp_network_transaction_unittest.cc
+++ b/net/ftp/ftp_network_transaction_unittest.cc
@@ -186,6 +186,82 @@ class FtpMockControlSocketDirectoryListing : public FtpMockControlSocket {
DISALLOW_COPY_AND_ASSIGN(FtpMockControlSocketDirectoryListing);
};
+class FtpMockControlSocketVMSDirectoryListing : public FtpMockControlSocket {
+ public:
+ FtpMockControlSocketVMSDirectoryListing() {
+ }
+
+ virtual MockWriteResult OnWrite(const std::string& data) {
+ if (InjectFault())
+ return MockWriteResult(true, data.length());
+ switch (state()) {
+ case PRE_SYST:
+ return Verify("SYST\r\n", data, PRE_PWD, "215 VMS\r\n");
+ case PRE_PWD:
+ return Verify("PWD\r\n", data, PRE_TYPE,
+ "257 \"ANONYMOUS_ROOT:[000000]\"\r\n");
+ case PRE_SIZE:
+ return Verify("SIZE ANONYMOUS_ROOT:[000000]dir\r\n", data, PRE_MDTM,
+ "550 I can only retrieve regular files\r\n");
+ case PRE_MDTM:
+ return Verify("MDTM ANONYMOUS_ROOT:[000000]dir\r\n", data, PRE_RETR,
+ "213 20070221112533\r\n");
+ case PRE_RETR:
+ return Verify("RETR ANONYMOUS_ROOT:[000000]dir\r\n", data, PRE_CWD,
+ "550 Can't download directory\r\n");
+ case PRE_CWD:
+ return Verify("CWD ANONYMOUS_ROOT:[dir]\r\n", data, PRE_LIST,
+ "200 OK\r\n");
+ case PRE_LIST:
+ return Verify("LIST *.*;0\r\n", data, PRE_QUIT, "200 OK\r\n");
+ default:
+ return FtpMockControlSocket::OnWrite(data);
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FtpMockControlSocketVMSDirectoryListing);
+};
+
+class FtpMockControlSocketVMSDirectoryListingRootDirectory
+ : public FtpMockControlSocket {
+ public:
+ FtpMockControlSocketVMSDirectoryListingRootDirectory() {
+ }
+
+ virtual MockWriteResult OnWrite(const std::string& data) {
+ if (InjectFault())
+ return MockWriteResult(true, data.length());
+ switch (state()) {
+ case PRE_SYST:
+ return Verify("SYST\r\n", data, PRE_PWD, "215 VMS\r\n");
+ case PRE_PWD:
+ return Verify("PWD\r\n", data, PRE_TYPE,
+ "257 \"ANONYMOUS_ROOT:[000000]\"\r\n");
+ case PRE_SIZE:
+ return Verify("SIZE ANONYMOUS_ROOT\r\n", data, PRE_MDTM,
+ "550 I can only retrieve regular files\r\n");
+ case PRE_MDTM:
+ return Verify("MDTM ANONYMOUS_ROOT\r\n", data, PRE_RETR,
+ "213 20070221112533\r\n");
+ case PRE_RETR:
+ return Verify("RETR ANONYMOUS_ROOT\r\n", data, PRE_CWD,
+ "550 Can't download directory\r\n");
+ case PRE_CWD:
+ return Verify("CWD ANONYMOUS_ROOT:[000000]\r\n", data, PRE_LIST,
+ "200 OK\r\n");
+ case PRE_LIST:
+ return Verify("LIST *.*;0\r\n", data, PRE_QUIT, "200 OK\r\n");
+ default:
+ return FtpMockControlSocket::OnWrite(data);
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(
+ FtpMockControlSocketVMSDirectoryListingRootDirectory);
+};
+
class FtpMockControlSocketFileDownload : public FtpMockControlSocket {
public:
FtpMockControlSocketFileDownload() {
@@ -212,6 +288,38 @@ class FtpMockControlSocketFileDownload : public FtpMockControlSocket {
DISALLOW_COPY_AND_ASSIGN(FtpMockControlSocketFileDownload);
};
+class FtpMockControlSocketVMSFileDownload : public FtpMockControlSocket {
+ public:
+ FtpMockControlSocketVMSFileDownload() {
+ }
+
+ virtual MockWriteResult OnWrite(const std::string& data) {
+ if (InjectFault())
+ return MockWriteResult(true, data.length());
+ switch (state()) {
+ case PRE_SYST:
+ return Verify("SYST\r\n", data, PRE_PWD, "215 VMS\r\n");
+ case PRE_PWD:
+ return Verify("PWD\r\n", data, PRE_TYPE,
+ "257 \"ANONYMOUS_ROOT:[000000]\"\r\n");
+ case PRE_SIZE:
+ return Verify("SIZE ANONYMOUS_ROOT:[000000]file\r\n", data, PRE_MDTM,
+ "213 18\r\n");
+ case PRE_MDTM:
+ return Verify("MDTM ANONYMOUS_ROOT:[000000]file\r\n", data, PRE_RETR,
+ "213 20070221112533\r\n");
+ case PRE_RETR:
+ return Verify("RETR ANONYMOUS_ROOT:[000000]file\r\n", data, PRE_QUIT,
+ "200 OK\r\n");
+ default:
+ return FtpMockControlSocket::OnWrite(data);
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FtpMockControlSocketVMSFileDownload);
+};
+
class FtpMockControlSocketEscaping : public FtpMockControlSocket {
public:
FtpMockControlSocketEscaping() {
@@ -491,6 +599,16 @@ TEST_F(FtpNetworkTransactionTest, DirectoryTransactionMultilineWelcomeShort) {
ExecuteTransaction(&ctrl_socket, "ftp://host", OK);
}
+TEST_F(FtpNetworkTransactionTest, DirectoryTransactionVMS) {
+ FtpMockControlSocketVMSDirectoryListing ctrl_socket;
+ ExecuteTransaction(&ctrl_socket, "ftp://host/dir", OK);
+}
+
+TEST_F(FtpNetworkTransactionTest, DirectoryTransactionVMSRootDirectory) {
+ FtpMockControlSocketVMSDirectoryListingRootDirectory ctrl_socket;
+ ExecuteTransaction(&ctrl_socket, "ftp://host", OK);
+}
+
TEST_F(FtpNetworkTransactionTest, DownloadTransaction) {
FtpMockControlSocketFileDownload ctrl_socket;
ExecuteTransaction(&ctrl_socket, "ftp://host/file", OK);
@@ -514,6 +632,11 @@ TEST_F(FtpNetworkTransactionTest, DownloadTransactionShortReads5) {
ExecuteTransaction(&ctrl_socket, "ftp://host/file", OK);
}
+TEST_F(FtpNetworkTransactionTest, DownloadTransactionVMS) {
+ FtpMockControlSocketVMSFileDownload ctrl_socket;
+ ExecuteTransaction(&ctrl_socket, "ftp://host/file", OK);
+}
+
TEST_F(FtpNetworkTransactionTest, DownloadTransactionAcceptedDataConnection) {
FtpMockControlSocketFileDownloadAcceptedDataConnection ctrl_socket;
std::string mock_data("mock-data");
diff --git a/net/ftp/ftp_util.cc b/net/ftp/ftp_util.cc
new file mode 100644
index 0000000..591f7b2
--- /dev/null
+++ b/net/ftp/ftp_util.cc
@@ -0,0 +1,107 @@
+// 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_tokenizer.h"
+#include "base/string_util.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[result.length() - 1] == '/')
+ result = result.substr(0, result.length() - 1);
+
+ return result;
+}
+
+} // namespace
diff --git a/net/ftp/ftp_util.h b/net/ftp/ftp_util.h
new file mode 100644
index 0000000..c71b919
--- /dev/null
+++ b/net/ftp/ftp_util.h
@@ -0,0 +1,26 @@
+// 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.
+
+#ifndef NET_FTP_FTP_UTIL_H_
+#define NET_FTP_FTP_UTIL_H_
+
+#include <string>
+
+namespace net {
+
+class FtpUtil {
+ public:
+ // Convert Unix file path to VMS path (must be a file, and not a directory).
+ static std::string UnixFilePathToVMS(const std::string& unix_path);
+
+ // Convert Unix directory path to VMS path (must be a directory).
+ static std::string UnixDirectoryPathToVMS(const std::string& unix_path);
+
+ // Convert VMS path to Unix-style path.
+ static std::string VMSPathToUnix(const std::string& vms_path);
+};
+
+} // namespace net
+
+#endif // NET_FTP_FTP_UTIL_H_
diff --git a/net/ftp/ftp_util_unittest.cc b/net/ftp/ftp_util_unittest.cc
new file mode 100644
index 0000000..2ccb75b
--- /dev/null
+++ b/net/ftp/ftp_util_unittest.cc
@@ -0,0 +1,101 @@
+// 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 "base/basictypes.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+TEST(FtpUtilTest, UnixFilePathToVMS) {
+ const struct {
+ const char* input;
+ const char* expected_output;
+ } kTestCases[] = {
+ { "", "" },
+ { "/", "[]" },
+ { "/a", "a" },
+ { "/a/b", "a:[000000]b" },
+ { "/a/b/c", "a:[b]c" },
+ { "/a/b/c/d", "a:[b.c]d" },
+ { "/a/b/c/d/e", "a:[b.c.d]e" },
+ { "a", "a" },
+ { "a/b", "[.a]b" },
+ { "a/b/c", "[.a.b]c" },
+ { "a/b/c/d", "[.a.b.c]d" },
+ };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); i++) {
+ EXPECT_EQ(kTestCases[i].expected_output,
+ net::FtpUtil::UnixFilePathToVMS(kTestCases[i].input))
+ << kTestCases[i].input;
+ }
+}
+
+TEST(FtpUtilTest, UnixDirectoryPathToVMS) {
+ const struct {
+ const char* input;
+ const char* expected_output;
+ } kTestCases[] = {
+ { "", "" },
+ { "/", "" },
+ { "/a", "a:[000000]" },
+ { "/a/", "a:[000000]" },
+ { "/a/b", "a:[b]" },
+ { "/a/b/", "a:[b]" },
+ { "/a/b/c", "a:[b.c]" },
+ { "/a/b/c/", "a:[b.c]" },
+ { "/a/b/c/d", "a:[b.c.d]" },
+ { "/a/b/c/d/", "a:[b.c.d]" },
+ { "/a/b/c/d/e", "a:[b.c.d.e]" },
+ { "/a/b/c/d/e/", "a:[b.c.d.e]" },
+ { "a", "[.a]" },
+ { "a/", "[.a]" },
+ { "a/b", "[.a.b]" },
+ { "a/b/", "[.a.b]" },
+ { "a/b/c", "[.a.b.c]" },
+ { "a/b/c/", "[.a.b.c]" },
+ { "a/b/c/d", "[.a.b.c.d]" },
+ { "a/b/c/d/", "[.a.b.c.d]" },
+ };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); i++) {
+ EXPECT_EQ(kTestCases[i].expected_output,
+ net::FtpUtil::UnixDirectoryPathToVMS(kTestCases[i].input))
+ << kTestCases[i].input;
+ }
+}
+
+TEST(FtpUtilTest, VMSPathToUnix) {
+ const struct {
+ const char* input;
+ const char* expected_output;
+ } kTestCases[] = {
+ { "", "." },
+ { "[]", "/" },
+ { "a", "/a" },
+ { "a:[000000]", "/a" },
+ { "a:[000000]b", "/a/b" },
+ { "a:[b]", "/a/b" },
+ { "a:[b]c", "/a/b/c" },
+ { "a:[b.c]", "/a/b/c" },
+ { "a:[b.c]d", "/a/b/c/d" },
+ { "a:[b.c.d]", "/a/b/c/d" },
+ { "a:[b.c.d]e", "/a/b/c/d/e" },
+ { "a:[b.c.d.e]", "/a/b/c/d/e" },
+ { "[.a]", "a" },
+ { "[.a]b", "a/b" },
+ { "[.a.b]", "a/b" },
+ { "[.a.b]c", "a/b/c" },
+ { "[.a.b.c]", "a/b/c" },
+ { "[.a.b.c]d", "a/b/c/d" },
+ { "[.a.b.c.d]", "a/b/c/d" },
+ };
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); i++) {
+ EXPECT_EQ(kTestCases[i].expected_output,
+ net::FtpUtil::VMSPathToUnix(kTestCases[i].input))
+ << kTestCases[i].input;
+ }
+}
+
+} // namespace