diff options
author | wtc@chromium.org <wtc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-11 17:01:17 +0000 |
---|---|---|
committer | wtc@chromium.org <wtc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-11 17:01:17 +0000 |
commit | 581e0d06dc3599bec309d34203b6b5693446fe7f (patch) | |
tree | c8c11535827d0b5302d07b257c12d998f8a9edb3 /net/ftp | |
parent | a63bd1137e938034873b318355731c656caf05fe (diff) | |
download | chromium_src-581e0d06dc3599bec309d34203b6b5693446fe7f.zip chromium_src-581e0d06dc3599bec309d34203b6b5693446fe7f.tar.gz chromium_src-581e0d06dc3599bec309d34203b6b5693446fe7f.tar.bz2 |
FTP Transaction code for new Portable FTP code.
The patch is contributed by Ibrar Ahmed <ibrar.ahmad@gmail.com>.
Original review: http://codereview.chromium.org/39130
R=wtc
http://crbug.com/4965
TEST=None. Work in progress.
Review URL: http://codereview.chromium.org/115137
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15760 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/ftp')
-rw-r--r-- | net/ftp/ftp_network_layer.cc | 9 | ||||
-rw-r--r-- | net/ftp/ftp_network_layer.h | 4 | ||||
-rw-r--r-- | net/ftp/ftp_network_transaction.cc | 653 | ||||
-rw-r--r-- | net/ftp/ftp_network_transaction.h | 129 | ||||
-rw-r--r-- | net/ftp/ftp_request_info.h | 12 | ||||
-rw-r--r-- | net/ftp/ftp_response_info.h | 15 | ||||
-rw-r--r-- | net/ftp/ftp_transaction.h | 9 | ||||
-rw-r--r-- | net/ftp/ftp_transaction_factory.h | 4 |
8 files changed, 754 insertions, 81 deletions
diff --git a/net/ftp/ftp_network_layer.cc b/net/ftp/ftp_network_layer.cc index 06884ce..e05b67f 100644 --- a/net/ftp/ftp_network_layer.cc +++ b/net/ftp/ftp_network_layer.cc @@ -18,6 +18,11 @@ FtpNetworkLayer::FtpNetworkLayer() FtpNetworkLayer::~FtpNetworkLayer() { } +// static +FtpTransactionFactory* FtpNetworkLayer::CreateFactory() { + return new FtpNetworkLayer(); +} + FtpTransaction* FtpNetworkLayer::CreateTransaction() { if (suspended_) return NULL; @@ -26,10 +31,6 @@ FtpTransaction* FtpNetworkLayer::CreateTransaction() { session_, ClientSocketFactory::GetDefaultFactory()); } -FtpAuthCache* FtpNetworkLayer::GetAuthCache() { - return session_->auth_cache(); -} - void FtpNetworkLayer::Suspend(bool suspend) { suspended_ = suspend; diff --git a/net/ftp/ftp_network_layer.h b/net/ftp/ftp_network_layer.h index fa5c430..71bd3b9 100644 --- a/net/ftp/ftp_network_layer.h +++ b/net/ftp/ftp_network_layer.h @@ -11,15 +11,17 @@ namespace net { class FtpNetworkSession; +class FtpAuthCache; class FtpNetworkLayer : public FtpTransactionFactory { public: FtpNetworkLayer(); ~FtpNetworkLayer(); + static FtpTransactionFactory* CreateFactory(); + // FtpTransactionFactory methods: virtual FtpTransaction* CreateTransaction(); - virtual FtpAuthCache* GetAuthCache(); virtual void Suspend(bool suspend); private: diff --git a/net/ftp/ftp_network_transaction.cc b/net/ftp/ftp_network_transaction.cc index 3ace779..d8e5a82 100644 --- a/net/ftp/ftp_network_transaction.cc +++ b/net/ftp/ftp_network_transaction.cc @@ -5,32 +5,52 @@ #include "net/ftp/ftp_network_transaction.h" #include "base/compiler_specific.h" -#include "base/logging.h" +#include "base/string_util.h" #include "net/base/client_socket.h" +#include "net/base/client_socket_factory.h" +#include "net/base/connection_type_histograms.h" +#include "net/base/dns_resolution_observer.h" #include "net/base/net_errors.h" #include "net/ftp/ftp_network_session.h" +#include "net/ftp/ftp_request_info.h" + +// TODO(ibrar): Try to avoid sscanf. +#if !defined(COMPILER_MSVC) +#define sscanf_s sscanf +#endif + +const char kCRLF[] = "\r\n"; + +const int kCtrlBufLen = 1024; namespace net { FtpNetworkTransaction::FtpNetworkTransaction( - FtpNetworkSession* session, ClientSocketFactory* socket_factory) - : ALLOW_THIS_IN_INITIALIZER_LIST(io_callback_(this, - &FtpNetworkTransaction::OnIOComplete)), + FtpNetworkSession* session, + ClientSocketFactory* socket_factory) + : command_sent_(COMMAND_NONE), + ALLOW_THIS_IN_INITIALIZER_LIST( + io_callback_(this, &FtpNetworkTransaction::OnIOComplete)), user_callback_(NULL), session_(session), + request_(NULL), + read_ctrl_buf_size_(kCtrlBufLen), + response_message_buf_len_(0), + read_data_buf_len_(0), + file_data_len_(0), + last_error_(OK), + data_connection_port_(0), socket_factory_(socket_factory), next_state_(STATE_NONE) { + read_ctrl_buf_ = new IOBuffer(kCtrlBufLen); + response_message_buf_ = new IOBuffer(kCtrlBufLen); } FtpNetworkTransaction::~FtpNetworkTransaction() { } -void FtpNetworkTransaction::Destroy() { - delete this; -} - -int FtpNetworkTransaction::Start( - const FtpRequestInfo* request_info, CompletionCallback* callback) { +int FtpNetworkTransaction::Start(const FtpRequestInfo* request_info, + CompletionCallback* callback) { request_ = request_info; next_state_ = STATE_CTRL_INIT; @@ -40,19 +60,50 @@ int FtpNetworkTransaction::Start( return rv; } -int FtpNetworkTransaction::RestartWithAuth( - const std::wstring& username, const std::wstring& password, - CompletionCallback* callback) { +int FtpNetworkTransaction::Stop(int error) { + next_state_ = STATE_CTRL_WRITE_QUIT; + last_error_ = error; + return OK; +} + +int FtpNetworkTransaction::RestartWithAuth(const std::wstring& username, + const std::wstring& password, + CompletionCallback* callback) { return ERR_FAILED; } -int FtpNetworkTransaction::Read( - char* buf, int buf_len, CompletionCallback* callback) { +int FtpNetworkTransaction::RestartIgnoringLastError( + CompletionCallback* callback) { return ERR_FAILED; } +int FtpNetworkTransaction::Read(IOBuffer* buf, + int buf_len, + CompletionCallback* callback) { + DCHECK(buf); + DCHECK(buf_len > 0); + + if (data_socket_ == NULL) + return 0; // Data socket closed, no more data left. + + if (!data_socket_->IsConnected()) + return 0; // Data socket disconnected, no more data left. + + read_data_buf_ = buf; + read_data_buf_len_ = buf_len; + + next_state_ = STATE_DATA_READ; + + int rv = DoLoop(OK); + if (rv == ERR_IO_PENDING) + user_callback_ = callback; + else if (rv == 0) + data_socket_->Disconnect(); + return rv; +} + const FtpResponseInfo* FtpNetworkTransaction::GetResponseInfo() const { - return NULL; + return &response_; } LoadState FtpNetworkTransaction::GetLoadState() const { @@ -63,6 +114,77 @@ uint64 FtpNetworkTransaction::GetUploadProgress() const { return 0; } +// Used to prepare and send FTP commad. +int FtpNetworkTransaction::SendFtpCommand(const std::string& command, + Command cmd) { + response_message_buf_len_ = 0; + command_sent_ = cmd; + DLOG(INFO) << " >> " << command; + const char* buf = command.c_str(); + int buf_len = command.size(); + DCHECK(!write_buf_); + write_buf_ = new IOBuffer(buf_len + 2); + memcpy(write_buf_->data(), buf, buf_len); + memcpy(write_buf_->data() + buf_len, kCRLF, 2); + + // TODO(ibrar): Handle the completion of Write and release write_buf_. + return ctrl_socket_->Write(write_buf_, buf_len, &io_callback_); +} + +int FtpNetworkTransaction::GetRespnseCode() { + std::string str(response_message_buf_->data(), 3); + return StringToInt(str); +} + +int FtpNetworkTransaction::ProcessResponse(int response_code) { + int rv = OK; + switch (command_sent_) { + case COMMAND_NONE: + next_state_ = STATE_CTRL_WRITE_USER; + break; + case COMMAND_USER: + rv = ProcessResponseUSER(response_code); + break; + case COMMAND_PASS: + rv = ProcessResponsePASS(response_code); + break; + case COMMAND_ACCT: + rv = ProcessResponseACCT(response_code); + break; + case COMMAND_SYST: + rv = ProcessResponseSYST(response_code); + break; + case COMMAND_PWD: + rv = ProcessResponsePWD(response_code); + break; + case COMMAND_TYPE: + rv = ProcessResponseTYPE(response_code); + break; + case COMMAND_PASV: + rv = ProcessResponsePASV(response_code); + break; + case COMMAND_SIZE: + rv = ProcessResponseSIZE(response_code); + break; + case COMMAND_RETR: + rv = ProcessResponseRETR(response_code); + break; + case COMMAND_CWD: + rv = ProcessResponseCWD(response_code); + break; + case COMMAND_LIST: + rv = ProcessResponseLIST(response_code); + break; + case COMMAND_QUIT: + rv = ProcessResponseQUIT(response_code); + break; + default: + DLOG(INFO) << "Missing Command response handling!"; + return ERR_FAILED; + } + return rv; +} + void FtpNetworkTransaction::DoCallback(int rv) { DCHECK(rv != ERR_IO_PENDING); DCHECK(user_callback_); @@ -108,20 +230,58 @@ int FtpNetworkTransaction::DoLoop(int result) { case STATE_CTRL_CONNECT_COMPLETE: rv = DoCtrlConnectComplete(rv); break; - case STATE_CTRL_WRITE: - DCHECK(rv == OK); - rv = DoCtrlWrite(); - break; - case STATE_CTRL_WRITE_COMPLETE: - rv = DoCtrlWriteComplete(rv); - break; case STATE_CTRL_READ: - DCHECK(rv == OK); rv = DoCtrlRead(); break; case STATE_CTRL_READ_COMPLETE: rv = DoCtrlReadComplete(rv); break; + case STATE_CTRL_WRITE_USER: + DCHECK(rv == OK); + rv = DoCtrlWriteUSER(); + break; + case STATE_CTRL_WRITE_PASS: + DCHECK(rv == OK); + rv = DoCtrlWritePASS(); + break; + case STATE_CTRL_WRITE_SYST: + DCHECK(rv == OK); + rv = DoCtrlWriteSYST(); + break; + case STATE_CTRL_WRITE_ACCT: + DCHECK(rv == OK); + rv = DoCtrlWriteACCT(); + break; + case STATE_CTRL_WRITE_PWD: + DCHECK(rv == OK); + rv = DoCtrlWritePWD(); + break; + case STATE_CTRL_WRITE_TYPE: + DCHECK(rv == OK); + rv = DoCtrlWriteTYPE(); + break; + case STATE_CTRL_WRITE_PASV: + DCHECK(rv == OK); + rv = DoCtrlWritePASV(); + break; + case STATE_CTRL_WRITE_RETR: + DCHECK(rv == OK); + rv = DoCtrlWriteRETR(); + break; + case STATE_CTRL_WRITE_SIZE: + DCHECK(rv == OK); + rv = DoCtrlWriteSIZE(); + break; + case STATE_CTRL_WRITE_CWD: + rv = DoCtrlWriteCWD(); + break; + case STATE_CTRL_WRITE_LIST: + rv = DoCtrlWriteLIST(); + break; + case STATE_CTRL_WRITE_QUIT: + rv = DoCtrlWriteQUIT(); + break; + case STATE_DATA_CONNECT: DCHECK(rv == OK); rv = DoDataConnect(); @@ -130,7 +290,6 @@ int FtpNetworkTransaction::DoLoop(int result) { rv = DoDataConnectComplete(rv); break; case STATE_DATA_READ: - DCHECK(rv == OK); rv = DoDataRead(); break; case STATE_DATA_READ_COMPLETE: @@ -142,64 +301,470 @@ int FtpNetworkTransaction::DoLoop(int result) { break; } } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); - return rv; } +// TODO(ibrar): Yet to see if we need any intialization int FtpNetworkTransaction::DoCtrlInit() { - return ERR_FAILED; // TODO(darin): implement me + next_state_ = STATE_CTRL_INIT_COMPLETE; + return OK; } int FtpNetworkTransaction::DoCtrlInitComplete(int result) { - return ERR_FAILED; // TODO(darin): implement me + next_state_ = STATE_CTRL_RESOLVE_HOST; + return OK; } int FtpNetworkTransaction::DoCtrlResolveHost() { - return ERR_FAILED; // TODO(darin): implement me + next_state_ = STATE_CTRL_RESOLVE_HOST_COMPLETE; + + std::string host; + int port; + + host = request_->url.host(); + port = request_->url.EffectiveIntPort(); + + DidStartDnsResolution(host, this); + return resolver_.Resolve(host, port, &addresses_, &io_callback_); } int FtpNetworkTransaction::DoCtrlResolveHostComplete(int result) { - return ERR_FAILED; // TODO(darin): implement me + bool ok = (result == OK); + DidFinishDnsResolutionWithStatus(ok, GURL(), this); + if (ok) { + next_state_ = STATE_CTRL_CONNECT; + return result; + } + return ERR_FAILED; } int FtpNetworkTransaction::DoCtrlConnect() { - return ERR_FAILED; // TODO(darin): implement me + next_state_ = STATE_CTRL_CONNECT_COMPLETE; + ctrl_socket_.reset(socket_factory_->CreateTCPClientSocket(addresses_)); + return ctrl_socket_->Connect(&io_callback_); } int FtpNetworkTransaction::DoCtrlConnectComplete(int result) { - return ERR_FAILED; // TODO(darin): implement me + next_state_ = STATE_CTRL_READ; + return result; +} + +int FtpNetworkTransaction::DoCtrlRead() { + next_state_ = STATE_CTRL_READ_COMPLETE; + read_ctrl_buf_->data()[0] = 0; + return ctrl_socket_->Read(read_ctrl_buf_, read_ctrl_buf_size_ - 1, + &io_callback_); } -int FtpNetworkTransaction::DoCtrlWrite() { - return ERR_FAILED; // TODO(darin): implement me +int FtpNetworkTransaction::DoCtrlReadComplete(int result) { + if (result < 0) + return Stop(ERR_FAILED); + int response_code; + // Null termination added, now we can treat this as string. + read_ctrl_buf_->data()[result] = 0; + memcpy(response_message_buf_->data() + response_message_buf_len_, + read_ctrl_buf_->data(), result); + + response_message_buf_len_ = response_message_buf_len_ + result; + for (int i = 0; i < response_message_buf_len_; i++) { + if (response_message_buf_->data()[i] == '\r' && + response_message_buf_->data()[i + 1] == '\n') { + if (response_message_buf_len_ > 3 && + response_message_buf_->data()[3] == ' ') { + response_message_buf_->data()[response_message_buf_len_ - 2] = 0; + response_code = GetRespnseCode(); + return ProcessResponse(response_code); + } + response_message_buf_len_ -= (i + 2); + memcpy(response_message_buf_->data(), + response_message_buf_->data() + i + 2, + response_message_buf_len_); + i = 0; + } + } + next_state_ = STATE_CTRL_READ; + return OK; } -int FtpNetworkTransaction::DoCtrlWriteComplete(int result) { - return ERR_FAILED; // TODO(darin): implement me +// FTP Commands and responses + +// USER Command. +int FtpNetworkTransaction::DoCtrlWriteUSER() { + std::string command = "USER "; + if (request_->url.has_username()) { + command.append(request_->url.username()); + } else { + command.append("anonymous"); + } + next_state_ = STATE_CTRL_READ; + return SendFtpCommand(command, COMMAND_USER); } -int FtpNetworkTransaction::DoCtrlRead() { - return ERR_FAILED; // TODO(darin): implement me +int FtpNetworkTransaction::ProcessResponseUSER(int response_code) { + switch (GetErrorClass(response_code)) { + case ERROR_CLASS_INITIATED: + case ERROR_CLASS_OK: + next_state_ = STATE_CTRL_WRITE_USER; + break; + case ERROR_CLASS_PENDING: + next_state_ = STATE_CTRL_WRITE_PASS; + break; + case ERROR_CLASS_ERROR_RETRY: + if (response_code == 421) + return Stop(ERR_FAILED); + break; + case ERROR_CLASS_ERROR: + return Stop(ERR_FAILED); + default: + return Stop(ERR_FAILED); + } + return OK; } -int FtpNetworkTransaction::DoCtrlReadComplete(int result) { - return ERR_FAILED; // TODO(darin): implement me +// PASS command. +int FtpNetworkTransaction::DoCtrlWritePASS() { + std::string command = "PASS "; + if (request_->url.has_password()) { + command.append(request_->url.password()); + } else { + command.append("IEUser@"); + } + next_state_ = STATE_CTRL_READ; + return SendFtpCommand(command, COMMAND_PASS); +} + +int FtpNetworkTransaction::ProcessResponsePASS(int response_code) { + switch (GetErrorClass(response_code)) { + case ERROR_CLASS_INITIATED: + case ERROR_CLASS_OK: + next_state_ = STATE_CTRL_WRITE_SYST; + break; + case ERROR_CLASS_PENDING: + next_state_ = STATE_CTRL_WRITE_ACCT; + break; + case ERROR_CLASS_ERROR_RETRY: + return Stop(ERR_FAILED); + case ERROR_CLASS_ERROR: + if (response_code == 503) { + next_state_ = STATE_CTRL_WRITE_PASS; + } else { + return Stop(ERR_FAILED); + } + break; + default: + return Stop(ERR_FAILED); + } + return OK; +} + +// ACCT command. +int FtpNetworkTransaction::DoCtrlWriteACCT() { + std::string command = "ACCT noaccount"; + next_state_ = STATE_CTRL_READ; + return SendFtpCommand(command, COMMAND_ACCT); +} + +int FtpNetworkTransaction::ProcessResponseACCT(int response_code) { + switch (GetErrorClass(response_code)) { + case ERROR_CLASS_INITIATED: + return Stop(ERR_FAILED); + case ERROR_CLASS_OK: + next_state_ = STATE_CTRL_WRITE_SYST; + break; + case ERROR_CLASS_PENDING: + return Stop(ERR_FAILED); + case ERROR_CLASS_ERROR_RETRY: + return Stop(ERR_FAILED); + case ERROR_CLASS_ERROR: + return Stop(ERR_FAILED); + default: + return Stop(ERR_FAILED); + } + return OK; +} + +// SYST command. +int FtpNetworkTransaction::DoCtrlWriteSYST() { + std::string command = "SYST"; + next_state_ = STATE_CTRL_READ; + return SendFtpCommand(command, COMMAND_SYST); +} + +int FtpNetworkTransaction::ProcessResponseSYST(int response_code) { + next_state_ = STATE_CTRL_WRITE_PWD; + return OK; +} + +// PWD command. +int FtpNetworkTransaction::DoCtrlWritePWD() { + std::string command = "PWD"; + next_state_ = STATE_CTRL_READ; + return SendFtpCommand(command, COMMAND_PWD); +} + +int FtpNetworkTransaction::ProcessResponsePWD(int response_code) { + switch (GetErrorClass(response_code)) { + case ERROR_CLASS_INITIATED: + return Stop(ERR_FAILED); + case ERROR_CLASS_OK: + next_state_ = STATE_CTRL_WRITE_TYPE; + break; + case ERROR_CLASS_PENDING: + return Stop(ERR_FAILED); + case ERROR_CLASS_ERROR_RETRY: + return Stop(ERR_FAILED); + case ERROR_CLASS_ERROR: + return Stop(ERR_FAILED); + default: + return Stop(ERR_FAILED); + } + return OK; +} + +// TYPE command. +int FtpNetworkTransaction::DoCtrlWriteTYPE() { + std::string command = "TYPE I"; + next_state_ = STATE_CTRL_READ; + return SendFtpCommand(command, COMMAND_TYPE); } +int FtpNetworkTransaction::ProcessResponseTYPE(int response_code) { + switch (GetErrorClass(response_code)) { + case ERROR_CLASS_INITIATED: + return Stop(ERR_FAILED); + case ERROR_CLASS_OK: + next_state_ = STATE_CTRL_WRITE_PASV; + break; + case ERROR_CLASS_PENDING: + return Stop(ERR_FAILED); + case ERROR_CLASS_ERROR_RETRY: + return Stop(ERR_FAILED); + case ERROR_CLASS_ERROR: + return Stop(ERR_FAILED); + default: + return Stop(ERR_FAILED); + } + return OK; +} + +// PASV command +int FtpNetworkTransaction::DoCtrlWritePASV() { + std::string command = "PASV "; + next_state_ = STATE_CTRL_READ; + return SendFtpCommand(command, COMMAND_PASV); +} + +// There are two way we can receive IP address and port. +// (127,0,0,1,23,21) IP address and port encapsulate in (). +// 127,0,0,1,23,21 IP address and port without (). +int FtpNetworkTransaction::ProcessResponsePASV(int response_code) { + switch (GetErrorClass(response_code)) { + case ERROR_CLASS_INITIATED: + return Stop(ERR_FAILED); + case ERROR_CLASS_OK: + char* ptr; + int i0, i1, i2, i3, p0, p1; + ptr = read_ctrl_buf_->data(); // Try with bracket. + while (*ptr && *ptr != '(') + ++ptr; + if (*ptr) { + ++ptr; + } else { + ptr = read_ctrl_buf_->data(); // Try without bracket. + while (*ptr && *ptr != ',') + ++ptr; + while (*ptr && *ptr != ' ') + --ptr; + } + if (sscanf_s(ptr, "%d,%d,%d,%d,%d,%d", + &i0, &i1, &i2, &i3, &p0, &p1) == 6) { + data_connection_ip_ = StringPrintf("%d.%d.%d.%d", i0, i1, i2, i3); + data_connection_port_ = (p0 << 8) + p1; + next_state_ = STATE_DATA_CONNECT; + } else { + return Stop(ERR_FAILED); + } + break; + case ERROR_CLASS_PENDING: + return Stop(ERR_FAILED); + case ERROR_CLASS_ERROR_RETRY: + return Stop(ERR_FAILED); + case ERROR_CLASS_ERROR: + return Stop(ERR_FAILED); + default: + return Stop(ERR_FAILED); + } + return OK; +} + +// SIZE command +int FtpNetworkTransaction::DoCtrlWriteSIZE() { + std::string command = "SIZE "; + if (request_->url.has_path()) + command.append(request_->url.path()); + next_state_ = STATE_CTRL_READ; + return SendFtpCommand(command, COMMAND_SIZE); +} + +int FtpNetworkTransaction::ProcessResponseSIZE(int response_code) { + switch (GetErrorClass(response_code)) { + case ERROR_CLASS_INITIATED: + next_state_ = STATE_CTRL_WRITE_LIST; + break; + case ERROR_CLASS_OK: + next_state_ = STATE_CTRL_WRITE_RETR; + if (!StringToInt(read_ctrl_buf_->data() + 4, &file_data_len_)) + return Stop(ERR_FAILED); + break; + case ERROR_CLASS_PENDING: + next_state_ = STATE_CTRL_WRITE_LIST; + break; + case ERROR_CLASS_ERROR_RETRY: + next_state_ = STATE_CTRL_WRITE_LIST; + break; + case ERROR_CLASS_ERROR: + next_state_ = STATE_CTRL_WRITE_LIST; + break; + default: + return Stop(ERR_FAILED); + } + return OK; +} + +// RETR command +int FtpNetworkTransaction::DoCtrlWriteRETR() { + std::string command = "RETR "; + if (request_->url.has_path()) + command.append(request_->url.path()); + next_state_ = STATE_CTRL_READ; + return SendFtpCommand(command, COMMAND_RETR); +} + +int FtpNetworkTransaction::ProcessResponseRETR(int response_code) { + switch (GetErrorClass(response_code)) { + case ERROR_CLASS_INITIATED: + next_state_ = STATE_CTRL_WRITE_QUIT; + ctrl_socket_->Disconnect(); + break; + case ERROR_CLASS_OK: + break; // FTP Done + case ERROR_CLASS_PENDING: + next_state_ = STATE_CTRL_WRITE_PASV; + break; + case ERROR_CLASS_ERROR_RETRY: + if (response_code == 421 || response_code == 425 || response_code == 426) + return Stop(ERR_FAILED); + return ERR_FAILED; // TODO(ibrar): Retry here. + case ERROR_CLASS_ERROR: + next_state_ = STATE_CTRL_WRITE_CWD; + break; + default: + return Stop(ERR_FAILED); + } + return OK; +} + +// CWD command +int FtpNetworkTransaction::DoCtrlWriteCWD() { + std::string command = "CWD"; + next_state_ = STATE_CTRL_READ; + return SendFtpCommand(command, COMMAND_CWD); +} + +int FtpNetworkTransaction::ProcessResponseCWD(int response_code) { + switch (GetErrorClass(response_code)) { + case ERROR_CLASS_INITIATED: + return Stop(ERR_FAILED); + case ERROR_CLASS_OK: + next_state_ = STATE_CTRL_WRITE_LIST; + case ERROR_CLASS_PENDING: + return Stop(ERR_FAILED); + case ERROR_CLASS_ERROR_RETRY: + return Stop(ERR_FAILED); + case ERROR_CLASS_ERROR: + return Stop(ERR_FAILED); + default: + return Stop(ERR_FAILED); + } + return OK; +} + +// LIST command +int FtpNetworkTransaction::DoCtrlWriteLIST() { + std::string command = "LIST "; + if (request_->url.has_path()) + command.append(request_->url.path()); + next_state_ = STATE_CTRL_READ; + return SendFtpCommand(command, COMMAND_LIST); +} + +int FtpNetworkTransaction::ProcessResponseLIST(int response_code) { + switch (GetErrorClass(response_code)) { + case ERROR_CLASS_INITIATED: + next_state_ = STATE_CTRL_WRITE_QUIT; + response_.is_directory_listing = true; + break; + case ERROR_CLASS_OK: + next_state_ = STATE_CTRL_WRITE_QUIT; + response_.is_directory_listing = true; + break; + case ERROR_CLASS_PENDING: + return Stop(ERR_FAILED); + case ERROR_CLASS_ERROR_RETRY: + return Stop(ERR_FAILED); + case ERROR_CLASS_ERROR: + return Stop(ERR_FAILED); + default: + return Stop(ERR_FAILED); + } + return OK; +} + +// Quit command +int FtpNetworkTransaction::DoCtrlWriteQUIT() { + std::string command = "QUIT"; + next_state_ = STATE_CTRL_READ; + return SendFtpCommand(command, COMMAND_QUIT); +} + +int FtpNetworkTransaction::ProcessResponseQUIT(int response_code) { + ctrl_socket_->Disconnect(); + return last_error_; +} + +// Data Connection int FtpNetworkTransaction::DoDataConnect() { - return ERR_FAILED; // TODO(darin): implement me + next_state_ = STATE_DATA_CONNECT_COMPLETE; + AddressList adr; + // TODO(ibrar): Call resolver_.Resolve in asynchronous mode, with a non-null + // callback. + int err = resolver_.Resolve(data_connection_ip_, + data_connection_port_, &adr, NULL); + if (err != OK) + return err; + data_socket_.reset(socket_factory_->CreateTCPClientSocket(adr)); + return data_socket_->Connect(&io_callback_); } int FtpNetworkTransaction::DoDataConnectComplete(int result) { - return ERR_FAILED; // TODO(darin): implement me + next_state_ = STATE_CTRL_WRITE_SIZE; + return result; } int FtpNetworkTransaction::DoDataRead() { - return ERR_FAILED; // TODO(darin): implement me + DCHECK(read_data_buf_); + DCHECK(read_data_buf_len_ > 0); + + next_state_ = STATE_DATA_READ_COMPLETE; + read_data_buf_->data()[0] = 0; + return data_socket_->Read(read_data_buf_, read_data_buf_len_, + &io_callback_); } int FtpNetworkTransaction::DoDataReadComplete(int result) { - return ERR_FAILED; // TODO(darin): implement me + DLOG(INFO) << read_data_buf_->data(); // The read_data_buf_ is NULL + // terminated string. + return result; } } // namespace net diff --git a/net/ftp/ftp_network_transaction.h b/net/ftp/ftp_network_transaction.h index c7f59cd..67ddaeb 100644 --- a/net/ftp/ftp_network_transaction.h +++ b/net/ftp/ftp_network_transaction.h @@ -5,8 +5,12 @@ #ifndef NET_FTP_FTP_NETWORK_TRANSACTION_H_ #define NET_FTP_FTP_NETWORK_TRANSACTION_H_ +#include <string> + #include "base/ref_counted.h" #include "base/scoped_ptr.h" +#include "net/base/address_list.h" +#include "net/base/host_resolver.h" #include "net/ftp/ftp_response_info.h" #include "net/ftp/ftp_transaction.h" @@ -18,25 +22,65 @@ class FtpNetworkSession; class FtpNetworkTransaction : public FtpTransaction { public: - FtpNetworkTransaction( - FtpNetworkSession* session, ClientSocketFactory* socket_factory); - ~FtpNetworkTransaction(); + FtpNetworkTransaction(FtpNetworkSession* session, + ClientSocketFactory* socket_factory); + virtual ~FtpNetworkTransaction(); // FtpTransaction methods: - virtual void Destroy(); - virtual int Start( - const FtpRequestInfo* request_info, CompletionCallback* callback); - virtual int RestartWithAuth( - const std::wstring& username, const std::wstring& password, - CompletionCallback* callback); - virtual int Read(char* buf, int buf_len, CompletionCallback* callback); + virtual int Start(const FtpRequestInfo* request_info, + CompletionCallback* callback); + virtual int Stop(int error); + virtual int RestartWithAuth(const std::wstring& username, + const std::wstring& password, + CompletionCallback* callback); + virtual int RestartIgnoringLastError(CompletionCallback* callback); + virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback); virtual const FtpResponseInfo* GetResponseInfo() const; virtual LoadState GetLoadState() const; virtual uint64 GetUploadProgress() const; private: + enum Command { + COMMAND_NONE, + COMMAND_USER, + COMMAND_PASS, + COMMAND_ACCT, + COMMAND_SYST, + COMMAND_TYPE, + COMMAND_PASV, + COMMAND_PWD, + COMMAND_SIZE, + COMMAND_RETR, + COMMAND_CWD, + COMMAND_LIST, + COMMAND_QUIT + }; + + enum ErrorClass { + ERROR_CLASS_INITIATED = 1, // The requested action was initiated. + ERROR_CLASS_OK, // The requested action successfully completed. + ERROR_CLASS_PENDING, // The command accepted, but the + // request on hold. + ERROR_CLASS_ERROR_RETRY, // The command was not accepted and the + // requested action did not take place, + // but the error condition is temporary and the + // action may be requested again. + ERROR_CLASS_ERROR, // The command was not accepted and + // the requested action did not take place. + }; + void DoCallback(int result); void OnIOComplete(int result); + int GetRespnseCode(); + int ProcessResponse(int response_code); + int ParsePasvResponse(); + + int SendFtpCommand(const std::string& command, Command cmd); + + // TODO(ibrar): Use C++ static_cast. + ErrorClass GetErrorClass(int response_code) { + return (ErrorClass)(response_code / 100); + } // Runs the state transition loop. int DoLoop(int result); @@ -51,15 +95,40 @@ class FtpNetworkTransaction : public FtpTransaction { int DoCtrlResolveHostComplete(int result); int DoCtrlConnect(); int DoCtrlConnectComplete(int result); - int DoCtrlWrite(); - int DoCtrlWriteComplete(int result); int DoCtrlRead(); int DoCtrlReadComplete(int result); + int DoCtrlWriteUSER(); + int ProcessResponseUSER(int response_code); + int DoCtrlWritePASS(); + int ProcessResponsePASS(int response_code); + int DoCtrlWriteACCT(); + int ProcessResponseACCT(int response_code); + int DoCtrlWriteSYST(); + int ProcessResponseSYST(int response_code); + int DoCtrlWritePWD(); + int ProcessResponsePWD(int response_code); + int DoCtrlWriteTYPE(); + int ProcessResponseTYPE(int response_code); + int DoCtrlWritePASV(); + int ProcessResponsePASV(int response_code); + int DoCtrlWriteRETR(); + int ProcessResponseRETR(int response_code); + int DoCtrlWriteSIZE(); + int ProcessResponseSIZE(int response_code); + int DoCtrlWriteCWD(); + int ProcessResponseCWD(int response_code); + int DoCtrlWriteLIST(); + int ProcessResponseLIST(int response_code); + int DoCtrlWriteQUIT(); + int ProcessResponseQUIT(int response_code); + int DoDataConnect(); int DoDataConnectComplete(int result); int DoDataRead(); int DoDataReadComplete(int result); + Command command_sent_; + CompletionCallbackImpl<FtpNetworkTransaction> io_callback_; CompletionCallback* user_callback_; @@ -68,7 +137,29 @@ class FtpNetworkTransaction : public FtpTransaction { const FtpRequestInfo* request_; FtpResponseInfo response_; + HostResolver resolver_; + AddressList addresses_; + + // User buffer and length passed to the Read method. + scoped_refptr<IOBuffer> read_ctrl_buf_; + int read_ctrl_buf_size_; + + scoped_refptr<IOBuffer> response_message_buf_; + int response_message_buf_len_; + + scoped_refptr<IOBuffer> read_data_buf_; + int read_data_buf_len_; + int file_data_len_; + + scoped_refptr<IOBuffer> write_buf_; + + int last_error_; + + std::string data_connection_ip_; + int data_connection_port_; + ClientSocketFactory* socket_factory_; + scoped_ptr<ClientSocket> ctrl_socket_; scoped_ptr<ClientSocket> data_socket_; @@ -80,10 +171,20 @@ class FtpNetworkTransaction : public FtpTransaction { STATE_CTRL_RESOLVE_HOST_COMPLETE, STATE_CTRL_CONNECT, STATE_CTRL_CONNECT_COMPLETE, - STATE_CTRL_WRITE, - STATE_CTRL_WRITE_COMPLETE, STATE_CTRL_READ, STATE_CTRL_READ_COMPLETE, + STATE_CTRL_WRITE_USER, + STATE_CTRL_WRITE_PASS, + STATE_CTRL_WRITE_ACCT, + STATE_CTRL_WRITE_SYST, + STATE_CTRL_WRITE_TYPE, + STATE_CTRL_WRITE_PASV, + STATE_CTRL_WRITE_PWD, + STATE_CTRL_WRITE_RETR, + STATE_CTRL_WRITE_SIZE, + STATE_CTRL_WRITE_CWD, + STATE_CTRL_WRITE_LIST, + STATE_CTRL_WRITE_QUIT, // Data connection states: STATE_DATA_CONNECT, STATE_DATA_CONNECT_COMPLETE, diff --git a/net/ftp/ftp_request_info.h b/net/ftp/ftp_request_info.h index 235a264..a2e90c6 100644 --- a/net/ftp/ftp_request_info.h +++ b/net/ftp/ftp_request_info.h @@ -5,16 +5,16 @@ #ifndef NET_FTP_FTP_REQUEST_INFO_H_ #define NET_FTP_FTP_REQUEST_INFO_H_ +#include "googleurl/src/gurl.h" + +namespace net { + class FtpRequestInfo { public: // The requested URL. GURL url; - - // Any upload data. - scoped_refptr<UploadData> upload_data; - - // Any load flags (see load_flags.h). - int load_flags; }; +} // namespace net + #endif // NET_FTP_FTP_REQUEST_INFO_H_ diff --git a/net/ftp/ftp_response_info.h b/net/ftp/ftp_response_info.h index 398f050..b3c3361 100644 --- a/net/ftp/ftp_response_info.h +++ b/net/ftp/ftp_response_info.h @@ -11,17 +11,22 @@ namespace net { class FtpResponseInfo { public: + FtpResponseInfo() : is_directory_listing(false) { + } + // Non-null when authentication is required. scoped_refptr<AuthChallengeInfo> auth_challenge; - // The length of the response. -1 means unspecified. - int64 content_length; + // The time at which the request was made that resulted in this response. + // For cached responses, this time could be "far" in the past. + base::Time request_time; + + // The time at which the response headers were received. For cached + // responses, this time could be "far" in the past. + base::Time response_time; // True if the response data is of a directory listing. bool is_directory_listing; - - FtpResponseInfo() : content_length(-1), is_directory_listing(false) { - } }; } // namespace net diff --git a/net/ftp/ftp_transaction.h b/net/ftp/ftp_transaction.h index 90fbf25..a0798ea 100644 --- a/net/ftp/ftp_transaction.h +++ b/net/ftp/ftp_transaction.h @@ -6,6 +6,7 @@ #define NET_FTP_FTP_TRANSACTION_H_ #include "net/base/completion_callback.h" +#include "net/base/io_buffer.h" #include "net/base/load_states.h" namespace net { @@ -17,7 +18,7 @@ class FtpResponseInfo; class FtpTransaction { public: // Stops any pending IO and destroys the transaction object. - virtual void Destroy() = 0; + virtual ~FtpTransaction() {} // Starts the FTP transaction (i.e., sends the FTP request). // @@ -54,7 +55,9 @@ class FtpTransaction { // // NOTE: The transaction is not responsible for deleting the callback object. // - virtual int Read(char* buf, int buf_len, CompletionCallback* callback) = 0; + virtual int Read(IOBuffer* buf, + int buf_len, + CompletionCallback* callback) = 0; // Returns the response info for this transaction or NULL if the response // info is not available. @@ -64,7 +67,7 @@ class FtpTransaction { virtual LoadState GetLoadState() const = 0; // Returns the upload progress in bytes. If there is no upload data, - // zero will be returned. This does not include the request headers. + // zero will be returned. virtual uint64 GetUploadProgress() const = 0; }; diff --git a/net/ftp/ftp_transaction_factory.h b/net/ftp/ftp_transaction_factory.h index f4cc53a..7e39026 100644 --- a/net/ftp/ftp_transaction_factory.h +++ b/net/ftp/ftp_transaction_factory.h @@ -7,7 +7,6 @@ namespace net { -class FtpAuthCache; class FtpTransaction; // An interface to a class that can create FtpTransaction objects. @@ -18,9 +17,6 @@ class FtpTransactionFactory { // Creates a FtpTransaction object. virtual FtpTransaction* CreateTransaction() = 0; - // Returns the associated FTP auth cache if any (may be NULL). - virtual FtpAuthCache* GetAuthCache() = 0; - // Suspends the creation of new transactions. If |suspend| is false, creation // of new transactions is resumed. virtual void Suspend(bool suspend) = 0; |