ftp_network_transaction.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
18d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// Copyright (c) 2012 The Chromium Authors. All rights reserved.
28d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// Use of this source code is governed by a BSD-style license that can be
38d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// found in the LICENSE file.
48d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
58d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "net/ftp/ftp_network_transaction.h"
6c5ec7f57ead87efa365800228aa0b09a12d9e6c4Dmitry Shmidt
7c5ec7f57ead87efa365800228aa0b09a12d9e6c4Dmitry Shmidt#include "base/bind.h"
88d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "base/bind_helpers.h"
98d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "base/compiler_specific.h"
108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "base/metrics/histogram.h"
118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "base/strings/string_number_conversions.h"
128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "base/strings/string_util.h"
138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "base/strings/string_split.h"
148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "base/strings/utf_string_conversions.h"
158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "base/values.h"
168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "net/base/address_list.h"
17b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidt#include "net/base/connection_type_histograms.h"
18cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt#include "net/base/escape.h"
198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "net/base/net_errors.h"
208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "net/base/net_log.h"
218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "net/base/net_util.h"
221f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt#include "net/ftp/ftp_network_session.h"
238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "net/ftp/ftp_request_info.h"
248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "net/ftp/ftp_util.h"
258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "net/socket/client_socket_factory.h"
268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "net/socket/stream_socket.h"
278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtconst char kCRLF[] = "\r\n";
298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtconst int kCtrlBufLen = 1024;
318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtnamespace {
338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// Returns true if |input| can be safely used as a part of FTP command.
358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtbool IsValidFTPCommandString(const std::string& input) {
368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  // RFC 959 only allows ASCII strings, but at least Firefox can send non-ASCII
378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  // characters in the command if the request path contains them. To be
388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  // compatible, we do the same and allow non-ASCII characters in a command.
398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  // Protect agains newline injection attack.
418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  if (input.find_first_of("\r\n") != std::string::npos)
428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    return false;
438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  return true;
458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
4668d0e3ed07847339aedfac8e02f50db68c702e52Dmitry Shmidt
4768d0e3ed07847339aedfac8e02f50db68c702e52Dmitry Shmidtenum ErrorClass {
4868d0e3ed07847339aedfac8e02f50db68c702e52Dmitry Shmidt  // The requested action was initiated. The client should expect another
4968d0e3ed07847339aedfac8e02f50db68c702e52Dmitry Shmidt  // reply before issuing the next command.
5068d0e3ed07847339aedfac8e02f50db68c702e52Dmitry Shmidt  ERROR_CLASS_INITIATED,
5168d0e3ed07847339aedfac8e02f50db68c702e52Dmitry Shmidt
5268d0e3ed07847339aedfac8e02f50db68c702e52Dmitry Shmidt  // The requested action has been successfully completed.
5368d0e3ed07847339aedfac8e02f50db68c702e52Dmitry Shmidt  ERROR_CLASS_OK,
5468d0e3ed07847339aedfac8e02f50db68c702e52Dmitry Shmidt
5568d0e3ed07847339aedfac8e02f50db68c702e52Dmitry Shmidt  // The command has been accepted, but to complete the operation, more
5668d0e3ed07847339aedfac8e02f50db68c702e52Dmitry Shmidt  // information must be sent by the client.
5768d0e3ed07847339aedfac8e02f50db68c702e52Dmitry Shmidt  ERROR_CLASS_INFO_NEEDED,
5868d0e3ed07847339aedfac8e02f50db68c702e52Dmitry Shmidt
5968d0e3ed07847339aedfac8e02f50db68c702e52Dmitry Shmidt  // The command was not accepted and the requested action did not take place.
6068d0e3ed07847339aedfac8e02f50db68c702e52Dmitry Shmidt  // This condition is temporary, and the client is encouraged to restart the
6168d0e3ed07847339aedfac8e02f50db68c702e52Dmitry Shmidt  // command sequence.
6268d0e3ed07847339aedfac8e02f50db68c702e52Dmitry Shmidt  ERROR_CLASS_TRANSIENT_ERROR,
6368d0e3ed07847339aedfac8e02f50db68c702e52Dmitry Shmidt
6468d0e3ed07847339aedfac8e02f50db68c702e52Dmitry Shmidt  // The command was not accepted and the requested action did not take place.
6568d0e3ed07847339aedfac8e02f50db68c702e52Dmitry Shmidt  // This condition is rather permanent, and the client is discouraged from
6668d0e3ed07847339aedfac8e02f50db68c702e52Dmitry Shmidt  // repeating the exact request.
6768d0e3ed07847339aedfac8e02f50db68c702e52Dmitry Shmidt  ERROR_CLASS_PERMANENT_ERROR,
6868d0e3ed07847339aedfac8e02f50db68c702e52Dmitry Shmidt};
6968d0e3ed07847339aedfac8e02f50db68c702e52Dmitry Shmidt
7068d0e3ed07847339aedfac8e02f50db68c702e52Dmitry Shmidt// Returns the error class for given response code. Caller should ensure
7168d0e3ed07847339aedfac8e02f50db68c702e52Dmitry Shmidt// that |response_code| is in range 100-599.
7268d0e3ed07847339aedfac8e02f50db68c702e52Dmitry ShmidtErrorClass GetErrorClass(int response_code) {
738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  if (response_code >= 100 && response_code <= 199)
748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    return ERROR_CLASS_INITIATED;
758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  if (response_code >= 200 && response_code <= 299)
77cce06667447b5aec83452adb0c15100ada531095Dmitry Shmidt    return ERROR_CLASS_OK;
788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  if (response_code >= 300 && response_code <= 399)
808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    return ERROR_CLASS_INFO_NEEDED;
818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  if (response_code >= 400 && response_code <= 499)
83b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidt    return ERROR_CLASS_TRANSIENT_ERROR;
84b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidt
85b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidt  if (response_code >= 500 && response_code <= 599)
86b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidt    return ERROR_CLASS_PERMANENT_ERROR;
87b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidt
88b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidt  // We should not be called on invalid error codes.
898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  NOTREACHED() << response_code;
908d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  return ERROR_CLASS_PERMANENT_ERROR;
91c55524ad84d13014e8019491c2b17e5dcf13545aDmitry Shmidt}
928d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
938d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// Returns network error code for received FTP |response_code|.
94c55524ad84d13014e8019491c2b17e5dcf13545aDmitry Shmidtint GetNetErrorCodeForFtpResponseCode(int response_code) {
95c55524ad84d13014e8019491c2b17e5dcf13545aDmitry Shmidt  switch (response_code) {
961f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt    case 421:
971f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt      return net::ERR_FTP_SERVICE_UNAVAILABLE;
98c55524ad84d13014e8019491c2b17e5dcf13545aDmitry Shmidt    case 426:
991f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt      return net::ERR_FTP_TRANSFER_ABORTED;
100c55524ad84d13014e8019491c2b17e5dcf13545aDmitry Shmidt    case 450:
1011f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt      return net::ERR_FTP_FILE_BUSY;
102c55524ad84d13014e8019491c2b17e5dcf13545aDmitry Shmidt    case 500:
10304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt    case 501:
1041f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt      return net::ERR_FTP_SYNTAX_ERROR;
1051f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt    case 502:
1061f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt    case 504:
107c55524ad84d13014e8019491c2b17e5dcf13545aDmitry Shmidt      return net::ERR_FTP_COMMAND_NOT_SUPPORTED;
108c55524ad84d13014e8019491c2b17e5dcf13545aDmitry Shmidt    case 503:
109c55524ad84d13014e8019491c2b17e5dcf13545aDmitry Shmidt      return net::ERR_FTP_BAD_COMMAND_SEQUENCE;
11004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt    default:
11104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt      return net::ERR_FTP_FAILED;
11204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt  }
11304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
11404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
11504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt// From RFC 2428 Section 3:
11604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt//   The text returned in response to the EPSV command MUST be:
11704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt//     <some text> (<d><d><d><tcp-port><d>)
11804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt//   <d> is a delimiter character, ideally to be |
11904949598a23f501be6eec21697465fd46a28840aDmitry Shmidtbool ExtractPortFromEPSVResponse(const net::FtpCtrlResponse& response,
120c55524ad84d13014e8019491c2b17e5dcf13545aDmitry Shmidt                                 int* port) {
12161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt  if (response.lines.size() != 1)
12261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt    return false;
12361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt  const char* ptr = response.lines[0].c_str();
12461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt  while (*ptr && *ptr != '(')
12561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt    ++ptr;
12661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt  if (!*ptr)
12761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt    return false;
12861d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt  char sep = *(++ptr);
12961d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt  if (!sep || isdigit(sep) || *(++ptr) != sep || *(++ptr) != sep)
13061d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt    return false;
13161d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt  if (!isdigit(*(++ptr)))
13261d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt    return false;
1331f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt  *port = *ptr - '0';
1341f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt  while (isdigit(*(++ptr))) {
1351f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt    *port *= 10;
1361f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt    *port += *ptr - '0';
1371f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt  }
1381f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt  if (*ptr != sep)
1391f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt    return false;
1401f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt
1411f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt  return true;
1421f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt}
1431f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt
1441f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt// There are two way we can receive IP address and port.
14568d0e3ed07847339aedfac8e02f50db68c702e52Dmitry Shmidt// (127,0,0,1,23,21) IP address and port encapsulated in ().
14668d0e3ed07847339aedfac8e02f50db68c702e52Dmitry Shmidt// 127,0,0,1,23,21  IP address and port without ().
14768d0e3ed07847339aedfac8e02f50db68c702e52Dmitry Shmidt//
14868d0e3ed07847339aedfac8e02f50db68c702e52Dmitry Shmidt// See RFC 959, Section 4.1.2
14968d0e3ed07847339aedfac8e02f50db68c702e52Dmitry Shmidtbool ExtractPortFromPASVResponse(const net::FtpCtrlResponse& response,
1501f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt                                 int* port) {
151c55524ad84d13014e8019491c2b17e5dcf13545aDmitry Shmidt  if (response.lines.size() != 1)
152c55524ad84d13014e8019491c2b17e5dcf13545aDmitry Shmidt    return false;
153c55524ad84d13014e8019491c2b17e5dcf13545aDmitry Shmidt
1548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  std::string line(response.lines[0]);
15534af306c42b7ccf956508e7cd23f0ba90606e360Dmitry Shmidt  if (!IsStringASCII(line))
15634af306c42b7ccf956508e7cd23f0ba90606e360Dmitry Shmidt    return false;
15734af306c42b7ccf956508e7cd23f0ba90606e360Dmitry Shmidt  if (line.length() < 2)
1588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    return false;
1598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  size_t paren_pos = line.find('(');
1618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  if (paren_pos == std::string::npos) {
1628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    // Find the first comma and use it to locate the beginning
1638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    // of the response data.
1648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    size_t comma_pos = line.find(',');
1658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    if (comma_pos == std::string::npos)
1668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      return false;
1678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    size_t space_pos = line.rfind(' ', comma_pos);
1698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    if (space_pos != std::string::npos)
1708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      line = line.substr(space_pos + 1);
1718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  } else {
1728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    // Remove the parentheses and use the text inside them.
1738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    size_t closing_paren_pos = line.rfind(')');
1748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    if (closing_paren_pos == std::string::npos)
1758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      return false;
1768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    if (closing_paren_pos <= paren_pos)
1778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      return false;
1788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    line = line.substr(paren_pos + 1, closing_paren_pos - paren_pos - 1);
1808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  }
1818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1821f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt  // Split the line into comma-separated pieces and extract
1831f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt  // the last two.
184391c59f0632df8db1c325da1d31d479b2eedce45Dmitry Shmidt  std::vector<std::string> pieces;
185cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt  base::SplitString(line, ',', &pieces);
186cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt  if (pieces.size() != 6)
187cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt    return false;
188cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt
189cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt  // Ignore the IP address supplied in the response. We are always going
190cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt  // to connect back to the same server to prevent FTP PASV port scanning.
191cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt  int p0, p1;
192cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt  if (!base::StringToInt(pieces[4], &p0))
193cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt    return false;
194cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt  if (!base::StringToInt(pieces[5], &p1))
1958d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    return false;
1968d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  *port = (p0 << 8) + p1;
1978d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1988d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  return true;
1998d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
2008d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
2018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}  // namespace
2028d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
2038d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtnamespace net {
2048d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
20504949598a23f501be6eec21697465fd46a28840aDmitry ShmidtFtpNetworkTransaction::FtpNetworkTransaction(
20604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt    FtpNetworkSession* session,
2071f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt    ClientSocketFactory* socket_factory)
2081f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt    : command_sent_(COMMAND_NONE),
2091f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt      io_callback_(base::Bind(&FtpNetworkTransaction::OnIOComplete,
2108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt                              base::Unretained(this))),
2118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      session_(session),
2128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      request_(NULL),
2138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      resolver_(session->host_resolver()),
214d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt      read_ctrl_buf_(new IOBuffer(kCtrlBufLen)),
2158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      read_data_buf_len_(0),
2168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      last_error_(OK),
2178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      system_type_(SYSTEM_TYPE_UNKNOWN),
2188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      // Use image (binary) transfer by default. It should always work,
2198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      // whereas the ascii transfer may damage binary data.
2208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      data_type_(DATA_TYPE_IMAGE),
221d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt      resource_type_(RESOURCE_TYPE_UNKNOWN),
222d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt      use_epsv_(true),
2231f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt      data_connection_port_(0),
2241f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt      socket_factory_(socket_factory),
2251f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt      next_state_(STATE_NONE),
2261f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt      state_after_data_connect_complete_(STATE_CTRL_WRITE_SIZE) {}
2271f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt
2281f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry ShmidtFtpNetworkTransaction::~FtpNetworkTransaction() {
2291f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt}
2301f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt
2311f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidtint FtpNetworkTransaction::Stop(int error) {
2321f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt  if (command_sent_ == COMMAND_QUIT)
2331f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt    return error;
2341f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt
2351f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt  next_state_ = STATE_CTRL_WRITE_QUIT;
2361f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt  last_error_ = error;
2371f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt  return OK;
2381f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt}
2398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
2408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtint FtpNetworkTransaction::RestartIgnoringLastError(
24104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt    const CompletionCallback& callback) {
24204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt  return ERR_NOT_IMPLEMENTED;
24304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
24404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
24504949598a23f501be6eec21697465fd46a28840aDmitry Shmidtint FtpNetworkTransaction::Start(const FtpRequestInfo* request_info,
2467a5e50a0554bee77a9da492ea3d86f46147f1671Dmitry Shmidt                                 const CompletionCallback& callback,
2477a5e50a0554bee77a9da492ea3d86f46147f1671Dmitry Shmidt                                 const BoundNetLog& net_log) {
2487a5e50a0554bee77a9da492ea3d86f46147f1671Dmitry Shmidt  net_log_ = net_log;
2497a5e50a0554bee77a9da492ea3d86f46147f1671Dmitry Shmidt  request_ = request_info;
2507a5e50a0554bee77a9da492ea3d86f46147f1671Dmitry Shmidt
2517a5e50a0554bee77a9da492ea3d86f46147f1671Dmitry Shmidt  ctrl_response_buffer_.reset(new FtpCtrlResponseBuffer(net_log_));
2527a5e50a0554bee77a9da492ea3d86f46147f1671Dmitry Shmidt
25304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt  if (request_->url.has_username()) {
254a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt    base::string16 username;
255a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt    base::string16 password;
256a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt    GetIdentityFromURL(request_->url, &username, &password);
257a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt    credentials_.Set(username, password);
2588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  } else {
2598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    credentials_.Set(base::ASCIIToUTF16("anonymous"),
2608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt                     base::ASCIIToUTF16("chrome@example.com"));
2618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  }
2628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
2638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  DetectTypecode();
2641f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt
2658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  next_state_ = STATE_CTRL_RESOLVE_HOST;
2668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  int rv = DoLoop(OK);
2671f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt  if (rv == ERR_IO_PENDING)
2681f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt    user_callback_ = callback;
2691f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt  return rv;
2701f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt}
2711f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt
2721f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidtint FtpNetworkTransaction::RestartWithAuth(const AuthCredentials& credentials,
2731f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt                                           const CompletionCallback& callback) {
2741f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt  ResetStateForRestart();
2751f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt
2761f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt  credentials_ = credentials;
2778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
2781f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt  next_state_ = STATE_CTRL_RESOLVE_HOST;
2791f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt  int rv = DoLoop(OK);
2801f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt  if (rv == ERR_IO_PENDING)
2811f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt    user_callback_ = callback;
2828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  return rv;
2831f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt}
2841f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt
2851f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidtint FtpNetworkTransaction::Read(IOBuffer* buf,
2861f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt                                int buf_len,
2878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt                                const CompletionCallback& callback) {
2888da800a193fb6f8832218715f82a7b4e2d2ad338Dmitry Shmidt  DCHECK(buf);
2898da800a193fb6f8832218715f82a7b4e2d2ad338Dmitry Shmidt  DCHECK_GT(buf_len, 0);
290fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt
291fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt  read_data_buf_ = buf;
292fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt  read_data_buf_len_ = buf_len;
2938da800a193fb6f8832218715f82a7b4e2d2ad338Dmitry Shmidt
2948da800a193fb6f8832218715f82a7b4e2d2ad338Dmitry Shmidt  next_state_ = STATE_DATA_READ;
2958da800a193fb6f8832218715f82a7b4e2d2ad338Dmitry Shmidt  int rv = DoLoop(OK);
2968da800a193fb6f8832218715f82a7b4e2d2ad338Dmitry Shmidt  if (rv == ERR_IO_PENDING)
2978da800a193fb6f8832218715f82a7b4e2d2ad338Dmitry Shmidt    user_callback_ = callback;
2988da800a193fb6f8832218715f82a7b4e2d2ad338Dmitry Shmidt  return rv;
2998da800a193fb6f8832218715f82a7b4e2d2ad338Dmitry Shmidt}
3008d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
3018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtconst FtpResponseInfo* FtpNetworkTransaction::GetResponseInfo() const {
3021f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt  return &response_;
3031f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt}
3041f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt
3058d520ff1dc2da35cdca849e982051b86468016d8Dmitry ShmidtLoadState FtpNetworkTransaction::GetLoadState() const {
3061f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt  if (next_state_ == STATE_CTRL_RESOLVE_HOST_COMPLETE)
3071f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt    return LOAD_STATE_RESOLVING_HOST;
3081f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt
3091f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt  if (next_state_ == STATE_CTRL_CONNECT_COMPLETE ||
3101f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt      next_state_ == STATE_DATA_CONNECT_COMPLETE)
311a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt    return LOAD_STATE_CONNECTING;
3121f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt
3131f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt  if (next_state_ == STATE_DATA_READ_COMPLETE)
3141f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt    return LOAD_STATE_READING_RESPONSE;
3158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
31604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt  if (command_sent_ == COMMAND_RETR && read_data_buf_.get())
31704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt    return LOAD_STATE_READING_RESPONSE;
31804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
31904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt  if (command_sent_ == COMMAND_QUIT)
3208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    return LOAD_STATE_IDLE;
3218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
3228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  if (command_sent_ != COMMAND_NONE)
3238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    return LOAD_STATE_SENDING_REQUEST;
3248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
3258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  return LOAD_STATE_IDLE;
3268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
3278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
3288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtuint64 FtpNetworkTransaction::GetUploadProgress() const {
3298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  return 0;
3308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
3318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
3328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtvoid FtpNetworkTransaction::ResetStateForRestart() {
3338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  command_sent_ = COMMAND_NONE;
3348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  user_callback_.Reset();
3358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  response_ = FtpResponseInfo();
3368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  read_ctrl_buf_ = new IOBuffer(kCtrlBufLen);
3378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  ctrl_response_buffer_.reset(new FtpCtrlResponseBuffer(net_log_));
3388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  read_data_buf_ = NULL;
3398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  read_data_buf_len_ = 0;
3408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  if (write_buf_.get())
3418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    write_buf_->SetOffset(0);
34204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt  last_error_ = OK;
3431f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt  data_connection_port_ = 0;
3448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  ctrl_socket_.reset();
3458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  data_socket_.reset();
3468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  next_state_ = STATE_NONE;
3478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  state_after_data_connect_complete_ = STATE_CTRL_WRITE_SIZE;
3488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
3498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
3508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtvoid FtpNetworkTransaction::ResetDataConnectionAfterError(State next_state) {
3518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  // The server _might_ have reset the data connection
3528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  // (see RFC 959 3.2. ESTABLISHING DATA CONNECTIONS:
3538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  // "The server MUST close the data connection under the following
3540ccb66edb8d2a0a397320ace3ec2a03fb0d00d5fDmitry Shmidt  // conditions:
3550ccb66edb8d2a0a397320ace3ec2a03fb0d00d5fDmitry Shmidt  // ...
3560ccb66edb8d2a0a397320ace3ec2a03fb0d00d5fDmitry Shmidt  // 5. An irrecoverable error condition occurs.")
3570ccb66edb8d2a0a397320ace3ec2a03fb0d00d5fDmitry Shmidt  //
3580ccb66edb8d2a0a397320ace3ec2a03fb0d00d5fDmitry Shmidt  // It is ambiguous what an irrecoverable error condition is,
3598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  // so we take no chances.
3608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  state_after_data_connect_complete_ = next_state;
3618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV;
3628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
3638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
3648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtvoid FtpNetworkTransaction::DoCallback(int rv) {
3658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  DCHECK(rv != ERR_IO_PENDING);
3668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  DCHECK(!user_callback_.is_null());
3678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
3688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  // Since Run may result in Read being called, clear callback_ up front.
3698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  CompletionCallback c = user_callback_;
3708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  user_callback_.Reset();
3718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  c.Run(rv);
3728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
3738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
3741846323989242844f0e857458a8939fa5836429cDmitry Shmidtvoid FtpNetworkTransaction::OnIOComplete(int result) {
3751846323989242844f0e857458a8939fa5836429cDmitry Shmidt  int rv = DoLoop(result);
3768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  if (rv != ERR_IO_PENDING)
3778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    DoCallback(rv);
3788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
3798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
3808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtint FtpNetworkTransaction::ProcessCtrlResponse() {
3818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  FtpCtrlResponse response = ctrl_response_buffer_->PopResponse();
3828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
3838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  int rv = OK;
3848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  switch (command_sent_) {
3858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case COMMAND_NONE:
3868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      // TODO(phajdan.jr): Check for errors in the welcome message.
3878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      next_state_ = STATE_CTRL_WRITE_USER;
3888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      break;
3898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case COMMAND_USER:
39075ecf5267604f166b85a7ee2cf0d9cb682966680Jouni Malinen      rv = ProcessResponseUSER(response);
3918d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      break;
3928d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case COMMAND_PASS:
39375ecf5267604f166b85a7ee2cf0d9cb682966680Jouni Malinen      rv = ProcessResponsePASS(response);
39475ecf5267604f166b85a7ee2cf0d9cb682966680Jouni Malinen      break;
39575ecf5267604f166b85a7ee2cf0d9cb682966680Jouni Malinen    case COMMAND_SYST:
39675ecf5267604f166b85a7ee2cf0d9cb682966680Jouni Malinen      rv = ProcessResponseSYST(response);
39775ecf5267604f166b85a7ee2cf0d9cb682966680Jouni Malinen      break;
39875ecf5267604f166b85a7ee2cf0d9cb682966680Jouni Malinen    case COMMAND_PWD:
39975ecf5267604f166b85a7ee2cf0d9cb682966680Jouni Malinen      rv = ProcessResponsePWD(response);
40075ecf5267604f166b85a7ee2cf0d9cb682966680Jouni Malinen      break;
40175ecf5267604f166b85a7ee2cf0d9cb682966680Jouni Malinen    case COMMAND_TYPE:
40275ecf5267604f166b85a7ee2cf0d9cb682966680Jouni Malinen      rv = ProcessResponseTYPE(response);
40375ecf5267604f166b85a7ee2cf0d9cb682966680Jouni Malinen      break;
40475ecf5267604f166b85a7ee2cf0d9cb682966680Jouni Malinen    case COMMAND_EPSV:
4058d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      rv = ProcessResponseEPSV(response);
4068d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      break;
4078d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case COMMAND_PASV:
4088d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      rv = ProcessResponsePASV(response);
4098d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      break;
4108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case COMMAND_SIZE:
4111f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt      rv = ProcessResponseSIZE(response);
4128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      break;
4131f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt    case COMMAND_RETR:
4148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      rv = ProcessResponseRETR(response);
4158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      break;
4168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case COMMAND_CWD:
417391c59f0632df8db1c325da1d31d479b2eedce45Dmitry Shmidt      rv = ProcessResponseCWD(response);
418391c59f0632df8db1c325da1d31d479b2eedce45Dmitry Shmidt      break;
419391c59f0632df8db1c325da1d31d479b2eedce45Dmitry Shmidt    case COMMAND_LIST:
420391c59f0632df8db1c325da1d31d479b2eedce45Dmitry Shmidt      rv = ProcessResponseLIST(response);
421391c59f0632df8db1c325da1d31d479b2eedce45Dmitry Shmidt      break;
422391c59f0632df8db1c325da1d31d479b2eedce45Dmitry Shmidt    case COMMAND_QUIT:
423391c59f0632df8db1c325da1d31d479b2eedce45Dmitry Shmidt      rv = ProcessResponseQUIT(response);
424391c59f0632df8db1c325da1d31d479b2eedce45Dmitry Shmidt      break;
425391c59f0632df8db1c325da1d31d479b2eedce45Dmitry Shmidt    default:
426391c59f0632df8db1c325da1d31d479b2eedce45Dmitry Shmidt      LOG(DFATAL) << "Unexpected value of command_sent_: " << command_sent_;
427391c59f0632df8db1c325da1d31d479b2eedce45Dmitry Shmidt      return ERR_UNEXPECTED;
428391c59f0632df8db1c325da1d31d479b2eedce45Dmitry Shmidt  }
429391c59f0632df8db1c325da1d31d479b2eedce45Dmitry Shmidt
4308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  // We may get multiple responses for some commands,
4318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  // see http://crbug.com/18036.
4328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  while (ctrl_response_buffer_->ResponseAvailable() && rv == OK) {
4338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    response = ctrl_response_buffer_->PopResponse();
4348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
4358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    switch (command_sent_) {
4368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      case COMMAND_RETR:
4378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        rv = ProcessResponseRETR(response);
4388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        break;
4398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      case COMMAND_LIST:
4408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        rv = ProcessResponseLIST(response);
4418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        break;
4428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      default:
4438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        // Multiple responses for other commands are invalid.
4448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        return Stop(ERR_INVALID_RESPONSE);
4458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    }
4468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  }
4478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
4488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  return rv;
4498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
4501f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt
45104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt// Used to prepare and send FTP command.
45204949598a23f501be6eec21697465fd46a28840aDmitry Shmidtint FtpNetworkTransaction::SendFtpCommand(const std::string& command,
4538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt                                          const std::string& command_for_log,
4548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt                                          Command cmd) {
4558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  // If we send a new command when we still have unprocessed responses
45604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt  // for previous commands, the response receiving code will have no way to know
45704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt  // which responses are for which command.
4588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  DCHECK(!ctrl_response_buffer_->ResponseAvailable());
4598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
4608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  DCHECK(!write_command_buf_.get());
4618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  DCHECK(!write_buf_.get());
4628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
4638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  if (!IsValidFTPCommandString(command)) {
4648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    // Callers should validate the command themselves and return a more specific
4658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    // error code.
4668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    NOTREACHED();
4678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    return Stop(ERR_UNEXPECTED);
4688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  }
4698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
4708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  command_sent_ = cmd;
4718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
4728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  write_command_buf_ = new IOBufferWithSize(command.length() + 2);
4738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  write_buf_ = new DrainableIOBuffer(write_command_buf_.get(),
4748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt                                     write_command_buf_->size());
4758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  memcpy(write_command_buf_->data(), command.data(), command.length());
4768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  memcpy(write_command_buf_->data() + command.length(), kCRLF, 2);
4778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
4788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  net_log_.AddEvent(NetLog::TYPE_FTP_COMMAND_SENT,
4798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt                    NetLog::StringCallback("command", &command_for_log));
4808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
4818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  next_state_ = STATE_CTRL_WRITE;
4828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  return OK;
4838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
4848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
4858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstd::string FtpNetworkTransaction::GetRequestPathForFtpCommand(
4868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    bool is_directory) const {
4878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  std::string path(current_remote_directory_);
4888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  if (request_->url.has_path()) {
4898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    std::string gurl_path(request_->url.path());
4908d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
4918d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    // Get rid of the typecode, see RFC 1738 section 3.2.2. FTP url-path.
4928d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    std::string::size_type pos = gurl_path.rfind(';');
4938d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    if (pos != std::string::npos)
4948d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      gurl_path.resize(pos);
4958d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
4968d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    path.append(gurl_path);
4978d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  }
4988d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  // Make sure that if the path is expected to be a file, it won't end
4998d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  // with a trailing slash.
5008d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  if (!is_directory && path.length() > 1 && path[path.length() - 1] == '/')
5018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    path.erase(path.length() - 1);
5028d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  UnescapeRule::Type unescape_rules = UnescapeRule::SPACES |
5038d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt                                      UnescapeRule::URL_SPECIAL_CHARS;
5048d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  // This may unescape to non-ASCII characters, but we allow that. See the
5058d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  // comment for IsValidFTPCommandString.
5068d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  path = net::UnescapeURLComponent(path, unescape_rules);
5078d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
5088d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  if (system_type_ == SYSTEM_TYPE_VMS) {
5098d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    if (is_directory)
5108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      path = FtpUtil::UnixDirectoryPathToVMS(path);
5118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    else
5128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      path = FtpUtil::UnixFilePathToVMS(path);
5138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  }
5148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
5158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  DCHECK(IsValidFTPCommandString(path));
5168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  return path;
5178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
5188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
5198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtvoid FtpNetworkTransaction::DetectTypecode() {
520b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidt  if (!request_->url.has_path())
521b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidt    return;
5228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  std::string gurl_path(request_->url.path());
5238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
5241f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt  // Extract the typecode, see RFC 1738 section 3.2.2. FTP url-path.
5258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  std::string::size_type pos = gurl_path.rfind(';');
5268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  if (pos == std::string::npos)
5278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    return;
5288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  std::string typecode_string(gurl_path.substr(pos));
529fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt  if (typecode_string == ";type=a") {
5308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    data_type_ = DATA_TYPE_ASCII;
531a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt    resource_type_ = RESOURCE_TYPE_FILE;
532a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt  } else if (typecode_string == ";type=i") {
533a54fa5fb807eaeff45464139b5a7759f060cec68Dmitry Shmidt    data_type_ = DATA_TYPE_IMAGE;
5348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    resource_type_ = RESOURCE_TYPE_FILE;
5358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  } else if (typecode_string == ";type=d") {
5368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    resource_type_ = RESOURCE_TYPE_DIRECTORY;
5378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  }
538fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt}
5398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
5408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtint FtpNetworkTransaction::DoLoop(int result) {
5418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  DCHECK(next_state_ != STATE_NONE);
5428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
5438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  int rv = result;
5448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  do {
5458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    State state = next_state_;
5468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    next_state_ = STATE_NONE;
5478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    switch (state) {
5488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      case STATE_CTRL_RESOLVE_HOST:
5498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        DCHECK(rv == OK);
5508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        rv = DoCtrlResolveHost();
5518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        break;
5528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      case STATE_CTRL_RESOLVE_HOST_COMPLETE:
5538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        rv = DoCtrlResolveHostComplete(rv);
5548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        break;
5558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      case STATE_CTRL_CONNECT:
5568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        DCHECK(rv == OK);
5578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        rv = DoCtrlConnect();
5588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        break;
5598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      case STATE_CTRL_CONNECT_COMPLETE:
5608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        rv = DoCtrlConnectComplete(rv);
5611f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt        break;
5621f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt      case STATE_CTRL_READ:
563444d567b27731d8572ef37697dd12fd1c37c2f24Dmitry Shmidt        DCHECK(rv == OK);
564444d567b27731d8572ef37697dd12fd1c37c2f24Dmitry Shmidt        rv = DoCtrlRead();
565444d567b27731d8572ef37697dd12fd1c37c2f24Dmitry Shmidt        break;
5668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      case STATE_CTRL_READ_COMPLETE:
5678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        rv = DoCtrlReadComplete(rv);
5688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        break;
5698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      case STATE_CTRL_WRITE:
5708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        DCHECK(rv == OK);
5718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        rv = DoCtrlWrite();
5728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        break;
57361d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt      case STATE_CTRL_WRITE_COMPLETE:
57461d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt        rv = DoCtrlWriteComplete(rv);
57561d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt        break;
57661d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt      case STATE_CTRL_WRITE_USER:
5771f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt        DCHECK(rv == OK);
578cce06667447b5aec83452adb0c15100ada531095Dmitry Shmidt        rv = DoCtrlWriteUSER();
579cce06667447b5aec83452adb0c15100ada531095Dmitry Shmidt        break;
5801f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt      case STATE_CTRL_WRITE_PASS:
5811f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt        DCHECK(rv == OK);
5828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        rv = DoCtrlWritePASS();
5838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        break;
5848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      case STATE_CTRL_WRITE_SYST:
5858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        DCHECK(rv == OK);
5868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        rv = DoCtrlWriteSYST();
5878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        break;
5888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      case STATE_CTRL_WRITE_PWD:
5898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        DCHECK(rv == OK);
590cce06667447b5aec83452adb0c15100ada531095Dmitry Shmidt        rv = DoCtrlWritePWD();
5918d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        break;
592cce06667447b5aec83452adb0c15100ada531095Dmitry Shmidt      case STATE_CTRL_WRITE_TYPE:
5938d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        DCHECK(rv == OK);
5948d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        rv = DoCtrlWriteTYPE();
5958d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        break;
5968d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      case STATE_CTRL_WRITE_EPSV:
59761d9df3e62aaa0e87ad05452fcb95142159a17b6Dmitry Shmidt        DCHECK(rv == OK);
5988d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        rv = DoCtrlWriteEPSV();
5998d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        break;
6008d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      case STATE_CTRL_WRITE_PASV:
6018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        DCHECK(rv == OK);
6028d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        rv = DoCtrlWritePASV();
6038d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        break;
6048d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      case STATE_CTRL_WRITE_RETR:
6058d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        DCHECK(rv == OK);
6068d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        rv = DoCtrlWriteRETR();
607cce06667447b5aec83452adb0c15100ada531095Dmitry Shmidt        break;
6088d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      case STATE_CTRL_WRITE_SIZE:
6098d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        DCHECK(rv == OK);
6108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        rv = DoCtrlWriteSIZE();
6118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        break;
6128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      case STATE_CTRL_WRITE_CWD:
6138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        DCHECK(rv == OK);
614497c1d5e50162d6b3c1cce5dbd9c5fd9da69aaefDmitry Shmidt        rv = DoCtrlWriteCWD();
6158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        break;
6168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      case STATE_CTRL_WRITE_LIST:
6178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        DCHECK(rv == OK);
6188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        rv = DoCtrlWriteLIST();
6198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        break;
6208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      case STATE_CTRL_WRITE_QUIT:
6218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        DCHECK(rv == OK);
6228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        rv = DoCtrlWriteQUIT();
6238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        break;
6248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      case STATE_DATA_CONNECT:
6258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        DCHECK(rv == OK);
6268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        rv = DoDataConnect();
6278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        break;
628391c59f0632df8db1c325da1d31d479b2eedce45Dmitry Shmidt      case STATE_DATA_CONNECT_COMPLETE:
629391c59f0632df8db1c325da1d31d479b2eedce45Dmitry Shmidt        rv = DoDataConnectComplete(rv);
6308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        break;
63104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt      case STATE_DATA_READ:
63204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt        DCHECK(rv == OK);
6338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        rv = DoDataRead();
6348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        break;
6358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      case STATE_DATA_READ_COMPLETE:
6368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        rv = DoDataReadComplete(rv);
6378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        break;
6388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      default:
6398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        NOTREACHED() << "bad state";
6408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        rv = ERR_UNEXPECTED;
6418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        break;
6428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    }
643b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidt  } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
6448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  return rv;
6458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
6468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
6478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtint FtpNetworkTransaction::DoCtrlResolveHost() {
6488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  next_state_ = STATE_CTRL_RESOLVE_HOST_COMPLETE;
6498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
6508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  HostResolver::RequestInfo info(HostPortPair::FromURL(request_->url));
6518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  // No known referrer.
6528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  return resolver_.Resolve(
6538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      info,
6548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      DEFAULT_PRIORITY,
6558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      &addresses_,
6568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      base::Bind(&FtpNetworkTransaction::OnIOComplete, base::Unretained(this)),
6578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      net_log_);
6588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
6598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
6608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtint FtpNetworkTransaction::DoCtrlResolveHostComplete(int result) {
6618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  if (result == OK)
6628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    next_state_ = STATE_CTRL_CONNECT;
6638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  return result;
6648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
6658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
6668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtint FtpNetworkTransaction::DoCtrlConnect() {
667b7b4d0ec07161a6d76c40ba7ef1306e82fbb7e15Dmitry Shmidt  next_state_ = STATE_CTRL_CONNECT_COMPLETE;
6688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  ctrl_socket_ = socket_factory_->CreateTransportClientSocket(
6698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      addresses_, net_log_.net_log(), net_log_.source());
6708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  net_log_.AddEvent(
6718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      NetLog::TYPE_FTP_CONTROL_CONNECTION,
6728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      ctrl_socket_->NetLog().source().ToEventParametersCallback());
6738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  return ctrl_socket_->Connect(io_callback_);
6748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
6758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
6768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtint FtpNetworkTransaction::DoCtrlConnectComplete(int result) {
6778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  if (result == OK) {
6788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    // Put the peer's IP address and port into the response.
6798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    IPEndPoint ip_endpoint;
6808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    result = ctrl_socket_->GetPeerAddress(&ip_endpoint);
6818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    if (result == OK) {
6828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      response_.socket_address = HostPortPair::FromIPEndPoint(ip_endpoint);
6838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      next_state_ = STATE_CTRL_READ;
6848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
6858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      if (ip_endpoint.GetFamily() == ADDRESS_FAMILY_IPV4) {
6868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        // Do not use EPSV for IPv4 connections. Some servers become confused
6878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        // and we time out while waiting to connect. PASV is perfectly fine for
6888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        // IPv4. Note that this blacklists IPv4 not to use EPSV instead of
6898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        // whitelisting IPv6 to use it, to make the code more future-proof:
6908d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        // all future protocols should just use EPSV.
6911f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt        use_epsv_ = false;
6921f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt      }
6938d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    }
6948d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  }
6958d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  return result;
696fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt}
697fb79edc9df1f20461e90e478363d207348213d35Dmitry Shmidt
6981f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidtint FtpNetworkTransaction::DoCtrlRead() {
6991f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt  next_state_ = STATE_CTRL_READ_COMPLETE;
7001f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt  return ctrl_socket_->Read(read_ctrl_buf_.get(), kCtrlBufLen, io_callback_);
7011f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt}
7021f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt
7031f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidtint FtpNetworkTransaction::DoCtrlReadComplete(int result) {
7041f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt  if (result == 0) {
7051f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt    // Some servers (for example Pure-FTPd) apparently close the control
7061f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt    // connection when anonymous login is not permitted. For more details
7071f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt    // see http://crbug.com/25023.
7081f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt    if (command_sent_ == COMMAND_USER &&
7091f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt        credentials_.username() == base::ASCIIToUTF16("anonymous")) {
7101f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt      response_.needs_auth = true;
7111f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt    }
7121f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt    return Stop(ERR_EMPTY_RESPONSE);
7131f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt  }
7141f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt  if (result < 0)
7151f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt    return Stop(result);
7161f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt
7171f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt  ctrl_response_buffer_->ConsumeData(read_ctrl_buf_->data(), result);
7188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
7198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  if (!ctrl_response_buffer_->ResponseAvailable()) {
7208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    // Read more data from the control socket.
7218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    next_state_ = STATE_CTRL_READ;
7228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    return OK;
7238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  }
7241f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt
7258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  return ProcessCtrlResponse();
7268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
7278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
7288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtint FtpNetworkTransaction::DoCtrlWrite() {
7298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  next_state_ = STATE_CTRL_WRITE_COMPLETE;
7308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
7318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  return ctrl_socket_->Write(
7321f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt      write_buf_.get(), write_buf_->BytesRemaining(), io_callback_);
7338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
7348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
7358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtint FtpNetworkTransaction::DoCtrlWriteComplete(int result) {
7368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  if (result < 0)
7378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    return result;
7388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
7398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  write_buf_->DidConsume(result);
7408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  if (write_buf_->BytesRemaining() == 0) {
7418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    // Clear the write buffer.
7428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    write_buf_ = NULL;
7438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    write_command_buf_ = NULL;
7448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
7458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    next_state_ = STATE_CTRL_READ;
7468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  } else {
7478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    next_state_ = STATE_CTRL_WRITE;
7488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  }
7498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  return OK;
7508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
7518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
7528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// FTP Commands and responses
7538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
7548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// USER Command.
7558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtint FtpNetworkTransaction::DoCtrlWriteUSER() {
7568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  std::string command = "USER " + base::UTF16ToUTF8(credentials_.username());
7578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
7588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  if (!IsValidFTPCommandString(command))
7598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    return Stop(ERR_MALFORMED_IDENTITY);
7608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
7618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  next_state_ = STATE_CTRL_READ;
7628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  return SendFtpCommand(command, "USER ***", COMMAND_USER);
7638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
7648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
7658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtint FtpNetworkTransaction::ProcessResponseUSER(
7668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    const FtpCtrlResponse& response) {
7678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  switch (GetErrorClass(response.status_code)) {
7688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case ERROR_CLASS_OK:
7698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      next_state_ = STATE_CTRL_WRITE_SYST;
7708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      break;
7718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case ERROR_CLASS_INFO_NEEDED:
7728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      next_state_ = STATE_CTRL_WRITE_PASS;
7738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      break;
7748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case ERROR_CLASS_TRANSIENT_ERROR:
77504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt    case ERROR_CLASS_PERMANENT_ERROR:
7768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      response_.needs_auth = true;
7778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
7788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    default:
7798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      NOTREACHED();
7808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      return Stop(ERR_UNEXPECTED);
7818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  }
7828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  return OK;
7838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
7848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
7858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// PASS command.
7868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtint FtpNetworkTransaction::DoCtrlWritePASS() {
7878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  std::string command = "PASS " + base::UTF16ToUTF8(credentials_.password());
7888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
7898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  if (!IsValidFTPCommandString(command))
7908d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    return Stop(ERR_MALFORMED_IDENTITY);
7918d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
7928d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  next_state_ = STATE_CTRL_READ;
7938d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  return SendFtpCommand(command, "PASS ***", COMMAND_PASS);
7948d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
795d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt
796d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidtint FtpNetworkTransaction::ProcessResponsePASS(
7978d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    const FtpCtrlResponse& response) {
7988d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  switch (GetErrorClass(response.status_code)) {
7998d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case ERROR_CLASS_OK:
8008d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      next_state_ = STATE_CTRL_WRITE_SYST;
8018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      break;
8028d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case ERROR_CLASS_INFO_NEEDED:
8038d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
8048d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case ERROR_CLASS_TRANSIENT_ERROR:
8051f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt    case ERROR_CLASS_PERMANENT_ERROR:
8068d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      response_.needs_auth = true;
8078d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
8088d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    default:
8098d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      NOTREACHED();
8108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      return Stop(ERR_UNEXPECTED);
811d5e4923d04122f81300fa68fb07d64ede28fd44dDmitry Shmidt  }
8128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  return OK;
8138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
8148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
8158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// SYST command.
8168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtint FtpNetworkTransaction::DoCtrlWriteSYST() {
8178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  std::string command = "SYST";
8188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  next_state_ = STATE_CTRL_READ;
8198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  return SendFtpCommand(command, command, COMMAND_SYST);
8208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
8218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
8228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtint FtpNetworkTransaction::ProcessResponseSYST(
8238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    const FtpCtrlResponse& response) {
8248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  switch (GetErrorClass(response.status_code)) {
8258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case ERROR_CLASS_INITIATED:
8268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      return Stop(ERR_INVALID_RESPONSE);
8278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case ERROR_CLASS_OK: {
8288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      // All important info should be on the first line.
8298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      std::string line = response.lines[0];
8308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      // The response should be ASCII, which allows us to do case-insensitive
8318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      // comparisons easily. If it is not ASCII, we leave the system type
8328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      // as unknown.
8338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      if (IsStringASCII(line)) {
8348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        line = StringToLowerASCII(line);
8358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
8368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        // Remove all whitespace, to correctly handle cases like fancy "V M S"
8378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        // response instead of "VMS".
8388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        base::RemoveChars(line, base::kWhitespaceASCII, &line);
8398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
8408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        // The "magic" strings we test for below have been gathered by an
8418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        // empirical study. VMS needs to come first because some VMS systems
8428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        // also respond with "UNIX emulation", which is not perfect. It is much
8438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        // more reliable to talk to these servers in their native language.
8448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        if (line.find("vms") != std::string::npos) {
8458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt          system_type_ = SYSTEM_TYPE_VMS;
8468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        } else if (line.find("l8") != std::string::npos ||
8478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt                   line.find("unix") != std::string::npos ||
8488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt                   line.find("bsd") != std::string::npos) {
8498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt          system_type_ = SYSTEM_TYPE_UNIX;
8508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        } else if (line.find("win32") != std::string::npos ||
8518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt                   line.find("windows") != std::string::npos) {
8528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt          system_type_ = SYSTEM_TYPE_WINDOWS;
8538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        } else if (line.find("os/2") != std::string::npos) {
8548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt          system_type_ = SYSTEM_TYPE_OS2;
8558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        }
8568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      }
8578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      next_state_ = STATE_CTRL_WRITE_PWD;
8588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      break;
8598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    }
8608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case ERROR_CLASS_INFO_NEEDED:
8618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      return Stop(ERR_INVALID_RESPONSE);
8628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case ERROR_CLASS_TRANSIENT_ERROR:
8638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
8648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case ERROR_CLASS_PERMANENT_ERROR:
8658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      // Server does not recognize the SYST command so proceed.
8661f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt      next_state_ = STATE_CTRL_WRITE_PWD;
8678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      break;
8688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    default:
8698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      NOTREACHED();
8708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      return Stop(ERR_UNEXPECTED);
8718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  }
8728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  return OK;
8738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
8748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
8758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// PWD command.
8768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtint FtpNetworkTransaction::DoCtrlWritePWD() {
8778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  std::string command = "PWD";
8788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  next_state_ = STATE_CTRL_READ;
8798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  return SendFtpCommand(command, command, COMMAND_PWD);
8808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
8818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
8828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtint FtpNetworkTransaction::ProcessResponsePWD(const FtpCtrlResponse& response) {
8838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  switch (GetErrorClass(response.status_code)) {
8848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case ERROR_CLASS_INITIATED:
8858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      return Stop(ERR_INVALID_RESPONSE);
8868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case ERROR_CLASS_OK: {
8878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      // The info we look for should be on the first line.
8888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      std::string line = response.lines[0];
8898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      if (line.empty())
8908d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        return Stop(ERR_INVALID_RESPONSE);
8918d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      std::string::size_type quote_pos = line.find('"');
8928d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      if (quote_pos != std::string::npos) {
8938d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        line = line.substr(quote_pos + 1);
8948d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        quote_pos = line.find('"');
8958d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        if (quote_pos == std::string::npos)
8968d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt          return Stop(ERR_INVALID_RESPONSE);
8978d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        line = line.substr(0, quote_pos);
8988d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      }
8998d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      if (system_type_ == SYSTEM_TYPE_VMS)
9008d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        line = FtpUtil::VMSPathToUnix(line);
9018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      if (line.length() && line[line.length() - 1] == '/')
9028d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        line.erase(line.length() - 1);
9038d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      current_remote_directory_ = line;
9048d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      next_state_ = STATE_CTRL_WRITE_TYPE;
9058d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      break;
9068d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    }
9078d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case ERROR_CLASS_INFO_NEEDED:
9088d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      return Stop(ERR_INVALID_RESPONSE);
9098d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case ERROR_CLASS_TRANSIENT_ERROR:
9108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
9118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case ERROR_CLASS_PERMANENT_ERROR:
9128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
9138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    default:
9148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      NOTREACHED();
9158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      return Stop(ERR_UNEXPECTED);
9168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  }
9178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  return OK;
9188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
9198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
9208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// TYPE command.
9218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtint FtpNetworkTransaction::DoCtrlWriteTYPE() {
9228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  std::string command = "TYPE ";
9238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  if (data_type_ == DATA_TYPE_ASCII) {
9248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    command += "A";
9258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  } else if (data_type_ == DATA_TYPE_IMAGE) {
9268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    command += "I";
9278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  } else {
9288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    NOTREACHED();
9298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    return Stop(ERR_UNEXPECTED);
9308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  }
9318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  next_state_ = STATE_CTRL_READ;
9328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  return SendFtpCommand(command, command, COMMAND_TYPE);
9338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
934f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt
935f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidtint FtpNetworkTransaction::ProcessResponseTYPE(
936f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt    const FtpCtrlResponse& response) {
937f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt  switch (GetErrorClass(response.status_code)) {
938f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt    case ERROR_CLASS_INITIATED:
939f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt      return Stop(ERR_INVALID_RESPONSE);
940f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt    case ERROR_CLASS_OK:
941f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt      next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV;
942f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt      break;
943f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt    case ERROR_CLASS_INFO_NEEDED:
944f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt      return Stop(ERR_INVALID_RESPONSE);
945f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt    case ERROR_CLASS_TRANSIENT_ERROR:
946f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
947f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt    case ERROR_CLASS_PERMANENT_ERROR:
948f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
949f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt    default:
950f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt      NOTREACHED();
951f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt      return Stop(ERR_UNEXPECTED);
952f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt  }
953f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt  return OK;
954f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt}
955f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt
956f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt// EPSV command
957f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidtint FtpNetworkTransaction::DoCtrlWriteEPSV() {
958f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt  const std::string command = "EPSV";
959f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt  next_state_ = STATE_CTRL_READ;
960cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt  return SendFtpCommand(command, command, COMMAND_EPSV);
961cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt}
962cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt
963cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidtint FtpNetworkTransaction::ProcessResponseEPSV(
964cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt    const FtpCtrlResponse& response) {
965cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt  switch (GetErrorClass(response.status_code)) {
966cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt    case ERROR_CLASS_INITIATED:
967cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt      return Stop(ERR_INVALID_RESPONSE);
968cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt    case ERROR_CLASS_OK:
969cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt      if (!ExtractPortFromEPSVResponse( response, &data_connection_port_))
970cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt        return Stop(ERR_INVALID_RESPONSE);
971cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt      if (data_connection_port_ < 1024 ||
972cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt          !IsPortAllowedByFtp(data_connection_port_))
973f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt        return Stop(ERR_UNSAFE_PORT);
974f86232838cf712377867cb42417c1613ab5dc425Dmitry Shmidt      next_state_ = STATE_DATA_CONNECT;
9758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      break;
9768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case ERROR_CLASS_INFO_NEEDED:
9778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      return Stop(ERR_INVALID_RESPONSE);
9788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case ERROR_CLASS_TRANSIENT_ERROR:
9798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case ERROR_CLASS_PERMANENT_ERROR:
9808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      use_epsv_ = false;
9818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      next_state_ = STATE_CTRL_WRITE_PASV;
9828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      return OK;
9838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    default:
9848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      NOTREACHED();
9858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      return Stop(ERR_UNEXPECTED);
9868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  }
9878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  return OK;
9888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
9898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
9908d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// PASV command
9918d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtint FtpNetworkTransaction::DoCtrlWritePASV() {
9928d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  std::string command = "PASV";
9938d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  next_state_ = STATE_CTRL_READ;
9948d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  return SendFtpCommand(command, command, COMMAND_PASV);
9958d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
9968d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
9978d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtint FtpNetworkTransaction::ProcessResponsePASV(
9988d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    const FtpCtrlResponse& response) {
9998d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  switch (GetErrorClass(response.status_code)) {
10008d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case ERROR_CLASS_INITIATED:
10018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      return Stop(ERR_INVALID_RESPONSE);
10028d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case ERROR_CLASS_OK:
10038d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      if (!ExtractPortFromPASVResponse(response, &data_connection_port_))
10048d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        return Stop(ERR_INVALID_RESPONSE);
10058d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      if (data_connection_port_ < 1024 ||
10068d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt          !IsPortAllowedByFtp(data_connection_port_))
10078d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        return Stop(ERR_UNSAFE_PORT);
10088d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      next_state_ = STATE_DATA_CONNECT;
10098d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      break;
101004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt    case ERROR_CLASS_INFO_NEEDED:
101104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt      return Stop(ERR_INVALID_RESPONSE);
101204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt    case ERROR_CLASS_TRANSIENT_ERROR:
101304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
101404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt    case ERROR_CLASS_PERMANENT_ERROR:
101504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
101604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt    default:
101704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt      NOTREACHED();
101804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt      return Stop(ERR_UNEXPECTED);
101904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt  }
102004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt  return OK;
102104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
102204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
102304949598a23f501be6eec21697465fd46a28840aDmitry Shmidt// RETR command
102404949598a23f501be6eec21697465fd46a28840aDmitry Shmidtint FtpNetworkTransaction::DoCtrlWriteRETR() {
102504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt  std::string command = "RETR " + GetRequestPathForFtpCommand(false);
102604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt  next_state_ = STATE_CTRL_READ;
102704949598a23f501be6eec21697465fd46a28840aDmitry Shmidt  return SendFtpCommand(command, command, COMMAND_RETR);
102804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt}
102904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
10308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtint FtpNetworkTransaction::ProcessResponseRETR(
10318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    const FtpCtrlResponse& response) {
10328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  switch (GetErrorClass(response.status_code)) {
10338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case ERROR_CLASS_INITIATED:
10348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      // We want the client to start reading the response at this point.
10358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      // It got here either through Start or RestartWithAuth. We want that
10368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      // method to complete. Not setting next state here will make DoLoop exit
10378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      // and in turn make Start/RestartWithAuth complete.
10388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      resource_type_ = RESOURCE_TYPE_FILE;
10398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      break;
10408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case ERROR_CLASS_OK:
10418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      resource_type_ = RESOURCE_TYPE_FILE;
10428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      next_state_ = STATE_CTRL_WRITE_QUIT;
10438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      break;
10448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case ERROR_CLASS_INFO_NEEDED:
10458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
10468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case ERROR_CLASS_TRANSIENT_ERROR:
10478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
10488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case ERROR_CLASS_PERMANENT_ERROR:
10498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      // Code 550 means "Failed to open file". Other codes are unrelated,
10508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      // like "Not logged in" etc.
10518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      if (response.status_code != 550 || resource_type_ == RESOURCE_TYPE_FILE)
10528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
10538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
10548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      // It's possible that RETR failed because the path is a directory.
10558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      resource_type_ = RESOURCE_TYPE_DIRECTORY;
10568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
10578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      // We're going to try CWD next, but first send a PASV one more time,
10588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      // because some FTP servers, including FileZilla, require that.
10598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      // See http://crbug.com/25316.
10608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV;
10618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      break;
10628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    default:
10638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      NOTREACHED();
10648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      return Stop(ERR_UNEXPECTED);
10658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  }
10668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
10671f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt  // We should be sure about our resource type now. Otherwise we risk
10681f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt  // an infinite loop (RETR can later send CWD, and CWD can later send RETR).
10691f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt  DCHECK_NE(RESOURCE_TYPE_UNKNOWN, resource_type_);
10708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
10718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  return OK;
10728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
10738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1074cce06667447b5aec83452adb0c15100ada531095Dmitry Shmidt// SIZE command
10758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtint FtpNetworkTransaction::DoCtrlWriteSIZE() {
1076cce06667447b5aec83452adb0c15100ada531095Dmitry Shmidt  std::string command = "SIZE " + GetRequestPathForFtpCommand(false);
10778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  next_state_ = STATE_CTRL_READ;
10788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  return SendFtpCommand(command, command, COMMAND_SIZE);
10798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
10808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
10811f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidtint FtpNetworkTransaction::ProcessResponseSIZE(
10821f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt    const FtpCtrlResponse& response) {
10831f69aa52ea2e0a73ac502565df8c666ee49cab6aDmitry Shmidt  State state_after_size;
10848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  if (resource_type_ == RESOURCE_TYPE_FILE)
10858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    state_after_size = STATE_CTRL_WRITE_RETR;
10868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  else
10878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    state_after_size = STATE_CTRL_WRITE_CWD;
10888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
10898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  switch (GetErrorClass(response.status_code)) {
1090e0e48dc666fb14a7bb60264ca87463ba7bc1fe0bDmitry Shmidt    case ERROR_CLASS_INITIATED:
1091e0e48dc666fb14a7bb60264ca87463ba7bc1fe0bDmitry Shmidt      next_state_ = state_after_size;
1092e0e48dc666fb14a7bb60264ca87463ba7bc1fe0bDmitry Shmidt      break;
1093e0e48dc666fb14a7bb60264ca87463ba7bc1fe0bDmitry Shmidt    case ERROR_CLASS_OK:
1094e0e48dc666fb14a7bb60264ca87463ba7bc1fe0bDmitry Shmidt      if (response.lines.size() != 1)
1095e0e48dc666fb14a7bb60264ca87463ba7bc1fe0bDmitry Shmidt        return Stop(ERR_INVALID_RESPONSE);
1096e0e48dc666fb14a7bb60264ca87463ba7bc1fe0bDmitry Shmidt      int64 size;
1097e0e48dc666fb14a7bb60264ca87463ba7bc1fe0bDmitry Shmidt      if (!base::StringToInt64(response.lines[0], &size))
1098e0e48dc666fb14a7bb60264ca87463ba7bc1fe0bDmitry Shmidt        return Stop(ERR_INVALID_RESPONSE);
1099e0e48dc666fb14a7bb60264ca87463ba7bc1fe0bDmitry Shmidt      if (size < 0)
1100e0e48dc666fb14a7bb60264ca87463ba7bc1fe0bDmitry Shmidt        return Stop(ERR_INVALID_RESPONSE);
1101e0e48dc666fb14a7bb60264ca87463ba7bc1fe0bDmitry Shmidt
1102e0e48dc666fb14a7bb60264ca87463ba7bc1fe0bDmitry Shmidt      // A successful response to SIZE does not mean the resource is a file.
1103e0e48dc666fb14a7bb60264ca87463ba7bc1fe0bDmitry Shmidt      // Some FTP servers (for example, the qnx one) send a SIZE even for
1104e0e48dc666fb14a7bb60264ca87463ba7bc1fe0bDmitry Shmidt      // directories.
1105e0e48dc666fb14a7bb60264ca87463ba7bc1fe0bDmitry Shmidt      response_.expected_content_size = size;
1106e0e48dc666fb14a7bb60264ca87463ba7bc1fe0bDmitry Shmidt
1107e0e48dc666fb14a7bb60264ca87463ba7bc1fe0bDmitry Shmidt      next_state_ = state_after_size;
1108e0e48dc666fb14a7bb60264ca87463ba7bc1fe0bDmitry Shmidt      break;
1109e0e48dc666fb14a7bb60264ca87463ba7bc1fe0bDmitry Shmidt    case ERROR_CLASS_INFO_NEEDED:
1110e0e48dc666fb14a7bb60264ca87463ba7bc1fe0bDmitry Shmidt      next_state_ = state_after_size;
1111e0e48dc666fb14a7bb60264ca87463ba7bc1fe0bDmitry Shmidt      break;
1112e0e48dc666fb14a7bb60264ca87463ba7bc1fe0bDmitry Shmidt    case ERROR_CLASS_TRANSIENT_ERROR:
1113e0e48dc666fb14a7bb60264ca87463ba7bc1fe0bDmitry Shmidt      ResetDataConnectionAfterError(state_after_size);
1114e0e48dc666fb14a7bb60264ca87463ba7bc1fe0bDmitry Shmidt      break;
1115e0e48dc666fb14a7bb60264ca87463ba7bc1fe0bDmitry Shmidt    case ERROR_CLASS_PERMANENT_ERROR:
111604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt      // It's possible that SIZE failed because the path is a directory.
111704f534e89ed127da4077485376f24debc50d80d5Dmitry Shmidt      if (resource_type_ == RESOURCE_TYPE_UNKNOWN &&
111804949598a23f501be6eec21697465fd46a28840aDmitry Shmidt          response.status_code != 550) {
111904949598a23f501be6eec21697465fd46a28840aDmitry Shmidt        return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
112004949598a23f501be6eec21697465fd46a28840aDmitry Shmidt      }
112104949598a23f501be6eec21697465fd46a28840aDmitry Shmidt
112204949598a23f501be6eec21697465fd46a28840aDmitry Shmidt      ResetDataConnectionAfterError(state_after_size);
112304f534e89ed127da4077485376f24debc50d80d5Dmitry Shmidt      break;
112404949598a23f501be6eec21697465fd46a28840aDmitry Shmidt    default:
112504949598a23f501be6eec21697465fd46a28840aDmitry Shmidt      NOTREACHED();
112604949598a23f501be6eec21697465fd46a28840aDmitry Shmidt      return Stop(ERR_UNEXPECTED);
11278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  }
11288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
11298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  return OK;
11308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
11318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
11328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt// CWD command
11338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtint FtpNetworkTransaction::DoCtrlWriteCWD() {
11348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  std::string command = "CWD " + GetRequestPathForFtpCommand(true);
11358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  next_state_ = STATE_CTRL_READ;
11368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  return SendFtpCommand(command, command, COMMAND_CWD);
11378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
11388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
11398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtint FtpNetworkTransaction::ProcessResponseCWD(const FtpCtrlResponse& response) {
11408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  // We should never issue CWD if we know the target resource is a file.
11418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  DCHECK_NE(RESOURCE_TYPE_FILE, resource_type_);
11428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
11438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt  switch (GetErrorClass(response.status_code)) {
11448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case ERROR_CLASS_INITIATED:
11458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      return Stop(ERR_INVALID_RESPONSE);
11468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case ERROR_CLASS_OK:
11478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      next_state_ = STATE_CTRL_WRITE_LIST;
11488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      break;
11498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case ERROR_CLASS_INFO_NEEDED:
11508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      return Stop(ERR_INVALID_RESPONSE);
11518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case ERROR_CLASS_TRANSIENT_ERROR:
11528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      // Some FTP servers send response 451 (not a valid CWD response according
11538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      // to RFC 959) instead of 550.
11548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      if (response.status_code == 451)
11558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        return ProcessResponseCWDNotADirectory();
11568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
11578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
11588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    case ERROR_CLASS_PERMANENT_ERROR:
11598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      if (response.status_code == 550)
11608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt        return ProcessResponseCWDNotADirectory();
11618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
11628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
11638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt    default:
11648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      NOTREACHED();
11658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt      return Stop(ERR_UNEXPECTED);
1166cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt  }
1167cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt
1168cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt  return OK;
1169cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt}
1170cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt
1171cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidtint FtpNetworkTransaction::ProcessResponseCWDNotADirectory() {
1172cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt  if (resource_type_ == RESOURCE_TYPE_DIRECTORY) {
1173cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt    // We're assuming that the resource is a directory, but the server
1174cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt    // says it's not true. The most probable interpretation is that it
1175cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt    // doesn't exist (with FTP we can't be sure).
1176cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt    return Stop(ERR_FILE_NOT_FOUND);
1177cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt  }
1178cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt
1179cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt  // We are here because SIZE failed and we are not sure what the resource
1180cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt  // type is. It could still be file, and SIZE could fail because of
1181cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt  // an access error (http://crbug.com/56734). Try RETR just to be sure.
1182cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt  resource_type_ = RESOURCE_TYPE_FILE;
1183cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt
1184cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt  ResetDataConnectionAfterError(STATE_CTRL_WRITE_RETR);
1185cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt  return OK;
1186cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt}
1187cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt
1188cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt// LIST command
1189cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidtint FtpNetworkTransaction::DoCtrlWriteLIST() {
1190cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt  // Use the -l option for mod_ftp configured in LISTIsNLST mode: the option
1191cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt  // forces LIST output instead of NLST (which would be ambiguous for us
1192cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt  // to parse).
1193cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt  std::string command("LIST -l");
1194cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt  if (system_type_ == SYSTEM_TYPE_VMS)
1195cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt    command = "LIST *.*;0";
1196cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt
1197cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt  next_state_ = STATE_CTRL_READ;
1198cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt  return SendFtpCommand(command, command, COMMAND_LIST);
1199cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt}
1200cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt
1201cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidtint FtpNetworkTransaction::ProcessResponseLIST(
1202cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt    const FtpCtrlResponse& response) {
1203cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt  switch (GetErrorClass(response.status_code)) {
1204cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt    case ERROR_CLASS_INITIATED:
1205cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt      // We want the client to start reading the response at this point.
1206cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt      // It got here either through Start or RestartWithAuth. We want that
1207cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt      // method to complete. Not setting next state here will make DoLoop exit
1208cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt      // and in turn make Start/RestartWithAuth complete.
1209cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt      response_.is_directory_listing = true;
1210cf32e60fa7e0d33fe1551a6dba8dcbbec47ea50eDmitry Shmidt      break;
1211    case ERROR_CLASS_OK:
1212      response_.is_directory_listing = true;
1213      next_state_ = STATE_CTRL_WRITE_QUIT;
1214      break;
1215    case ERROR_CLASS_INFO_NEEDED:
1216      return Stop(ERR_INVALID_RESPONSE);
1217    case ERROR_CLASS_TRANSIENT_ERROR:
1218      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1219    case ERROR_CLASS_PERMANENT_ERROR:
1220      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1221    default:
1222      NOTREACHED();
1223      return Stop(ERR_UNEXPECTED);
1224  }
1225  return OK;
1226}
1227
1228// QUIT command
1229int FtpNetworkTransaction::DoCtrlWriteQUIT() {
1230  std::string command = "QUIT";
1231  next_state_ = STATE_CTRL_READ;
1232  return SendFtpCommand(command, command, COMMAND_QUIT);
1233}
1234
1235int FtpNetworkTransaction::ProcessResponseQUIT(
1236    const FtpCtrlResponse& response) {
1237  ctrl_socket_->Disconnect();
1238  return last_error_;
1239}
1240
1241// Data Connection
1242
1243int FtpNetworkTransaction::DoDataConnect() {
1244  next_state_ = STATE_DATA_CONNECT_COMPLETE;
1245  IPEndPoint ip_endpoint;
1246  AddressList data_address;
1247  // Connect to the same host as the control socket to prevent PASV port
1248  // scanning attacks.
1249  int rv = ctrl_socket_->GetPeerAddress(&ip_endpoint);
1250  if (rv != OK)
1251    return Stop(rv);
1252  data_address = AddressList::CreateFromIPAddress(
1253      ip_endpoint.address(), data_connection_port_);
1254  data_socket_ = socket_factory_->CreateTransportClientSocket(
1255        data_address, net_log_.net_log(), net_log_.source());
1256  net_log_.AddEvent(
1257      NetLog::TYPE_FTP_DATA_CONNECTION,
1258      data_socket_->NetLog().source().ToEventParametersCallback());
1259  return data_socket_->Connect(io_callback_);
1260}
1261
1262int FtpNetworkTransaction::DoDataConnectComplete(int result) {
1263  if (result != OK && use_epsv_) {
1264    // It's possible we hit a broken server, sadly. They can break in different
1265    // ways. Some time out, some reset a connection. Fall back to PASV.
1266    // TODO(phajdan.jr): remember it for future transactions with this server.
1267    // TODO(phajdan.jr): write a test for this code path.
1268    use_epsv_ = false;
1269    next_state_ = STATE_CTRL_WRITE_PASV;
1270    return OK;
1271  }
1272
1273  // Only record the connection error after we've applied all our fallbacks.
1274  // We want to capture the final error, one we're not going to recover from.
1275  RecordDataConnectionError(result);
1276
1277  if (result != OK)
1278    return Stop(result);
1279
1280  next_state_ = state_after_data_connect_complete_;
1281  return OK;
1282}
1283
1284int FtpNetworkTransaction::DoDataRead() {
1285  DCHECK(read_data_buf_.get());
1286  DCHECK_GT(read_data_buf_len_, 0);
1287
1288  if (data_socket_ == NULL || !data_socket_->IsConnected()) {
1289    // If we don't destroy the data socket completely, some servers will wait
1290    // for us (http://crbug.com/21127). The half-closed TCP connection needs
1291    // to be closed on our side too.
1292    data_socket_.reset();
1293
1294    if (ctrl_socket_->IsConnected()) {
1295      // Wait for the server's response, we should get it before sending QUIT.
1296      next_state_ = STATE_CTRL_READ;
1297      return OK;
1298    }
1299
1300    // We are no longer connected to the server, so just finish the transaction.
1301    return Stop(OK);
1302  }
1303
1304  next_state_ = STATE_DATA_READ_COMPLETE;
1305  read_data_buf_->data()[0] = 0;
1306  return data_socket_->Read(
1307      read_data_buf_.get(), read_data_buf_len_, io_callback_);
1308}
1309
1310int FtpNetworkTransaction::DoDataReadComplete(int result) {
1311  return result;
1312}
1313
1314// We're using a histogram as a group of counters, with one bucket for each
1315// enumeration value.  We're only interested in the values of the counters.
1316// Ignore the shape, average, and standard deviation of the histograms because
1317// they are meaningless.
1318//
1319// We use two histograms.  In the first histogram we tally whether the user has
1320// seen an error of that type during the session.  In the second histogram we
1321// tally the total number of times the users sees each errer.
1322void FtpNetworkTransaction::RecordDataConnectionError(int result) {
1323  // Gather data for http://crbug.com/3073. See how many users have trouble
1324  // establishing FTP data connection in passive FTP mode.
1325  enum {
1326    // Data connection successful.
1327    NET_ERROR_OK = 0,
1328
1329    // Local firewall blocked the connection.
1330    NET_ERROR_ACCESS_DENIED = 1,
1331
1332    // Connection timed out.
1333    NET_ERROR_TIMED_OUT = 2,
1334
1335    // Connection has been estabilished, but then got broken (either reset
1336    // or aborted).
1337    NET_ERROR_CONNECTION_BROKEN = 3,
1338
1339    // Connection has been refused.
1340    NET_ERROR_CONNECTION_REFUSED = 4,
1341
1342    // No connection to the internet.
1343    NET_ERROR_INTERNET_DISCONNECTED = 5,
1344
1345    // Could not reach the destination address.
1346    NET_ERROR_ADDRESS_UNREACHABLE = 6,
1347
1348    // A programming error in our network stack.
1349    NET_ERROR_UNEXPECTED = 7,
1350
1351    // Other kind of error.
1352    NET_ERROR_OTHER = 20,
1353
1354    NUM_OF_NET_ERROR_TYPES
1355  } type;
1356  switch (result) {
1357    case OK:
1358      type = NET_ERROR_OK;
1359      break;
1360    case ERR_ACCESS_DENIED:
1361    case ERR_NETWORK_ACCESS_DENIED:
1362      type = NET_ERROR_ACCESS_DENIED;
1363      break;
1364    case ERR_TIMED_OUT:
1365      type = NET_ERROR_TIMED_OUT;
1366      break;
1367    case ERR_CONNECTION_ABORTED:
1368    case ERR_CONNECTION_RESET:
1369    case ERR_CONNECTION_CLOSED:
1370      type = NET_ERROR_CONNECTION_BROKEN;
1371      break;
1372    case ERR_CONNECTION_FAILED:
1373    case ERR_CONNECTION_REFUSED:
1374      type = NET_ERROR_CONNECTION_REFUSED;
1375      break;
1376    case ERR_INTERNET_DISCONNECTED:
1377      type = NET_ERROR_INTERNET_DISCONNECTED;
1378      break;
1379    case ERR_ADDRESS_INVALID:
1380    case ERR_ADDRESS_UNREACHABLE:
1381      type = NET_ERROR_ADDRESS_UNREACHABLE;
1382      break;
1383    case ERR_UNEXPECTED:
1384      type = NET_ERROR_UNEXPECTED;
1385      break;
1386    default:
1387      type = NET_ERROR_OTHER;
1388      break;
1389  };
1390  static bool had_error_type[NUM_OF_NET_ERROR_TYPES];
1391
1392  DCHECK(type >= 0 && type < NUM_OF_NET_ERROR_TYPES);
1393  if (!had_error_type[type]) {
1394    had_error_type[type] = true;
1395    UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorHappened",
1396        type, NUM_OF_NET_ERROR_TYPES);
1397  }
1398  UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorCount",
1399      type, NUM_OF_NET_ERROR_TYPES);
1400}
1401
1402}  // namespace net
1403