| Index: net/ftp/ftp_network_transaction.cc
|
| diff --git a/net/ftp/ftp_network_transaction.cc b/net/ftp/ftp_network_transaction.cc
|
| deleted file mode 100644
|
| index ef6adb8bf759124f1bb14462ded90c72dd1de596..0000000000000000000000000000000000000000
|
| --- a/net/ftp/ftp_network_transaction.cc
|
| +++ /dev/null
|
| @@ -1,1384 +0,0 @@
|
| -// Copyright (c) 2012 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_network_transaction.h"
|
| -
|
| -#include "base/bind.h"
|
| -#include "base/bind_helpers.h"
|
| -#include "base/compiler_specific.h"
|
| -#include "base/metrics/histogram.h"
|
| -#include "base/profiler/scoped_tracker.h"
|
| -#include "base/strings/string_number_conversions.h"
|
| -#include "base/strings/string_util.h"
|
| -#include "base/strings/string_split.h"
|
| -#include "base/strings/utf_string_conversions.h"
|
| -#include "base/values.h"
|
| -#include "net/base/address_list.h"
|
| -#include "net/base/connection_type_histograms.h"
|
| -#include "net/base/escape.h"
|
| -#include "net/base/net_errors.h"
|
| -#include "net/base/net_log.h"
|
| -#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_factory.h"
|
| -#include "net/socket/stream_socket.h"
|
| -
|
| -const char kCRLF[] = "\r\n";
|
| -
|
| -const int kCtrlBufLen = 1024;
|
| -
|
| -namespace {
|
| -
|
| -// Returns true if |input| can be safely used as a part of FTP command.
|
| -bool IsValidFTPCommandString(const std::string& input) {
|
| - // RFC 959 only allows ASCII strings, but at least Firefox can send non-ASCII
|
| - // characters in the command if the request path contains them. To be
|
| - // compatible, we do the same and allow non-ASCII characters in a command.
|
| -
|
| - // Protect agains newline injection attack.
|
| - if (input.find_first_of("\r\n") != std::string::npos)
|
| - return false;
|
| -
|
| - return true;
|
| -}
|
| -
|
| -enum ErrorClass {
|
| - // The requested action was initiated. The client should expect another
|
| - // reply before issuing the next command.
|
| - ERROR_CLASS_INITIATED,
|
| -
|
| - // The requested action has been successfully completed.
|
| - ERROR_CLASS_OK,
|
| -
|
| - // The command has been accepted, but to complete the operation, more
|
| - // information must be sent by the client.
|
| - ERROR_CLASS_INFO_NEEDED,
|
| -
|
| - // The command was not accepted and the requested action did not take place.
|
| - // This condition is temporary, and the client is encouraged to restart the
|
| - // command sequence.
|
| - ERROR_CLASS_TRANSIENT_ERROR,
|
| -
|
| - // The command was not accepted and the requested action did not take place.
|
| - // This condition is rather permanent, and the client is discouraged from
|
| - // repeating the exact request.
|
| - ERROR_CLASS_PERMANENT_ERROR,
|
| -};
|
| -
|
| -// Returns the error class for given response code. Caller should ensure
|
| -// that |response_code| is in range 100-599.
|
| -ErrorClass GetErrorClass(int response_code) {
|
| - if (response_code >= 100 && response_code <= 199)
|
| - return ERROR_CLASS_INITIATED;
|
| -
|
| - if (response_code >= 200 && response_code <= 299)
|
| - return ERROR_CLASS_OK;
|
| -
|
| - if (response_code >= 300 && response_code <= 399)
|
| - return ERROR_CLASS_INFO_NEEDED;
|
| -
|
| - if (response_code >= 400 && response_code <= 499)
|
| - return ERROR_CLASS_TRANSIENT_ERROR;
|
| -
|
| - if (response_code >= 500 && response_code <= 599)
|
| - return ERROR_CLASS_PERMANENT_ERROR;
|
| -
|
| - // We should not be called on invalid error codes.
|
| - NOTREACHED() << response_code;
|
| - return ERROR_CLASS_PERMANENT_ERROR;
|
| -}
|
| -
|
| -// Returns network error code for received FTP |response_code|.
|
| -int GetNetErrorCodeForFtpResponseCode(int response_code) {
|
| - switch (response_code) {
|
| - case 421:
|
| - return net::ERR_FTP_SERVICE_UNAVAILABLE;
|
| - case 426:
|
| - return net::ERR_FTP_TRANSFER_ABORTED;
|
| - case 450:
|
| - return net::ERR_FTP_FILE_BUSY;
|
| - case 500:
|
| - case 501:
|
| - return net::ERR_FTP_SYNTAX_ERROR;
|
| - case 502:
|
| - case 504:
|
| - return net::ERR_FTP_COMMAND_NOT_SUPPORTED;
|
| - case 503:
|
| - return net::ERR_FTP_BAD_COMMAND_SEQUENCE;
|
| - default:
|
| - return net::ERR_FTP_FAILED;
|
| - }
|
| -}
|
| -
|
| -// From RFC 2428 Section 3:
|
| -// The text returned in response to the EPSV command MUST be:
|
| -// <some text> (<d><d><d><tcp-port><d>)
|
| -// <d> is a delimiter character, ideally to be |
|
| -bool ExtractPortFromEPSVResponse(const net::FtpCtrlResponse& response,
|
| - int* port) {
|
| - if (response.lines.size() != 1)
|
| - return false;
|
| - const char* ptr = response.lines[0].c_str();
|
| - while (*ptr && *ptr != '(')
|
| - ++ptr;
|
| - if (!*ptr)
|
| - return false;
|
| - char sep = *(++ptr);
|
| - if (!sep || isdigit(sep) || *(++ptr) != sep || *(++ptr) != sep)
|
| - return false;
|
| - if (!isdigit(*(++ptr)))
|
| - return false;
|
| - *port = *ptr - '0';
|
| - while (isdigit(*(++ptr))) {
|
| - *port *= 10;
|
| - *port += *ptr - '0';
|
| - }
|
| - if (*ptr != sep)
|
| - return false;
|
| -
|
| - return true;
|
| -}
|
| -
|
| -// There are two way we can receive IP address and port.
|
| -// (127,0,0,1,23,21) IP address and port encapsulated in ().
|
| -// 127,0,0,1,23,21 IP address and port without ().
|
| -//
|
| -// See RFC 959, Section 4.1.2
|
| -bool ExtractPortFromPASVResponse(const net::FtpCtrlResponse& response,
|
| - int* port) {
|
| - if (response.lines.size() != 1)
|
| - return false;
|
| -
|
| - std::string line(response.lines[0]);
|
| - if (!base::IsStringASCII(line))
|
| - return false;
|
| - if (line.length() < 2)
|
| - return false;
|
| -
|
| - size_t paren_pos = line.find('(');
|
| - if (paren_pos == std::string::npos) {
|
| - // Find the first comma and use it to locate the beginning
|
| - // of the response data.
|
| - size_t comma_pos = line.find(',');
|
| - if (comma_pos == std::string::npos)
|
| - return false;
|
| -
|
| - size_t space_pos = line.rfind(' ', comma_pos);
|
| - if (space_pos != std::string::npos)
|
| - line = line.substr(space_pos + 1);
|
| - } else {
|
| - // Remove the parentheses and use the text inside them.
|
| - size_t closing_paren_pos = line.rfind(')');
|
| - if (closing_paren_pos == std::string::npos)
|
| - return false;
|
| - if (closing_paren_pos <= paren_pos)
|
| - return false;
|
| -
|
| - line = line.substr(paren_pos + 1, closing_paren_pos - paren_pos - 1);
|
| - }
|
| -
|
| - // Split the line into comma-separated pieces and extract
|
| - // the last two.
|
| - std::vector<std::string> pieces;
|
| - base::SplitString(line, ',', &pieces);
|
| - if (pieces.size() != 6)
|
| - return false;
|
| -
|
| - // Ignore the IP address supplied in the response. We are always going
|
| - // to connect back to the same server to prevent FTP PASV port scanning.
|
| - int p0, p1;
|
| - if (!base::StringToInt(pieces[4], &p0))
|
| - return false;
|
| - if (!base::StringToInt(pieces[5], &p1))
|
| - return false;
|
| - *port = (p0 << 8) + p1;
|
| -
|
| - return true;
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -namespace net {
|
| -
|
| -FtpNetworkTransaction::FtpNetworkTransaction(
|
| - FtpNetworkSession* session,
|
| - ClientSocketFactory* socket_factory)
|
| - : command_sent_(COMMAND_NONE),
|
| - io_callback_(base::Bind(&FtpNetworkTransaction::OnIOComplete,
|
| - base::Unretained(this))),
|
| - session_(session),
|
| - request_(NULL),
|
| - resolver_(session->host_resolver()),
|
| - read_ctrl_buf_(new IOBuffer(kCtrlBufLen)),
|
| - read_data_buf_len_(0),
|
| - last_error_(OK),
|
| - system_type_(SYSTEM_TYPE_UNKNOWN),
|
| - // Use image (binary) transfer by default. It should always work,
|
| - // whereas the ascii transfer may damage binary data.
|
| - data_type_(DATA_TYPE_IMAGE),
|
| - resource_type_(RESOURCE_TYPE_UNKNOWN),
|
| - use_epsv_(true),
|
| - data_connection_port_(0),
|
| - socket_factory_(socket_factory),
|
| - next_state_(STATE_NONE),
|
| - state_after_data_connect_complete_(STATE_NONE) {
|
| -}
|
| -
|
| -FtpNetworkTransaction::~FtpNetworkTransaction() {
|
| -}
|
| -
|
| -int FtpNetworkTransaction::Stop(int error) {
|
| - if (command_sent_ == COMMAND_QUIT)
|
| - return error;
|
| -
|
| - next_state_ = STATE_CTRL_WRITE_QUIT;
|
| - last_error_ = error;
|
| - return OK;
|
| -}
|
| -
|
| -int FtpNetworkTransaction::Start(const FtpRequestInfo* request_info,
|
| - const CompletionCallback& callback,
|
| - const BoundNetLog& net_log) {
|
| - net_log_ = net_log;
|
| - request_ = request_info;
|
| -
|
| - ctrl_response_buffer_.reset(new FtpCtrlResponseBuffer(net_log_));
|
| -
|
| - if (request_->url.has_username()) {
|
| - base::string16 username;
|
| - base::string16 password;
|
| - GetIdentityFromURL(request_->url, &username, &password);
|
| - credentials_.Set(username, password);
|
| - } else {
|
| - credentials_.Set(base::ASCIIToUTF16("anonymous"),
|
| - base::ASCIIToUTF16("chrome@example.com"));
|
| - }
|
| -
|
| - DetectTypecode();
|
| -
|
| - next_state_ = STATE_CTRL_RESOLVE_HOST;
|
| - int rv = DoLoop(OK);
|
| - if (rv == ERR_IO_PENDING)
|
| - user_callback_ = callback;
|
| - return rv;
|
| -}
|
| -
|
| -int FtpNetworkTransaction::RestartWithAuth(const AuthCredentials& credentials,
|
| - const CompletionCallback& callback) {
|
| - ResetStateForRestart();
|
| -
|
| - credentials_ = credentials;
|
| -
|
| - next_state_ = STATE_CTRL_RESOLVE_HOST;
|
| - int rv = DoLoop(OK);
|
| - if (rv == ERR_IO_PENDING)
|
| - user_callback_ = callback;
|
| - return rv;
|
| -}
|
| -
|
| -int FtpNetworkTransaction::Read(IOBuffer* buf,
|
| - int buf_len,
|
| - const CompletionCallback& callback) {
|
| - DCHECK(buf);
|
| - DCHECK_GT(buf_len, 0);
|
| -
|
| - 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;
|
| - return rv;
|
| -}
|
| -
|
| -const FtpResponseInfo* FtpNetworkTransaction::GetResponseInfo() const {
|
| - return &response_;
|
| -}
|
| -
|
| -LoadState FtpNetworkTransaction::GetLoadState() const {
|
| - if (next_state_ == STATE_CTRL_RESOLVE_HOST_COMPLETE)
|
| - return LOAD_STATE_RESOLVING_HOST;
|
| -
|
| - if (next_state_ == STATE_CTRL_CONNECT_COMPLETE ||
|
| - next_state_ == STATE_DATA_CONNECT_COMPLETE)
|
| - return LOAD_STATE_CONNECTING;
|
| -
|
| - if (next_state_ == STATE_DATA_READ_COMPLETE)
|
| - return LOAD_STATE_READING_RESPONSE;
|
| -
|
| - if (command_sent_ == COMMAND_RETR && read_data_buf_.get())
|
| - return LOAD_STATE_READING_RESPONSE;
|
| -
|
| - if (command_sent_ == COMMAND_QUIT)
|
| - return LOAD_STATE_IDLE;
|
| -
|
| - if (command_sent_ != COMMAND_NONE)
|
| - return LOAD_STATE_SENDING_REQUEST;
|
| -
|
| - return LOAD_STATE_IDLE;
|
| -}
|
| -
|
| -uint64 FtpNetworkTransaction::GetUploadProgress() const {
|
| - return 0;
|
| -}
|
| -
|
| -void FtpNetworkTransaction::ResetStateForRestart() {
|
| - command_sent_ = COMMAND_NONE;
|
| - user_callback_.Reset();
|
| - response_ = FtpResponseInfo();
|
| - read_ctrl_buf_ = new IOBuffer(kCtrlBufLen);
|
| - ctrl_response_buffer_.reset(new FtpCtrlResponseBuffer(net_log_));
|
| - read_data_buf_ = NULL;
|
| - read_data_buf_len_ = 0;
|
| - if (write_buf_.get())
|
| - write_buf_->SetOffset(0);
|
| - last_error_ = OK;
|
| - data_connection_port_ = 0;
|
| - ctrl_socket_.reset();
|
| - data_socket_.reset();
|
| - next_state_ = STATE_NONE;
|
| - state_after_data_connect_complete_ = STATE_NONE;
|
| -}
|
| -
|
| -void FtpNetworkTransaction::EstablishDataConnection(State state_after_connect) {
|
| - DCHECK(state_after_connect == STATE_CTRL_WRITE_RETR ||
|
| - state_after_connect == STATE_CTRL_WRITE_LIST);
|
| - state_after_data_connect_complete_ = state_after_connect;
|
| - next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV;
|
| -}
|
| -
|
| -void FtpNetworkTransaction::DoCallback(int rv) {
|
| - DCHECK(rv != ERR_IO_PENDING);
|
| - DCHECK(!user_callback_.is_null());
|
| -
|
| - // Since Run may result in Read being called, clear callback_ up front.
|
| - CompletionCallback c = user_callback_;
|
| - user_callback_.Reset();
|
| - c.Run(rv);
|
| -}
|
| -
|
| -void FtpNetworkTransaction::OnIOComplete(int result) {
|
| - // TODO(vadimt): Remove ScopedTracker below once crbug.com/436634 is fixed.
|
| - tracked_objects::ScopedTracker tracking_profile(
|
| - FROM_HERE_WITH_EXPLICIT_FUNCTION(
|
| - "436634 FtpNetworkTransaction::OnIOComplete"));
|
| -
|
| - int rv = DoLoop(result);
|
| - if (rv != ERR_IO_PENDING)
|
| - DoCallback(rv);
|
| -}
|
| -
|
| -int FtpNetworkTransaction::ProcessCtrlResponse() {
|
| - FtpCtrlResponse response = ctrl_response_buffer_->PopResponse();
|
| -
|
| - int rv = OK;
|
| - switch (command_sent_) {
|
| - case COMMAND_NONE:
|
| - // TODO(phajdan.jr): Check for errors in the welcome message.
|
| - next_state_ = STATE_CTRL_WRITE_USER;
|
| - break;
|
| - case COMMAND_USER:
|
| - rv = ProcessResponseUSER(response);
|
| - break;
|
| - case COMMAND_PASS:
|
| - rv = ProcessResponsePASS(response);
|
| - break;
|
| - case COMMAND_SYST:
|
| - rv = ProcessResponseSYST(response);
|
| - break;
|
| - case COMMAND_PWD:
|
| - rv = ProcessResponsePWD(response);
|
| - break;
|
| - case COMMAND_TYPE:
|
| - rv = ProcessResponseTYPE(response);
|
| - break;
|
| - case COMMAND_EPSV:
|
| - rv = ProcessResponseEPSV(response);
|
| - break;
|
| - case COMMAND_PASV:
|
| - rv = ProcessResponsePASV(response);
|
| - break;
|
| - case COMMAND_SIZE:
|
| - rv = ProcessResponseSIZE(response);
|
| - break;
|
| - case COMMAND_RETR:
|
| - rv = ProcessResponseRETR(response);
|
| - break;
|
| - case COMMAND_CWD:
|
| - rv = ProcessResponseCWD(response);
|
| - break;
|
| - case COMMAND_LIST:
|
| - rv = ProcessResponseLIST(response);
|
| - break;
|
| - case COMMAND_QUIT:
|
| - rv = ProcessResponseQUIT(response);
|
| - break;
|
| - default:
|
| - LOG(DFATAL) << "Unexpected value of command_sent_: " << command_sent_;
|
| - return ERR_UNEXPECTED;
|
| - }
|
| -
|
| - // We may get multiple responses for some commands,
|
| - // see http://crbug.com/18036.
|
| - while (ctrl_response_buffer_->ResponseAvailable() && rv == OK) {
|
| - response = ctrl_response_buffer_->PopResponse();
|
| -
|
| - switch (command_sent_) {
|
| - case COMMAND_RETR:
|
| - rv = ProcessResponseRETR(response);
|
| - break;
|
| - case COMMAND_LIST:
|
| - rv = ProcessResponseLIST(response);
|
| - break;
|
| - default:
|
| - // Multiple responses for other commands are invalid.
|
| - return Stop(ERR_INVALID_RESPONSE);
|
| - }
|
| - }
|
| -
|
| - return rv;
|
| -}
|
| -
|
| -// Used to prepare and send FTP command.
|
| -int FtpNetworkTransaction::SendFtpCommand(const std::string& command,
|
| - const std::string& command_for_log,
|
| - Command cmd) {
|
| - // If we send a new command when we still have unprocessed responses
|
| - // for previous commands, the response receiving code will have no way to know
|
| - // which responses are for which command.
|
| - DCHECK(!ctrl_response_buffer_->ResponseAvailable());
|
| -
|
| - DCHECK(!write_command_buf_.get());
|
| - DCHECK(!write_buf_.get());
|
| -
|
| - if (!IsValidFTPCommandString(command)) {
|
| - // Callers should validate the command themselves and return a more specific
|
| - // error code.
|
| - NOTREACHED();
|
| - return Stop(ERR_UNEXPECTED);
|
| - }
|
| -
|
| - command_sent_ = cmd;
|
| -
|
| - write_command_buf_ = new IOBufferWithSize(command.length() + 2);
|
| - write_buf_ = new DrainableIOBuffer(write_command_buf_.get(),
|
| - write_command_buf_->size());
|
| - memcpy(write_command_buf_->data(), command.data(), command.length());
|
| - memcpy(write_command_buf_->data() + command.length(), kCRLF, 2);
|
| -
|
| - net_log_.AddEvent(NetLog::TYPE_FTP_COMMAND_SENT,
|
| - NetLog::StringCallback("command", &command_for_log));
|
| -
|
| - next_state_ = STATE_CTRL_WRITE;
|
| - return OK;
|
| -}
|
| -
|
| -std::string FtpNetworkTransaction::GetRequestPathForFtpCommand(
|
| - bool is_directory) const {
|
| - std::string path(current_remote_directory_);
|
| - if (request_->url.has_path()) {
|
| - std::string gurl_path(request_->url.path());
|
| -
|
| - // Get rid of the typecode, see RFC 1738 section 3.2.2. FTP url-path.
|
| - std::string::size_type pos = gurl_path.rfind(';');
|
| - if (pos != std::string::npos)
|
| - gurl_path.resize(pos);
|
| -
|
| - path.append(gurl_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 = net::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;
|
| -}
|
| -
|
| -void FtpNetworkTransaction::DetectTypecode() {
|
| - if (!request_->url.has_path())
|
| - return;
|
| - std::string gurl_path(request_->url.path());
|
| -
|
| - // Extract the typecode, see RFC 1738 section 3.2.2. FTP url-path.
|
| - std::string::size_type pos = gurl_path.rfind(';');
|
| - if (pos == std::string::npos)
|
| - return;
|
| - std::string typecode_string(gurl_path.substr(pos));
|
| - if (typecode_string == ";type=a") {
|
| - data_type_ = DATA_TYPE_ASCII;
|
| - resource_type_ = RESOURCE_TYPE_FILE;
|
| - } else if (typecode_string == ";type=i") {
|
| - data_type_ = DATA_TYPE_IMAGE;
|
| - resource_type_ = RESOURCE_TYPE_FILE;
|
| - } else if (typecode_string == ";type=d") {
|
| - resource_type_ = RESOURCE_TYPE_DIRECTORY;
|
| - }
|
| -}
|
| -
|
| -int FtpNetworkTransaction::DoLoop(int result) {
|
| - DCHECK(next_state_ != STATE_NONE);
|
| -
|
| - int rv = result;
|
| - do {
|
| - State state = next_state_;
|
| - next_state_ = STATE_NONE;
|
| - switch (state) {
|
| - case STATE_CTRL_RESOLVE_HOST:
|
| - DCHECK(rv == OK);
|
| - rv = DoCtrlResolveHost();
|
| - break;
|
| - case STATE_CTRL_RESOLVE_HOST_COMPLETE:
|
| - rv = DoCtrlResolveHostComplete(rv);
|
| - break;
|
| - case STATE_CTRL_CONNECT:
|
| - DCHECK(rv == OK);
|
| - rv = DoCtrlConnect();
|
| - break;
|
| - case STATE_CTRL_CONNECT_COMPLETE:
|
| - rv = DoCtrlConnectComplete(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:
|
| - DCHECK(rv == OK);
|
| - rv = DoCtrlWrite();
|
| - break;
|
| - case STATE_CTRL_WRITE_COMPLETE:
|
| - rv = DoCtrlWriteComplete(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_PWD:
|
| - DCHECK(rv == OK);
|
| - rv = DoCtrlWritePWD();
|
| - break;
|
| - case STATE_CTRL_WRITE_TYPE:
|
| - DCHECK(rv == OK);
|
| - rv = DoCtrlWriteTYPE();
|
| - break;
|
| - case STATE_CTRL_WRITE_EPSV:
|
| - DCHECK(rv == OK);
|
| - rv = DoCtrlWriteEPSV();
|
| - 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:
|
| - DCHECK(rv == OK);
|
| - rv = DoCtrlWriteCWD();
|
| - break;
|
| - case STATE_CTRL_WRITE_LIST:
|
| - DCHECK(rv == OK);
|
| - rv = DoCtrlWriteLIST();
|
| - break;
|
| - case STATE_CTRL_WRITE_QUIT:
|
| - DCHECK(rv == OK);
|
| - rv = DoCtrlWriteQUIT();
|
| - break;
|
| - case STATE_DATA_CONNECT:
|
| - DCHECK(rv == OK);
|
| - rv = DoDataConnect();
|
| - break;
|
| - case STATE_DATA_CONNECT_COMPLETE:
|
| - rv = DoDataConnectComplete(rv);
|
| - break;
|
| - case STATE_DATA_READ:
|
| - DCHECK(rv == OK);
|
| - rv = DoDataRead();
|
| - break;
|
| - case STATE_DATA_READ_COMPLETE:
|
| - rv = DoDataReadComplete(rv);
|
| - break;
|
| - default:
|
| - NOTREACHED() << "bad state";
|
| - rv = ERR_UNEXPECTED;
|
| - break;
|
| - }
|
| - } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
|
| - return rv;
|
| -}
|
| -
|
| -int FtpNetworkTransaction::DoCtrlResolveHost() {
|
| - next_state_ = STATE_CTRL_RESOLVE_HOST_COMPLETE;
|
| -
|
| - HostResolver::RequestInfo info(HostPortPair::FromURL(request_->url));
|
| - // No known referrer.
|
| - return resolver_.Resolve(
|
| - info,
|
| - DEFAULT_PRIORITY,
|
| - &addresses_,
|
| - base::Bind(&FtpNetworkTransaction::OnIOComplete, base::Unretained(this)),
|
| - net_log_);
|
| -}
|
| -
|
| -int FtpNetworkTransaction::DoCtrlResolveHostComplete(int result) {
|
| - if (result == OK)
|
| - next_state_ = STATE_CTRL_CONNECT;
|
| - return result;
|
| -}
|
| -
|
| -int FtpNetworkTransaction::DoCtrlConnect() {
|
| - next_state_ = STATE_CTRL_CONNECT_COMPLETE;
|
| - ctrl_socket_ = socket_factory_->CreateTransportClientSocket(
|
| - addresses_, net_log_.net_log(), net_log_.source());
|
| - net_log_.AddEvent(
|
| - NetLog::TYPE_FTP_CONTROL_CONNECTION,
|
| - ctrl_socket_->NetLog().source().ToEventParametersCallback());
|
| - return ctrl_socket_->Connect(io_callback_);
|
| -}
|
| -
|
| -int FtpNetworkTransaction::DoCtrlConnectComplete(int result) {
|
| - if (result == OK) {
|
| - // Put the peer's IP address and port into the response.
|
| - IPEndPoint ip_endpoint;
|
| - result = ctrl_socket_->GetPeerAddress(&ip_endpoint);
|
| - if (result == OK) {
|
| - response_.socket_address = HostPortPair::FromIPEndPoint(ip_endpoint);
|
| - next_state_ = STATE_CTRL_READ;
|
| -
|
| - if (ip_endpoint.GetFamily() == ADDRESS_FAMILY_IPV4) {
|
| - // Do not use EPSV for IPv4 connections. Some servers become confused
|
| - // and we time out while waiting to connect. PASV is perfectly fine for
|
| - // IPv4. Note that this blacklists IPv4 not to use EPSV instead of
|
| - // whitelisting IPv6 to use it, to make the code more future-proof:
|
| - // all future protocols should just use EPSV.
|
| - use_epsv_ = false;
|
| - }
|
| - }
|
| - }
|
| - return result;
|
| -}
|
| -
|
| -int FtpNetworkTransaction::DoCtrlRead() {
|
| - next_state_ = STATE_CTRL_READ_COMPLETE;
|
| - return ctrl_socket_->Read(read_ctrl_buf_.get(), kCtrlBufLen, io_callback_);
|
| -}
|
| -
|
| -int FtpNetworkTransaction::DoCtrlReadComplete(int result) {
|
| - if (result == 0) {
|
| - // Some servers (for example Pure-FTPd) apparently close the control
|
| - // connection when anonymous login is not permitted. For more details
|
| - // see http://crbug.com/25023.
|
| - if (command_sent_ == COMMAND_USER &&
|
| - credentials_.username() == base::ASCIIToUTF16("anonymous")) {
|
| - response_.needs_auth = true;
|
| - }
|
| - return Stop(ERR_EMPTY_RESPONSE);
|
| - }
|
| - if (result < 0)
|
| - return Stop(result);
|
| -
|
| - ctrl_response_buffer_->ConsumeData(read_ctrl_buf_->data(), result);
|
| -
|
| - if (!ctrl_response_buffer_->ResponseAvailable()) {
|
| - // Read more data from the control socket.
|
| - next_state_ = STATE_CTRL_READ;
|
| - return OK;
|
| - }
|
| -
|
| - return ProcessCtrlResponse();
|
| -}
|
| -
|
| -int FtpNetworkTransaction::DoCtrlWrite() {
|
| - next_state_ = STATE_CTRL_WRITE_COMPLETE;
|
| -
|
| - return ctrl_socket_->Write(
|
| - write_buf_.get(), write_buf_->BytesRemaining(), io_callback_);
|
| -}
|
| -
|
| -int FtpNetworkTransaction::DoCtrlWriteComplete(int result) {
|
| - if (result < 0)
|
| - return result;
|
| -
|
| - write_buf_->DidConsume(result);
|
| - if (write_buf_->BytesRemaining() == 0) {
|
| - // Clear the write buffer.
|
| - write_buf_ = NULL;
|
| - write_command_buf_ = NULL;
|
| -
|
| - next_state_ = STATE_CTRL_READ;
|
| - } else {
|
| - next_state_ = STATE_CTRL_WRITE;
|
| - }
|
| - return OK;
|
| -}
|
| -
|
| -// FTP Commands and responses
|
| -
|
| -// USER Command.
|
| -int FtpNetworkTransaction::DoCtrlWriteUSER() {
|
| - std::string command = "USER " + base::UTF16ToUTF8(credentials_.username());
|
| -
|
| - if (!IsValidFTPCommandString(command))
|
| - return Stop(ERR_MALFORMED_IDENTITY);
|
| -
|
| - next_state_ = STATE_CTRL_READ;
|
| - return SendFtpCommand(command, "USER ***", COMMAND_USER);
|
| -}
|
| -
|
| -int FtpNetworkTransaction::ProcessResponseUSER(
|
| - const FtpCtrlResponse& response) {
|
| - switch (GetErrorClass(response.status_code)) {
|
| - case ERROR_CLASS_OK:
|
| - next_state_ = STATE_CTRL_WRITE_SYST;
|
| - break;
|
| - case ERROR_CLASS_INFO_NEEDED:
|
| - next_state_ = STATE_CTRL_WRITE_PASS;
|
| - break;
|
| - case ERROR_CLASS_TRANSIENT_ERROR:
|
| - case ERROR_CLASS_PERMANENT_ERROR:
|
| - response_.needs_auth = true;
|
| - return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
|
| - default:
|
| - NOTREACHED();
|
| - return Stop(ERR_UNEXPECTED);
|
| - }
|
| - return OK;
|
| -}
|
| -
|
| -// PASS command.
|
| -int FtpNetworkTransaction::DoCtrlWritePASS() {
|
| - std::string command = "PASS " + base::UTF16ToUTF8(credentials_.password());
|
| -
|
| - if (!IsValidFTPCommandString(command))
|
| - return Stop(ERR_MALFORMED_IDENTITY);
|
| -
|
| - next_state_ = STATE_CTRL_READ;
|
| - return SendFtpCommand(command, "PASS ***", COMMAND_PASS);
|
| -}
|
| -
|
| -int FtpNetworkTransaction::ProcessResponsePASS(
|
| - const FtpCtrlResponse& response) {
|
| - switch (GetErrorClass(response.status_code)) {
|
| - case ERROR_CLASS_OK:
|
| - next_state_ = STATE_CTRL_WRITE_SYST;
|
| - break;
|
| - case ERROR_CLASS_INFO_NEEDED:
|
| - return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
|
| - case ERROR_CLASS_TRANSIENT_ERROR:
|
| - case ERROR_CLASS_PERMANENT_ERROR:
|
| - response_.needs_auth = true;
|
| - return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
|
| - default:
|
| - NOTREACHED();
|
| - return Stop(ERR_UNEXPECTED);
|
| - }
|
| - return OK;
|
| -}
|
| -
|
| -// SYST command.
|
| -int FtpNetworkTransaction::DoCtrlWriteSYST() {
|
| - std::string command = "SYST";
|
| - next_state_ = STATE_CTRL_READ;
|
| - return SendFtpCommand(command, command, COMMAND_SYST);
|
| -}
|
| -
|
| -int FtpNetworkTransaction::ProcessResponseSYST(
|
| - const FtpCtrlResponse& response) {
|
| - switch (GetErrorClass(response.status_code)) {
|
| - case ERROR_CLASS_INITIATED:
|
| - return Stop(ERR_INVALID_RESPONSE);
|
| - 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 (base::IsStringASCII(line)) {
|
| - line = base::StringToLowerASCII(line);
|
| -
|
| - // Remove all whitespace, to correctly handle cases like fancy "V M S"
|
| - // response instead of "VMS".
|
| - base::RemoveChars(line, base::kWhitespaceASCII, &line);
|
| -
|
| - // The "magic" strings we test for below have been gathered by an
|
| - // empirical study. VMS needs to come first because some VMS systems
|
| - // also respond with "UNIX emulation", which is not perfect. It is much
|
| - // more reliable to talk to these servers in their native language.
|
| - if (line.find("vms") != std::string::npos) {
|
| - system_type_ = SYSTEM_TYPE_VMS;
|
| - } else 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;
|
| - }
|
| - }
|
| - next_state_ = STATE_CTRL_WRITE_PWD;
|
| - break;
|
| - }
|
| - case ERROR_CLASS_INFO_NEEDED:
|
| - return Stop(ERR_INVALID_RESPONSE);
|
| - case ERROR_CLASS_TRANSIENT_ERROR:
|
| - return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
|
| - case ERROR_CLASS_PERMANENT_ERROR:
|
| - // Server does not recognize the SYST command so proceed.
|
| - next_state_ = STATE_CTRL_WRITE_PWD;
|
| - break;
|
| - default:
|
| - NOTREACHED();
|
| - return Stop(ERR_UNEXPECTED);
|
| - }
|
| - return OK;
|
| -}
|
| -
|
| -// PWD command.
|
| -int FtpNetworkTransaction::DoCtrlWritePWD() {
|
| - std::string command = "PWD";
|
| - next_state_ = STATE_CTRL_READ;
|
| - return SendFtpCommand(command, command, COMMAND_PWD);
|
| -}
|
| -
|
| -int FtpNetworkTransaction::ProcessResponsePWD(const FtpCtrlResponse& response) {
|
| - switch (GetErrorClass(response.status_code)) {
|
| - case ERROR_CLASS_INITIATED:
|
| - return Stop(ERR_INVALID_RESPONSE);
|
| - 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.length() && 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:
|
| - return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
|
| - case ERROR_CLASS_PERMANENT_ERROR:
|
| - return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
|
| - default:
|
| - NOTREACHED();
|
| - return Stop(ERR_UNEXPECTED);
|
| - }
|
| - return OK;
|
| -}
|
| -
|
| -// TYPE command.
|
| -int FtpNetworkTransaction::DoCtrlWriteTYPE() {
|
| - std::string command = "TYPE ";
|
| - if (data_type_ == DATA_TYPE_ASCII) {
|
| - command += "A";
|
| - } else if (data_type_ == DATA_TYPE_IMAGE) {
|
| - command += "I";
|
| - } else {
|
| - NOTREACHED();
|
| - return Stop(ERR_UNEXPECTED);
|
| - }
|
| - next_state_ = STATE_CTRL_READ;
|
| - return SendFtpCommand(command, command, COMMAND_TYPE);
|
| -}
|
| -
|
| -int FtpNetworkTransaction::ProcessResponseTYPE(
|
| - const FtpCtrlResponse& response) {
|
| - switch (GetErrorClass(response.status_code)) {
|
| - case ERROR_CLASS_INITIATED:
|
| - return Stop(ERR_INVALID_RESPONSE);
|
| - case ERROR_CLASS_OK:
|
| - next_state_ = STATE_CTRL_WRITE_SIZE;
|
| - break;
|
| - case ERROR_CLASS_INFO_NEEDED:
|
| - return Stop(ERR_INVALID_RESPONSE);
|
| - case ERROR_CLASS_TRANSIENT_ERROR:
|
| - return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
|
| - case ERROR_CLASS_PERMANENT_ERROR:
|
| - return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
|
| - default:
|
| - NOTREACHED();
|
| - return Stop(ERR_UNEXPECTED);
|
| - }
|
| - return OK;
|
| -}
|
| -
|
| -// EPSV command
|
| -int FtpNetworkTransaction::DoCtrlWriteEPSV() {
|
| - const std::string command = "EPSV";
|
| - next_state_ = STATE_CTRL_READ;
|
| - return SendFtpCommand(command, command, COMMAND_EPSV);
|
| -}
|
| -
|
| -int FtpNetworkTransaction::ProcessResponseEPSV(
|
| - const FtpCtrlResponse& response) {
|
| - switch (GetErrorClass(response.status_code)) {
|
| - case ERROR_CLASS_INITIATED:
|
| - return Stop(ERR_INVALID_RESPONSE);
|
| - case ERROR_CLASS_OK: {
|
| - int port;
|
| - if (!ExtractPortFromEPSVResponse(response, &port))
|
| - return Stop(ERR_INVALID_RESPONSE);
|
| - if (port < 1024 || !IsPortAllowedByFtp(port))
|
| - return Stop(ERR_UNSAFE_PORT);
|
| - data_connection_port_ = static_cast<uint16>(port);
|
| - next_state_ = STATE_DATA_CONNECT;
|
| - break;
|
| - }
|
| - case ERROR_CLASS_INFO_NEEDED:
|
| - return Stop(ERR_INVALID_RESPONSE);
|
| - case ERROR_CLASS_TRANSIENT_ERROR:
|
| - case ERROR_CLASS_PERMANENT_ERROR:
|
| - use_epsv_ = false;
|
| - next_state_ = STATE_CTRL_WRITE_PASV;
|
| - return OK;
|
| - default:
|
| - NOTREACHED();
|
| - return Stop(ERR_UNEXPECTED);
|
| - }
|
| - return OK;
|
| -}
|
| -
|
| -// PASV command
|
| -int FtpNetworkTransaction::DoCtrlWritePASV() {
|
| - std::string command = "PASV";
|
| - next_state_ = STATE_CTRL_READ;
|
| - return SendFtpCommand(command, command, COMMAND_PASV);
|
| -}
|
| -
|
| -int FtpNetworkTransaction::ProcessResponsePASV(
|
| - const FtpCtrlResponse& response) {
|
| - switch (GetErrorClass(response.status_code)) {
|
| - case ERROR_CLASS_INITIATED:
|
| - return Stop(ERR_INVALID_RESPONSE);
|
| - case ERROR_CLASS_OK: {
|
| - int port;
|
| - if (!ExtractPortFromPASVResponse(response, &port))
|
| - return Stop(ERR_INVALID_RESPONSE);
|
| - if (port < 1024 || !IsPortAllowedByFtp(port))
|
| - return Stop(ERR_UNSAFE_PORT);
|
| - data_connection_port_ = static_cast<uint16>(port);
|
| - next_state_ = STATE_DATA_CONNECT;
|
| - break;
|
| - }
|
| - case ERROR_CLASS_INFO_NEEDED:
|
| - return Stop(ERR_INVALID_RESPONSE);
|
| - case ERROR_CLASS_TRANSIENT_ERROR:
|
| - return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
|
| - case ERROR_CLASS_PERMANENT_ERROR:
|
| - return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
|
| - default:
|
| - NOTREACHED();
|
| - return Stop(ERR_UNEXPECTED);
|
| - }
|
| - return OK;
|
| -}
|
| -
|
| -// RETR command
|
| -int FtpNetworkTransaction::DoCtrlWriteRETR() {
|
| - std::string command = "RETR " + GetRequestPathForFtpCommand(false);
|
| - next_state_ = STATE_CTRL_READ;
|
| - return SendFtpCommand(command, command, COMMAND_RETR);
|
| -}
|
| -
|
| -int FtpNetworkTransaction::ProcessResponseRETR(
|
| - const FtpCtrlResponse& response) {
|
| - // Resource type should be either filled in by DetectTypecode() or
|
| - // detected with CWD. RETR is sent only when the resource is a file.
|
| - DCHECK_EQ(RESOURCE_TYPE_FILE, resource_type_);
|
| -
|
| - switch (GetErrorClass(response.status_code)) {
|
| - case ERROR_CLASS_INITIATED:
|
| - // We want the client to start reading the response at this point.
|
| - // It got here either through Start or RestartWithAuth. We want that
|
| - // method to complete. Not setting next state here will make DoLoop exit
|
| - // and in turn make Start/RestartWithAuth complete.
|
| - break;
|
| - case ERROR_CLASS_OK:
|
| - next_state_ = STATE_CTRL_WRITE_QUIT;
|
| - break;
|
| - case ERROR_CLASS_INFO_NEEDED:
|
| - return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
|
| - case ERROR_CLASS_TRANSIENT_ERROR:
|
| - return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
|
| - case ERROR_CLASS_PERMANENT_ERROR:
|
| - return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
|
| - default:
|
| - NOTREACHED();
|
| - return Stop(ERR_UNEXPECTED);
|
| - }
|
| -
|
| - return OK;
|
| -}
|
| -
|
| -// SIZE command
|
| -int FtpNetworkTransaction::DoCtrlWriteSIZE() {
|
| - std::string command = "SIZE " + GetRequestPathForFtpCommand(false);
|
| - next_state_ = STATE_CTRL_READ;
|
| - return SendFtpCommand(command, command, COMMAND_SIZE);
|
| -}
|
| -
|
| -int FtpNetworkTransaction::ProcessResponseSIZE(
|
| - const FtpCtrlResponse& response) {
|
| - switch (GetErrorClass(response.status_code)) {
|
| - case ERROR_CLASS_INITIATED:
|
| - break;
|
| - case ERROR_CLASS_OK:
|
| - if (response.lines.size() != 1)
|
| - return Stop(ERR_INVALID_RESPONSE);
|
| - int64 size;
|
| - if (!base::StringToInt64(response.lines[0], &size))
|
| - return Stop(ERR_INVALID_RESPONSE);
|
| - if (size < 0)
|
| - return Stop(ERR_INVALID_RESPONSE);
|
| -
|
| - // A successful response to SIZE does not mean the resource is a file.
|
| - // Some FTP servers (for example, the qnx one) send a SIZE even for
|
| - // directories.
|
| - response_.expected_content_size = size;
|
| - break;
|
| - case ERROR_CLASS_INFO_NEEDED:
|
| - break;
|
| - case ERROR_CLASS_TRANSIENT_ERROR:
|
| - break;
|
| - case ERROR_CLASS_PERMANENT_ERROR:
|
| - // It's possible that SIZE failed because the path is a directory.
|
| - // TODO(xunjieli): Add a test for this case.
|
| - if (resource_type_ == RESOURCE_TYPE_UNKNOWN &&
|
| - response.status_code != 550) {
|
| - return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
|
| - }
|
| - break;
|
| - default:
|
| - NOTREACHED();
|
| - return Stop(ERR_UNEXPECTED);
|
| - }
|
| -
|
| - // If the resource is known beforehand to be a file, RETR should be issued,
|
| - // otherwise do CWD which will detect the resource type.
|
| - if (resource_type_ == RESOURCE_TYPE_FILE)
|
| - EstablishDataConnection(STATE_CTRL_WRITE_RETR);
|
| - else
|
| - next_state_ = STATE_CTRL_WRITE_CWD;
|
| - return OK;
|
| -}
|
| -
|
| -// CWD command
|
| -int FtpNetworkTransaction::DoCtrlWriteCWD() {
|
| - std::string command = "CWD " + GetRequestPathForFtpCommand(true);
|
| - next_state_ = STATE_CTRL_READ;
|
| - return SendFtpCommand(command, command, COMMAND_CWD);
|
| -}
|
| -
|
| -int FtpNetworkTransaction::ProcessResponseCWD(const FtpCtrlResponse& response) {
|
| - // CWD should be invoked only when the resource is not a file.
|
| - DCHECK_NE(RESOURCE_TYPE_FILE, resource_type_);
|
| -
|
| - switch (GetErrorClass(response.status_code)) {
|
| - case ERROR_CLASS_INITIATED:
|
| - return Stop(ERR_INVALID_RESPONSE);
|
| - case ERROR_CLASS_OK:
|
| - resource_type_ = RESOURCE_TYPE_DIRECTORY;
|
| - EstablishDataConnection(STATE_CTRL_WRITE_LIST);
|
| - break;
|
| - case ERROR_CLASS_INFO_NEEDED:
|
| - return Stop(ERR_INVALID_RESPONSE);
|
| - case ERROR_CLASS_TRANSIENT_ERROR:
|
| - // Some FTP servers send response 451 (not a valid CWD response according
|
| - // to RFC 959) instead of 550.
|
| - if (response.status_code == 451)
|
| - return ProcessResponseCWDNotADirectory();
|
| -
|
| - return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
|
| - case ERROR_CLASS_PERMANENT_ERROR:
|
| - if (response.status_code == 550)
|
| - return ProcessResponseCWDNotADirectory();
|
| -
|
| - return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
|
| - default:
|
| - NOTREACHED();
|
| - return Stop(ERR_UNEXPECTED);
|
| - }
|
| -
|
| - return OK;
|
| -}
|
| -
|
| -int FtpNetworkTransaction::ProcessResponseCWDNotADirectory() {
|
| - if (resource_type_ == RESOURCE_TYPE_DIRECTORY) {
|
| - // We're assuming that the resource is a directory, but the server
|
| - // says it's not true. The most probable interpretation is that it
|
| - // doesn't exist (with FTP we can't be sure).
|
| - return Stop(ERR_FILE_NOT_FOUND);
|
| - }
|
| -
|
| - // If it is not a directory, it is probably a file.
|
| - resource_type_ = RESOURCE_TYPE_FILE;
|
| -
|
| - EstablishDataConnection(STATE_CTRL_WRITE_RETR);
|
| - return OK;
|
| -}
|
| -
|
| -// LIST command
|
| -int FtpNetworkTransaction::DoCtrlWriteLIST() {
|
| - // Use the -l option for mod_ftp configured in LISTIsNLST mode: the option
|
| - // forces LIST output instead of NLST (which would be ambiguous for us
|
| - // to parse).
|
| - std::string command("LIST -l");
|
| - if (system_type_ == SYSTEM_TYPE_VMS)
|
| - command = "LIST *.*;0";
|
| -
|
| - next_state_ = STATE_CTRL_READ;
|
| - return SendFtpCommand(command, command, COMMAND_LIST);
|
| -}
|
| -
|
| -int FtpNetworkTransaction::ProcessResponseLIST(
|
| - const FtpCtrlResponse& response) {
|
| - // Resource type should be either filled in by DetectTypecode() or
|
| - // detected with CWD. LIST is sent only when the resource is a directory.
|
| - DCHECK_EQ(RESOURCE_TYPE_DIRECTORY, resource_type_);
|
| -
|
| - switch (GetErrorClass(response.status_code)) {
|
| - case ERROR_CLASS_INITIATED:
|
| - // We want the client to start reading the response at this point.
|
| - // It got here either through Start or RestartWithAuth. We want that
|
| - // method to complete. Not setting next state here will make DoLoop exit
|
| - // and in turn make Start/RestartWithAuth complete.
|
| - response_.is_directory_listing = true;
|
| - break;
|
| - case ERROR_CLASS_OK:
|
| - response_.is_directory_listing = true;
|
| - next_state_ = STATE_CTRL_WRITE_QUIT;
|
| - break;
|
| - case ERROR_CLASS_INFO_NEEDED:
|
| - return Stop(ERR_INVALID_RESPONSE);
|
| - case ERROR_CLASS_TRANSIENT_ERROR:
|
| - return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
|
| - case ERROR_CLASS_PERMANENT_ERROR:
|
| - return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
|
| - default:
|
| - NOTREACHED();
|
| - return Stop(ERR_UNEXPECTED);
|
| - }
|
| - return OK;
|
| -}
|
| -
|
| -// QUIT command
|
| -int FtpNetworkTransaction::DoCtrlWriteQUIT() {
|
| - std::string command = "QUIT";
|
| - next_state_ = STATE_CTRL_READ;
|
| - return SendFtpCommand(command, command, COMMAND_QUIT);
|
| -}
|
| -
|
| -int FtpNetworkTransaction::ProcessResponseQUIT(
|
| - const FtpCtrlResponse& response) {
|
| - ctrl_socket_->Disconnect();
|
| - return last_error_;
|
| -}
|
| -
|
| -// Data Connection
|
| -
|
| -int FtpNetworkTransaction::DoDataConnect() {
|
| - next_state_ = STATE_DATA_CONNECT_COMPLETE;
|
| - IPEndPoint ip_endpoint;
|
| - AddressList data_address;
|
| - // Connect to the same host as the control socket to prevent PASV port
|
| - // scanning attacks.
|
| - int rv = ctrl_socket_->GetPeerAddress(&ip_endpoint);
|
| - if (rv != OK)
|
| - return Stop(rv);
|
| - data_address = AddressList::CreateFromIPAddress(
|
| - ip_endpoint.address(), data_connection_port_);
|
| - data_socket_ = socket_factory_->CreateTransportClientSocket(
|
| - data_address, net_log_.net_log(), net_log_.source());
|
| - net_log_.AddEvent(
|
| - NetLog::TYPE_FTP_DATA_CONNECTION,
|
| - data_socket_->NetLog().source().ToEventParametersCallback());
|
| - return data_socket_->Connect(io_callback_);
|
| -}
|
| -
|
| -int FtpNetworkTransaction::DoDataConnectComplete(int result) {
|
| - if (result != OK && use_epsv_) {
|
| - // It's possible we hit a broken server, sadly. They can break in different
|
| - // ways. Some time out, some reset a connection. Fall back to PASV.
|
| - // TODO(phajdan.jr): remember it for future transactions with this server.
|
| - // TODO(phajdan.jr): write a test for this code path.
|
| - use_epsv_ = false;
|
| - next_state_ = STATE_CTRL_WRITE_PASV;
|
| - return OK;
|
| - }
|
| -
|
| - // Only record the connection error after we've applied all our fallbacks.
|
| - // We want to capture the final error, one we're not going to recover from.
|
| - RecordDataConnectionError(result);
|
| -
|
| - if (result != OK)
|
| - return Stop(result);
|
| -
|
| - next_state_ = state_after_data_connect_complete_;
|
| - return OK;
|
| -}
|
| -
|
| -int FtpNetworkTransaction::DoDataRead() {
|
| - DCHECK(read_data_buf_.get());
|
| - DCHECK_GT(read_data_buf_len_, 0);
|
| -
|
| - if (data_socket_ == NULL || !data_socket_->IsConnected()) {
|
| - // If we don't destroy the data socket completely, some servers will wait
|
| - // for us (http://crbug.com/21127). The half-closed TCP connection needs
|
| - // to be closed on our side too.
|
| - data_socket_.reset();
|
| -
|
| - if (ctrl_socket_->IsConnected()) {
|
| - // Wait for the server's response, we should get it before sending QUIT.
|
| - next_state_ = STATE_CTRL_READ;
|
| - return OK;
|
| - }
|
| -
|
| - // We are no longer connected to the server, so just finish the transaction.
|
| - return Stop(OK);
|
| - }
|
| -
|
| - next_state_ = STATE_DATA_READ_COMPLETE;
|
| - read_data_buf_->data()[0] = 0;
|
| - return data_socket_->Read(
|
| - read_data_buf_.get(), read_data_buf_len_, io_callback_);
|
| -}
|
| -
|
| -int FtpNetworkTransaction::DoDataReadComplete(int result) {
|
| - return result;
|
| -}
|
| -
|
| -// We're using a histogram as a group of counters, with one bucket for each
|
| -// enumeration value. We're only interested in the values of the counters.
|
| -// Ignore the shape, average, and standard deviation of the histograms because
|
| -// they are meaningless.
|
| -//
|
| -// We use two histograms. In the first histogram we tally whether the user has
|
| -// seen an error of that type during the session. In the second histogram we
|
| -// tally the total number of times the users sees each errer.
|
| -void FtpNetworkTransaction::RecordDataConnectionError(int result) {
|
| - // Gather data for http://crbug.com/3073. See how many users have trouble
|
| - // establishing FTP data connection in passive FTP mode.
|
| - enum {
|
| - // Data connection successful.
|
| - NET_ERROR_OK = 0,
|
| -
|
| - // Local firewall blocked the connection.
|
| - NET_ERROR_ACCESS_DENIED = 1,
|
| -
|
| - // Connection timed out.
|
| - NET_ERROR_TIMED_OUT = 2,
|
| -
|
| - // Connection has been estabilished, but then got broken (either reset
|
| - // or aborted).
|
| - NET_ERROR_CONNECTION_BROKEN = 3,
|
| -
|
| - // Connection has been refused.
|
| - NET_ERROR_CONNECTION_REFUSED = 4,
|
| -
|
| - // No connection to the internet.
|
| - NET_ERROR_INTERNET_DISCONNECTED = 5,
|
| -
|
| - // Could not reach the destination address.
|
| - NET_ERROR_ADDRESS_UNREACHABLE = 6,
|
| -
|
| - // A programming error in our network stack.
|
| - NET_ERROR_UNEXPECTED = 7,
|
| -
|
| - // Other kind of error.
|
| - NET_ERROR_OTHER = 20,
|
| -
|
| - NUM_OF_NET_ERROR_TYPES
|
| - } type;
|
| - switch (result) {
|
| - case OK:
|
| - type = NET_ERROR_OK;
|
| - break;
|
| - case ERR_ACCESS_DENIED:
|
| - case ERR_NETWORK_ACCESS_DENIED:
|
| - type = NET_ERROR_ACCESS_DENIED;
|
| - break;
|
| - case ERR_TIMED_OUT:
|
| - type = NET_ERROR_TIMED_OUT;
|
| - break;
|
| - case ERR_CONNECTION_ABORTED:
|
| - case ERR_CONNECTION_RESET:
|
| - case ERR_CONNECTION_CLOSED:
|
| - type = NET_ERROR_CONNECTION_BROKEN;
|
| - break;
|
| - case ERR_CONNECTION_FAILED:
|
| - case ERR_CONNECTION_REFUSED:
|
| - type = NET_ERROR_CONNECTION_REFUSED;
|
| - break;
|
| - case ERR_INTERNET_DISCONNECTED:
|
| - type = NET_ERROR_INTERNET_DISCONNECTED;
|
| - break;
|
| - case ERR_ADDRESS_INVALID:
|
| - case ERR_ADDRESS_UNREACHABLE:
|
| - type = NET_ERROR_ADDRESS_UNREACHABLE;
|
| - break;
|
| - case ERR_UNEXPECTED:
|
| - type = NET_ERROR_UNEXPECTED;
|
| - break;
|
| - default:
|
| - type = NET_ERROR_OTHER;
|
| - break;
|
| - };
|
| - static bool had_error_type[NUM_OF_NET_ERROR_TYPES];
|
| -
|
| - DCHECK(type >= 0 && type < NUM_OF_NET_ERROR_TYPES);
|
| - if (!had_error_type[type]) {
|
| - had_error_type[type] = true;
|
| - UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorHappened",
|
| - type, NUM_OF_NET_ERROR_TYPES);
|
| - }
|
| - UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorCount",
|
| - type, NUM_OF_NET_ERROR_TYPES);
|
| -}
|
| -
|
| -} // namespace net
|
|
|