ftp_network_transaction.cc revision 6e8cce623b6e4fe0c9e4af605d675dd9d0338c38
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/ftp/ftp_network_transaction.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h"
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind_helpers.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/compiler_specific.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/metrics/histogram.h"
115e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)#include "base/strings/string_number_conversions.h"
125e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)#include "base/strings/string_util.h"
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_split.h"
14868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/values.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/address_list.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/connection_type_histograms.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/escape.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/net_errors.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/net_log.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/net_util.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/ftp/ftp_network_session.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/ftp/ftp_request_info.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/ftp/ftp_util.h"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/socket/client_socket_factory.h"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/socket/stream_socket.h"
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kCRLF[] = "\r\n";
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kCtrlBufLen = 1024;
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns true if |input| can be safely used as a part of FTP command.
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool IsValidFTPCommandString(const std::string& input) {
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // RFC 959 only allows ASCII strings, but at least Firefox can send non-ASCII
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // characters in the command if the request path contains them. To be
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // compatible, we do the same and allow non-ASCII characters in a command.
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Protect agains newline injection attack.
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (input.find_first_of("\r\n") != std::string::npos)
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)enum ErrorClass {
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The requested action was initiated. The client should expect another
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // reply before issuing the next command.
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ERROR_CLASS_INITIATED,
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The requested action has been successfully completed.
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ERROR_CLASS_OK,
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The command has been accepted, but to complete the operation, more
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // information must be sent by the client.
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ERROR_CLASS_INFO_NEEDED,
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The command was not accepted and the requested action did not take place.
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This condition is temporary, and the client is encouraged to restart the
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // command sequence.
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ERROR_CLASS_TRANSIENT_ERROR,
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The command was not accepted and the requested action did not take place.
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This condition is rather permanent, and the client is discouraged from
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // repeating the exact request.
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ERROR_CLASS_PERMANENT_ERROR,
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns the error class for given response code. Caller should ensure
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// that |response_code| is in range 100-599.
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ErrorClass GetErrorClass(int response_code) {
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (response_code >= 100 && response_code <= 199)
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ERROR_CLASS_INITIATED;
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (response_code >= 200 && response_code <= 299)
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ERROR_CLASS_OK;
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (response_code >= 300 && response_code <= 399)
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ERROR_CLASS_INFO_NEEDED;
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (response_code >= 400 && response_code <= 499)
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ERROR_CLASS_TRANSIENT_ERROR;
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (response_code >= 500 && response_code <= 599)
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ERROR_CLASS_PERMANENT_ERROR;
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We should not be called on invalid error codes.
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  NOTREACHED() << response_code;
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ERROR_CLASS_PERMANENT_ERROR;
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns network error code for received FTP |response_code|.
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int GetNetErrorCodeForFtpResponseCode(int response_code) {
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (response_code) {
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 421:
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return net::ERR_FTP_SERVICE_UNAVAILABLE;
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 426:
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return net::ERR_FTP_TRANSFER_ABORTED;
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 450:
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return net::ERR_FTP_FILE_BUSY;
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 500:
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 501:
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return net::ERR_FTP_SYNTAX_ERROR;
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 502:
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 504:
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return net::ERR_FTP_COMMAND_NOT_SUPPORTED;
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case 503:
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return net::ERR_FTP_BAD_COMMAND_SEQUENCE;
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return net::ERR_FTP_FAILED;
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// From RFC 2428 Section 3:
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//   The text returned in response to the EPSV command MUST be:
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//     <some text> (<d><d><d><tcp-port><d>)
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//   <d> is a delimiter character, ideally to be |
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ExtractPortFromEPSVResponse(const net::FtpCtrlResponse& response,
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 int* port) {
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (response.lines.size() != 1)
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const char* ptr = response.lines[0].c_str();
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (*ptr && *ptr != '(')
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ++ptr;
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!*ptr)
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  char sep = *(++ptr);
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!sep || isdigit(sep) || *(++ptr) != sep || *(++ptr) != sep)
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!isdigit(*(++ptr)))
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *port = *ptr - '0';
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (isdigit(*(++ptr))) {
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    *port *= 10;
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    *port += *ptr - '0';
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (*ptr != sep)
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// There are two way we can receive IP address and port.
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// (127,0,0,1,23,21) IP address and port encapsulated in ().
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 127,0,0,1,23,21  IP address and port without ().
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// See RFC 959, Section 4.1.2
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool ExtractPortFromPASVResponse(const net::FtpCtrlResponse& response,
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 int* port) {
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (response.lines.size() != 1)
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string line(response.lines[0]);
155010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  if (!base::IsStringASCII(line))
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (line.length() < 2)
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t paren_pos = line.find('(');
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (paren_pos == std::string::npos) {
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Find the first comma and use it to locate the beginning
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // of the response data.
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    size_t comma_pos = line.find(',');
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (comma_pos == std::string::npos)
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    size_t space_pos = line.rfind(' ', comma_pos);
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (space_pos != std::string::npos)
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      line = line.substr(space_pos + 1);
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Remove the parentheses and use the text inside them.
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    size_t closing_paren_pos = line.rfind(')');
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (closing_paren_pos == std::string::npos)
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (closing_paren_pos <= paren_pos)
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    line = line.substr(paren_pos + 1, closing_paren_pos - paren_pos - 1);
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Split the line into comma-separated pieces and extract
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the last two.
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<std::string> pieces;
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::SplitString(line, ',', &pieces);
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (pieces.size() != 6)
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Ignore the IP address supplied in the response. We are always going
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // to connect back to the same server to prevent FTP PASV port scanning.
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int p0, p1;
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!base::StringToInt(pieces[4], &p0))
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!base::StringToInt(pieces[5], &p1))
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *port = (p0 << 8) + p1;
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace net {
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)FtpNetworkTransaction::FtpNetworkTransaction(
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    FtpNetworkSession* session,
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ClientSocketFactory* socket_factory)
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : command_sent_(COMMAND_NONE),
209c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      io_callback_(base::Bind(&FtpNetworkTransaction::OnIOComplete,
210c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                              base::Unretained(this))),
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      session_(session),
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      request_(NULL),
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      resolver_(session->host_resolver()),
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      read_ctrl_buf_(new IOBuffer(kCtrlBufLen)),
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      read_data_buf_len_(0),
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      last_error_(OK),
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      system_type_(SYSTEM_TYPE_UNKNOWN),
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Use image (binary) transfer by default. It should always work,
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // whereas the ascii transfer may damage binary data.
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      data_type_(DATA_TYPE_IMAGE),
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      resource_type_(RESOURCE_TYPE_UNKNOWN),
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      use_epsv_(true),
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      data_connection_port_(0),
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      socket_factory_(socket_factory),
2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      next_state_(STATE_NONE),
2267d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      state_after_data_connect_complete_(STATE_CTRL_WRITE_SIZE) {}
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)FtpNetworkTransaction::~FtpNetworkTransaction() {
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::Stop(int error) {
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (command_sent_ == COMMAND_QUIT)
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return error;
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_WRITE_QUIT;
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  last_error_ = error;
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OK;
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::RestartIgnoringLastError(
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const CompletionCallback& callback) {
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ERR_NOT_IMPLEMENTED;
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::Start(const FtpRequestInfo* request_info,
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 const CompletionCallback& callback,
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 const BoundNetLog& net_log) {
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  net_log_ = net_log;
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  request_ = request_info;
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ctrl_response_buffer_.reset(new FtpCtrlResponseBuffer(net_log_));
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (request_->url.has_username()) {
254c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    base::string16 username;
255c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    base::string16 password;
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GetIdentityFromURL(request_->url, &username, &password);
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    credentials_.Set(username, password);
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
2595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    credentials_.Set(base::ASCIIToUTF16("anonymous"),
2605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                     base::ASCIIToUTF16("chrome@example.com"));
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DetectTypecode();
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_RESOLVE_HOST;
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int rv = DoLoop(OK);
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (rv == ERR_IO_PENDING)
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    user_callback_ = callback;
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return rv;
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::RestartWithAuth(const AuthCredentials& credentials,
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                           const CompletionCallback& callback) {
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ResetStateForRestart();
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  credentials_ = credentials;
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_RESOLVE_HOST;
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int rv = DoLoop(OK);
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (rv == ERR_IO_PENDING)
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    user_callback_ = callback;
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return rv;
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::Read(IOBuffer* buf,
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                int buf_len,
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                const CompletionCallback& callback) {
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(buf);
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_GT(buf_len, 0);
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  read_data_buf_ = buf;
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  read_data_buf_len_ = buf_len;
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_DATA_READ;
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int rv = DoLoop(OK);
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (rv == ERR_IO_PENDING)
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    user_callback_ = callback;
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return rv;
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const FtpResponseInfo* FtpNetworkTransaction::GetResponseInfo() const {
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return &response_;
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)LoadState FtpNetworkTransaction::GetLoadState() const {
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (next_state_ == STATE_CTRL_RESOLVE_HOST_COMPLETE)
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return LOAD_STATE_RESOLVING_HOST;
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (next_state_ == STATE_CTRL_CONNECT_COMPLETE ||
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_ == STATE_DATA_CONNECT_COMPLETE)
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return LOAD_STATE_CONNECTING;
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (next_state_ == STATE_DATA_READ_COMPLETE)
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return LOAD_STATE_READING_RESPONSE;
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (command_sent_ == COMMAND_RETR && read_data_buf_.get())
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return LOAD_STATE_READING_RESPONSE;
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (command_sent_ == COMMAND_QUIT)
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return LOAD_STATE_IDLE;
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (command_sent_ != COMMAND_NONE)
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return LOAD_STATE_SENDING_REQUEST;
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return LOAD_STATE_IDLE;
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)uint64 FtpNetworkTransaction::GetUploadProgress() const {
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return 0;
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void FtpNetworkTransaction::ResetStateForRestart() {
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  command_sent_ = COMMAND_NONE;
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  user_callback_.Reset();
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  response_ = FtpResponseInfo();
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  read_ctrl_buf_ = new IOBuffer(kCtrlBufLen);
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ctrl_response_buffer_.reset(new FtpCtrlResponseBuffer(net_log_));
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  read_data_buf_ = NULL;
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  read_data_buf_len_ = 0;
340868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (write_buf_.get())
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    write_buf_->SetOffset(0);
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  last_error_ = OK;
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  data_connection_port_ = 0;
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ctrl_socket_.reset();
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  data_socket_.reset();
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_NONE;
3472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  state_after_data_connect_complete_ = STATE_CTRL_WRITE_SIZE;
3482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void FtpNetworkTransaction::ResetDataConnectionAfterError(State next_state) {
3512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The server _might_ have reset the data connection
3522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // (see RFC 959 3.2. ESTABLISHING DATA CONNECTIONS:
3532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // "The server MUST close the data connection under the following
3542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // conditions:
3552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // ...
3562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // 5. An irrecoverable error condition occurs.")
3572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  //
3582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // It is ambiguous what an irrecoverable error condition is,
3592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // so we take no chances.
3602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  state_after_data_connect_complete_ = next_state;
3612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV;
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void FtpNetworkTransaction::DoCallback(int rv) {
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(rv != ERR_IO_PENDING);
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!user_callback_.is_null());
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Since Run may result in Read being called, clear callback_ up front.
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CompletionCallback c = user_callback_;
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  user_callback_.Reset();
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  c.Run(rv);
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void FtpNetworkTransaction::OnIOComplete(int result) {
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int rv = DoLoop(result);
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (rv != ERR_IO_PENDING)
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DoCallback(rv);
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::ProcessCtrlResponse() {
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  FtpCtrlResponse response = ctrl_response_buffer_->PopResponse();
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int rv = OK;
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (command_sent_) {
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case COMMAND_NONE:
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // TODO(phajdan.jr): Check for errors in the welcome message.
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_ = STATE_CTRL_WRITE_USER;
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case COMMAND_USER:
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rv = ProcessResponseUSER(response);
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case COMMAND_PASS:
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rv = ProcessResponsePASS(response);
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case COMMAND_SYST:
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rv = ProcessResponseSYST(response);
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case COMMAND_PWD:
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rv = ProcessResponsePWD(response);
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case COMMAND_TYPE:
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rv = ProcessResponseTYPE(response);
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case COMMAND_EPSV:
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rv = ProcessResponseEPSV(response);
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case COMMAND_PASV:
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rv = ProcessResponsePASV(response);
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case COMMAND_SIZE:
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rv = ProcessResponseSIZE(response);
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case COMMAND_RETR:
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rv = ProcessResponseRETR(response);
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case COMMAND_CWD:
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rv = ProcessResponseCWD(response);
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case COMMAND_LIST:
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rv = ProcessResponseLIST(response);
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case COMMAND_QUIT:
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rv = ProcessResponseQUIT(response);
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      LOG(DFATAL) << "Unexpected value of command_sent_: " << command_sent_;
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return ERR_UNEXPECTED;
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We may get multiple responses for some commands,
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // see http://crbug.com/18036.
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (ctrl_response_buffer_->ResponseAvailable() && rv == OK) {
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    response = ctrl_response_buffer_->PopResponse();
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    switch (command_sent_) {
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case COMMAND_RETR:
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = ProcessResponseRETR(response);
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case COMMAND_LIST:
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = ProcessResponseLIST(response);
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      default:
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Multiple responses for other commands are invalid.
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return Stop(ERR_INVALID_RESPONSE);
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return rv;
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Used to prepare and send FTP command.
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::SendFtpCommand(const std::string& command,
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                          const std::string& command_for_log,
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                          Command cmd) {
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If we send a new command when we still have unprocessed responses
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // for previous commands, the response receiving code will have no way to know
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // which responses are for which command.
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!ctrl_response_buffer_->ResponseAvailable());
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
460868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  DCHECK(!write_command_buf_.get());
461868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  DCHECK(!write_buf_.get());
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!IsValidFTPCommandString(command)) {
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Callers should validate the command themselves and return a more specific
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // error code.
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED();
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return Stop(ERR_UNEXPECTED);
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  command_sent_ = cmd;
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  write_command_buf_ = new IOBufferWithSize(command.length() + 2);
473868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  write_buf_ = new DrainableIOBuffer(write_command_buf_.get(),
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     write_command_buf_->size());
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  memcpy(write_command_buf_->data(), command.data(), command.length());
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  memcpy(write_command_buf_->data() + command.length(), kCRLF, 2);
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  net_log_.AddEvent(NetLog::TYPE_FTP_COMMAND_SENT,
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    NetLog::StringCallback("command", &command_for_log));
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_WRITE;
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OK;
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::string FtpNetworkTransaction::GetRequestPathForFtpCommand(
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bool is_directory) const {
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string path(current_remote_directory_);
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (request_->url.has_path()) {
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string gurl_path(request_->url.path());
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Get rid of the typecode, see RFC 1738 section 3.2.2. FTP url-path.
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string::size_type pos = gurl_path.rfind(';');
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (pos != std::string::npos)
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gurl_path.resize(pos);
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    path.append(gurl_path);
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Make sure that if the path is expected to be a file, it won't end
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // with a trailing slash.
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!is_directory && path.length() > 1 && path[path.length() - 1] == '/')
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    path.erase(path.length() - 1);
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UnescapeRule::Type unescape_rules = UnescapeRule::SPACES |
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      UnescapeRule::URL_SPECIAL_CHARS;
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This may unescape to non-ASCII characters, but we allow that. See the
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // comment for IsValidFTPCommandString.
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  path = net::UnescapeURLComponent(path, unescape_rules);
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (system_type_ == SYSTEM_TYPE_VMS) {
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (is_directory)
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      path = FtpUtil::UnixDirectoryPathToVMS(path);
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      path = FtpUtil::UnixFilePathToVMS(path);
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(IsValidFTPCommandString(path));
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return path;
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void FtpNetworkTransaction::DetectTypecode() {
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!request_->url.has_path())
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string gurl_path(request_->url.path());
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Extract the typecode, see RFC 1738 section 3.2.2. FTP url-path.
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string::size_type pos = gurl_path.rfind(';');
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (pos == std::string::npos)
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string typecode_string(gurl_path.substr(pos));
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (typecode_string == ";type=a") {
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    data_type_ = DATA_TYPE_ASCII;
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    resource_type_ = RESOURCE_TYPE_FILE;
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (typecode_string == ";type=i") {
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    data_type_ = DATA_TYPE_IMAGE;
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    resource_type_ = RESOURCE_TYPE_FILE;
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (typecode_string == ";type=d") {
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    resource_type_ = RESOURCE_TYPE_DIRECTORY;
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoLoop(int result) {
5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(next_state_ != STATE_NONE);
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int rv = result;
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  do {
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    State state = next_state_;
5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    next_state_ = STATE_NONE;
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    switch (state) {
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_RESOLVE_HOST:
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(rv == OK);
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlResolveHost();
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_RESOLVE_HOST_COMPLETE:
5535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlResolveHostComplete(rv);
5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_CONNECT:
5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(rv == OK);
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlConnect();
5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_CONNECT_COMPLETE:
5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlConnectComplete(rv);
5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_READ:
5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(rv == OK);
5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlRead();
5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_READ_COMPLETE:
5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlReadComplete(rv);
5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_WRITE:
5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(rv == OK);
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlWrite();
5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
5735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_WRITE_COMPLETE:
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlWriteComplete(rv);
5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_WRITE_USER:
5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(rv == OK);
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlWriteUSER();
5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_WRITE_PASS:
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(rv == OK);
5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlWritePASS();
5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_WRITE_SYST:
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(rv == OK);
5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlWriteSYST();
5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_WRITE_PWD:
5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(rv == OK);
5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlWritePWD();
5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_WRITE_TYPE:
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(rv == OK);
5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlWriteTYPE();
5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_WRITE_EPSV:
5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(rv == OK);
5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlWriteEPSV();
5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_WRITE_PASV:
6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(rv == OK);
6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlWritePASV();
6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_WRITE_RETR:
6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(rv == OK);
6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlWriteRETR();
6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_WRITE_SIZE:
6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(rv == OK);
6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlWriteSIZE();
6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_WRITE_CWD:
6135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(rv == OK);
6145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlWriteCWD();
6155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
6165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_WRITE_LIST:
6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(rv == OK);
6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlWriteLIST();
6195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_WRITE_QUIT:
6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(rv == OK);
6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlWriteQUIT();
6235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_DATA_CONNECT:
6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(rv == OK);
6265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoDataConnect();
6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
6285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_DATA_CONNECT_COMPLETE:
6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoDataConnectComplete(rv);
6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_DATA_READ:
6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(rv == OK);
6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoDataRead();
6345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
6355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_DATA_READ_COMPLETE:
6365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoDataReadComplete(rv);
6375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      default:
6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        NOTREACHED() << "bad state";
6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = ERR_UNEXPECTED;
6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
6425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
6445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return rv;
6455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlResolveHost() {
6485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_RESOLVE_HOST_COMPLETE;
6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HostResolver::RequestInfo info(HostPortPair::FromURL(request_->url));
6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // No known referrer.
6525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return resolver_.Resolve(
6533551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      info,
6543551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      DEFAULT_PRIORITY,
6553551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      &addresses_,
6565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::Bind(&FtpNetworkTransaction::OnIOComplete, base::Unretained(this)),
6575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      net_log_);
6585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlResolveHostComplete(int result) {
6615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (result == OK)
6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    next_state_ = STATE_CTRL_CONNECT;
6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return result;
6645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlConnect() {
6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_CONNECT_COMPLETE;
6683551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  ctrl_socket_ = socket_factory_->CreateTransportClientSocket(
6693551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      addresses_, net_log_.net_log(), net_log_.source());
6705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  net_log_.AddEvent(
6715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NetLog::TYPE_FTP_CONTROL_CONNECTION,
6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ctrl_socket_->NetLog().source().ToEventParametersCallback());
6735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ctrl_socket_->Connect(io_callback_);
6745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlConnectComplete(int result) {
6775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (result == OK) {
6785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Put the peer's IP address and port into the response.
6795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    IPEndPoint ip_endpoint;
6805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    result = ctrl_socket_->GetPeerAddress(&ip_endpoint);
6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (result == OK) {
6825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      response_.socket_address = HostPortPair::FromIPEndPoint(ip_endpoint);
6835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_ = STATE_CTRL_READ;
6842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (ip_endpoint.GetFamily() == ADDRESS_FAMILY_IPV4) {
6862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // Do not use EPSV for IPv4 connections. Some servers become confused
6872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // and we time out while waiting to connect. PASV is perfectly fine for
6882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // IPv4. Note that this blacklists IPv4 not to use EPSV instead of
6892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // whitelisting IPv6 to use it, to make the code more future-proof:
6902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // all future protocols should just use EPSV.
6912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        use_epsv_ = false;
6922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
6935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
6945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return result;
6965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlRead() {
6995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_READ_COMPLETE;
700868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  return ctrl_socket_->Read(read_ctrl_buf_.get(), kCtrlBufLen, io_callback_);
7015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlReadComplete(int result) {
7045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (result == 0) {
7055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Some servers (for example Pure-FTPd) apparently close the control
7065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // connection when anonymous login is not permitted. For more details
7075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // see http://crbug.com/25023.
7085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (command_sent_ == COMMAND_USER &&
7095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        credentials_.username() == base::ASCIIToUTF16("anonymous")) {
7105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      response_.needs_auth = true;
7115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
7125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return Stop(ERR_EMPTY_RESPONSE);
7135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (result < 0)
7155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return Stop(result);
7165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ctrl_response_buffer_->ConsumeData(read_ctrl_buf_->data(), result);
7185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!ctrl_response_buffer_->ResponseAvailable()) {
7205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Read more data from the control socket.
7215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    next_state_ = STATE_CTRL_READ;
7225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return OK;
7235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ProcessCtrlResponse();
7265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlWrite() {
7295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_WRITE_COMPLETE;
7305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
731868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  return ctrl_socket_->Write(
732868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      write_buf_.get(), write_buf_->BytesRemaining(), io_callback_);
7335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlWriteComplete(int result) {
7365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (result < 0)
7375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return result;
7385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  write_buf_->DidConsume(result);
7405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (write_buf_->BytesRemaining() == 0) {
7415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Clear the write buffer.
7425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    write_buf_ = NULL;
7435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    write_command_buf_ = NULL;
7445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    next_state_ = STATE_CTRL_READ;
7465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
7475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    next_state_ = STATE_CTRL_WRITE;
7485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OK;
7505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// FTP Commands and responses
7535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// USER Command.
7555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlWriteUSER() {
7565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  std::string command = "USER " + base::UTF16ToUTF8(credentials_.username());
7575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!IsValidFTPCommandString(command))
7595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return Stop(ERR_MALFORMED_IDENTITY);
7605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_READ;
7625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return SendFtpCommand(command, "USER ***", COMMAND_USER);
7635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::ProcessResponseUSER(
7665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const FtpCtrlResponse& response) {
7675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (GetErrorClass(response.status_code)) {
7685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_OK:
7695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_ = STATE_CTRL_WRITE_SYST;
7705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
7715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INFO_NEEDED:
7725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_ = STATE_CTRL_WRITE_PASS;
7735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
7745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_TRANSIENT_ERROR:
7755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_PERMANENT_ERROR:
7765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      response_.needs_auth = true;
7775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
7785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
7795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
7805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_UNEXPECTED);
7815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OK;
7835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// PASS command.
7865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlWritePASS() {
7875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  std::string command = "PASS " + base::UTF16ToUTF8(credentials_.password());
7885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!IsValidFTPCommandString(command))
7905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return Stop(ERR_MALFORMED_IDENTITY);
7915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_READ;
7935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return SendFtpCommand(command, "PASS ***", COMMAND_PASS);
7945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::ProcessResponsePASS(
7975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const FtpCtrlResponse& response) {
7985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (GetErrorClass(response.status_code)) {
7995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_OK:
8005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_ = STATE_CTRL_WRITE_SYST;
8015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
8025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INFO_NEEDED:
8035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
8045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_TRANSIENT_ERROR:
8055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_PERMANENT_ERROR:
8065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      response_.needs_auth = true;
8075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
8085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
8095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
8105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_UNEXPECTED);
8115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OK;
8135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// SYST command.
8165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlWriteSYST() {
8175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string command = "SYST";
8185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_READ;
8195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return SendFtpCommand(command, command, COMMAND_SYST);
8205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::ProcessResponseSYST(
8235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const FtpCtrlResponse& response) {
8245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (GetErrorClass(response.status_code)) {
8255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INITIATED:
8265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_INVALID_RESPONSE);
8275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_OK: {
8285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // All important info should be on the first line.
8295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::string line = response.lines[0];
8305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // The response should be ASCII, which allows us to do case-insensitive
8315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // comparisons easily. If it is not ASCII, we leave the system type
8325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // as unknown.
833010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      if (base::IsStringASCII(line)) {
8346e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        line = base::StringToLowerASCII(line);
8352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // Remove all whitespace, to correctly handle cases like fancy "V M S"
8372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // response instead of "VMS".
838a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        base::RemoveChars(line, base::kWhitespaceASCII, &line);
8392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // The "magic" strings we test for below have been gathered by an
8412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // empirical study. VMS needs to come first because some VMS systems
8422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // also respond with "UNIX emulation", which is not perfect. It is much
8432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // more reliable to talk to these servers in their native language.
8442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (line.find("vms") != std::string::npos) {
8452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          system_type_ = SYSTEM_TYPE_VMS;
8462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        } else if (line.find("l8") != std::string::npos ||
8472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                   line.find("unix") != std::string::npos ||
8482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                   line.find("bsd") != std::string::npos) {
8495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          system_type_ = SYSTEM_TYPE_UNIX;
8505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        } else if (line.find("win32") != std::string::npos ||
8515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   line.find("windows") != std::string::npos) {
8525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          system_type_ = SYSTEM_TYPE_WINDOWS;
8535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        } else if (line.find("os/2") != std::string::npos) {
8545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          system_type_ = SYSTEM_TYPE_OS2;
8555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
8565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
8575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_ = STATE_CTRL_WRITE_PWD;
8585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
8595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
8605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INFO_NEEDED:
8615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_INVALID_RESPONSE);
8625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_TRANSIENT_ERROR:
8635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
8645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_PERMANENT_ERROR:
8655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Server does not recognize the SYST command so proceed.
8665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_ = STATE_CTRL_WRITE_PWD;
8675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
8685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
8695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
8705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_UNEXPECTED);
8715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OK;
8735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// PWD command.
8765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlWritePWD() {
8775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string command = "PWD";
8785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_READ;
8795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return SendFtpCommand(command, command, COMMAND_PWD);
8805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::ProcessResponsePWD(const FtpCtrlResponse& response) {
8835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (GetErrorClass(response.status_code)) {
8845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INITIATED:
8855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_INVALID_RESPONSE);
8865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_OK: {
8875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // The info we look for should be on the first line.
8885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::string line = response.lines[0];
8895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (line.empty())
8905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return Stop(ERR_INVALID_RESPONSE);
8915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::string::size_type quote_pos = line.find('"');
8925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (quote_pos != std::string::npos) {
8935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        line = line.substr(quote_pos + 1);
8945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        quote_pos = line.find('"');
8955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (quote_pos == std::string::npos)
8965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return Stop(ERR_INVALID_RESPONSE);
8975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        line = line.substr(0, quote_pos);
8985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
8995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (system_type_ == SYSTEM_TYPE_VMS)
9005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        line = FtpUtil::VMSPathToUnix(line);
9015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (line.length() && line[line.length() - 1] == '/')
9025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        line.erase(line.length() - 1);
9035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      current_remote_directory_ = line;
9045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_ = STATE_CTRL_WRITE_TYPE;
9055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
9065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
9075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INFO_NEEDED:
9085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_INVALID_RESPONSE);
9095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_TRANSIENT_ERROR:
9105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
9115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_PERMANENT_ERROR:
9125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
9135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
9145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
9155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_UNEXPECTED);
9165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OK;
9185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TYPE command.
9215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlWriteTYPE() {
9225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string command = "TYPE ";
9235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (data_type_ == DATA_TYPE_ASCII) {
9245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    command += "A";
9255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (data_type_ == DATA_TYPE_IMAGE) {
9265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    command += "I";
9275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
9285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED();
9295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return Stop(ERR_UNEXPECTED);
9305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_READ;
9325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return SendFtpCommand(command, command, COMMAND_TYPE);
9335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::ProcessResponseTYPE(
9365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const FtpCtrlResponse& response) {
9375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (GetErrorClass(response.status_code)) {
9385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INITIATED:
9395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_INVALID_RESPONSE);
9405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_OK:
9415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV;
9425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
9435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INFO_NEEDED:
9445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_INVALID_RESPONSE);
9455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_TRANSIENT_ERROR:
9465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
9475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_PERMANENT_ERROR:
9485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
9495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
9505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
9515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_UNEXPECTED);
9525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OK;
9545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// EPSV command
9575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlWriteEPSV() {
9585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const std::string command = "EPSV";
9595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_READ;
9605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return SendFtpCommand(command, command, COMMAND_EPSV);
9615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::ProcessResponseEPSV(
9645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const FtpCtrlResponse& response) {
9655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (GetErrorClass(response.status_code)) {
9665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INITIATED:
9675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_INVALID_RESPONSE);
9685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_OK:
9695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!ExtractPortFromEPSVResponse( response, &data_connection_port_))
9705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return Stop(ERR_INVALID_RESPONSE);
9715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (data_connection_port_ < 1024 ||
9725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          !IsPortAllowedByFtp(data_connection_port_))
9735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return Stop(ERR_UNSAFE_PORT);
9745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_ = STATE_DATA_CONNECT;
9755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
9765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INFO_NEEDED:
9775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_INVALID_RESPONSE);
9785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_TRANSIENT_ERROR:
9795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_PERMANENT_ERROR:
9805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      use_epsv_ = false;
9815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_ = STATE_CTRL_WRITE_PASV;
9825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return OK;
9835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
9845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
9855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_UNEXPECTED);
9865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OK;
9885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// PASV command
9915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlWritePASV() {
9925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string command = "PASV";
9935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_READ;
9945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return SendFtpCommand(command, command, COMMAND_PASV);
9955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::ProcessResponsePASV(
9985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const FtpCtrlResponse& response) {
9995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (GetErrorClass(response.status_code)) {
10005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INITIATED:
10015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_INVALID_RESPONSE);
10025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_OK:
10035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!ExtractPortFromPASVResponse(response, &data_connection_port_))
10045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return Stop(ERR_INVALID_RESPONSE);
10055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (data_connection_port_ < 1024 ||
10065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          !IsPortAllowedByFtp(data_connection_port_))
10075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return Stop(ERR_UNSAFE_PORT);
10085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_ = STATE_DATA_CONNECT;
10095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
10105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INFO_NEEDED:
10115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_INVALID_RESPONSE);
10125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_TRANSIENT_ERROR:
10135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
10145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_PERMANENT_ERROR:
10155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
10165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
10175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
10185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_UNEXPECTED);
10195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
10205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OK;
10215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
10225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// RETR command
10245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlWriteRETR() {
10255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string command = "RETR " + GetRequestPathForFtpCommand(false);
10265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_READ;
10275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return SendFtpCommand(command, command, COMMAND_RETR);
10285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
10295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::ProcessResponseRETR(
10315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const FtpCtrlResponse& response) {
10325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (GetErrorClass(response.status_code)) {
10335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INITIATED:
10345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // We want the client to start reading the response at this point.
10355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // It got here either through Start or RestartWithAuth. We want that
10365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // method to complete. Not setting next state here will make DoLoop exit
10375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // and in turn make Start/RestartWithAuth complete.
10385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      resource_type_ = RESOURCE_TYPE_FILE;
10395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
10405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_OK:
10415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      resource_type_ = RESOURCE_TYPE_FILE;
10425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_ = STATE_CTRL_WRITE_QUIT;
10435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
10445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INFO_NEEDED:
10455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
10465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_TRANSIENT_ERROR:
10475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
10485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_PERMANENT_ERROR:
10495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Code 550 means "Failed to open file". Other codes are unrelated,
10505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // like "Not logged in" etc.
10515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (response.status_code != 550 || resource_type_ == RESOURCE_TYPE_FILE)
10525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
10535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // It's possible that RETR failed because the path is a directory.
10555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      resource_type_ = RESOURCE_TYPE_DIRECTORY;
10565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // We're going to try CWD next, but first send a PASV one more time,
10585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // because some FTP servers, including FileZilla, require that.
10595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // See http://crbug.com/25316.
10605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV;
10615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
10625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
10635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
10645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_UNEXPECTED);
10655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
10665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We should be sure about our resource type now. Otherwise we risk
10685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // an infinite loop (RETR can later send CWD, and CWD can later send RETR).
10695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_NE(RESOURCE_TYPE_UNKNOWN, resource_type_);
10705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OK;
10725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
10735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// SIZE command
10755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlWriteSIZE() {
10765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string command = "SIZE " + GetRequestPathForFtpCommand(false);
10775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_READ;
10785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return SendFtpCommand(command, command, COMMAND_SIZE);
10795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
10805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::ProcessResponseSIZE(
10825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const FtpCtrlResponse& response) {
10832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  State state_after_size;
10842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (resource_type_ == RESOURCE_TYPE_FILE)
10852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    state_after_size = STATE_CTRL_WRITE_RETR;
10862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  else
10872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    state_after_size = STATE_CTRL_WRITE_CWD;
10882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
10895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (GetErrorClass(response.status_code)) {
10905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INITIATED:
10912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      next_state_ = state_after_size;
10925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
10935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_OK:
10945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (response.lines.size() != 1)
10955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return Stop(ERR_INVALID_RESPONSE);
10965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int64 size;
10975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!base::StringToInt64(response.lines[0], &size))
10985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return Stop(ERR_INVALID_RESPONSE);
10995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (size < 0)
11005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return Stop(ERR_INVALID_RESPONSE);
11015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // A successful response to SIZE does not mean the resource is a file.
11035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Some FTP servers (for example, the qnx one) send a SIZE even for
11045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // directories.
11055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      response_.expected_content_size = size;
11062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
11072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      next_state_ = state_after_size;
11085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
11095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INFO_NEEDED:
11102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      next_state_ = state_after_size;
11115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
11125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_TRANSIENT_ERROR:
11132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ResetDataConnectionAfterError(state_after_size);
11145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
11155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_PERMANENT_ERROR:
11165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // It's possible that SIZE failed because the path is a directory.
11175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (resource_type_ == RESOURCE_TYPE_UNKNOWN &&
11185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          response.status_code != 550) {
11195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
11205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
11212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
11222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ResetDataConnectionAfterError(state_after_size);
11235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
11245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
11255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
11265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_UNEXPECTED);
11275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
11285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OK;
11305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
11315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// CWD command
11335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlWriteCWD() {
11345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string command = "CWD " + GetRequestPathForFtpCommand(true);
11355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_READ;
11365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return SendFtpCommand(command, command, COMMAND_CWD);
11375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
11385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::ProcessResponseCWD(const FtpCtrlResponse& response) {
11405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We should never issue CWD if we know the target resource is a file.
11415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_NE(RESOURCE_TYPE_FILE, resource_type_);
11425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (GetErrorClass(response.status_code)) {
11445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INITIATED:
11455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_INVALID_RESPONSE);
11465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_OK:
11475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_ = STATE_CTRL_WRITE_LIST;
11485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
11495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INFO_NEEDED:
11505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_INVALID_RESPONSE);
11515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_TRANSIENT_ERROR:
11525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Some FTP servers send response 451 (not a valid CWD response according
11535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // to RFC 959) instead of 550.
11545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (response.status_code == 451)
11555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return ProcessResponseCWDNotADirectory();
11565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
11585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_PERMANENT_ERROR:
11595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (response.status_code == 550)
11605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return ProcessResponseCWDNotADirectory();
11615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
11635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
11645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
11655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_UNEXPECTED);
11665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
11675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OK;
11695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
11705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::ProcessResponseCWDNotADirectory() {
11725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (resource_type_ == RESOURCE_TYPE_DIRECTORY) {
11735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We're assuming that the resource is a directory, but the server
11745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // says it's not true. The most probable interpretation is that it
11755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // doesn't exist (with FTP we can't be sure).
11765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return Stop(ERR_FILE_NOT_FOUND);
11775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
11785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We are here because SIZE failed and we are not sure what the resource
11805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // type is. It could still be file, and SIZE could fail because of
11815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // an access error (http://crbug.com/56734). Try RETR just to be sure.
11825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  resource_type_ = RESOURCE_TYPE_FILE;
11835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ResetDataConnectionAfterError(STATE_CTRL_WRITE_RETR);
11855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OK;
11865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
11875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// LIST command
11895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlWriteLIST() {
11902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Use the -l option for mod_ftp configured in LISTIsNLST mode: the option
11912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // forces LIST output instead of NLST (which would be ambiguous for us
11922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // to parse).
11932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::string command("LIST -l");
11942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (system_type_ == SYSTEM_TYPE_VMS)
11952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    command = "LIST *.*;0";
11962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
11975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_READ;
11985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return SendFtpCommand(command, command, COMMAND_LIST);
11995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
12005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::ProcessResponseLIST(
12025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const FtpCtrlResponse& response) {
12035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (GetErrorClass(response.status_code)) {
12045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INITIATED:
12055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // We want the client to start reading the response at this point.
12065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // It got here either through Start or RestartWithAuth. We want that
12075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // method to complete. Not setting next state here will make DoLoop exit
12085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // and in turn make Start/RestartWithAuth complete.
12095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      response_.is_directory_listing = true;
12105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
12115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_OK:
12125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      response_.is_directory_listing = true;
12135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_ = STATE_CTRL_WRITE_QUIT;
12145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
12155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INFO_NEEDED:
12165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_INVALID_RESPONSE);
12175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_TRANSIENT_ERROR:
12185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
12195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_PERMANENT_ERROR:
12205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
12215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
12225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
12235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_UNEXPECTED);
12245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
12255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OK;
12265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
12275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// QUIT command
12295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlWriteQUIT() {
12305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string command = "QUIT";
12315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_READ;
12325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return SendFtpCommand(command, command, COMMAND_QUIT);
12335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
12345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::ProcessResponseQUIT(
12365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const FtpCtrlResponse& response) {
12375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ctrl_socket_->Disconnect();
12385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return last_error_;
12395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
12405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Data Connection
12425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoDataConnect() {
12445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_DATA_CONNECT_COMPLETE;
12455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  IPEndPoint ip_endpoint;
12465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AddressList data_address;
12475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Connect to the same host as the control socket to prevent PASV port
12485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // scanning attacks.
12495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int rv = ctrl_socket_->GetPeerAddress(&ip_endpoint);
12505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (rv != OK)
12515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return Stop(rv);
12525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  data_address = AddressList::CreateFromIPAddress(
12535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ip_endpoint.address(), data_connection_port_);
12543551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  data_socket_ = socket_factory_->CreateTransportClientSocket(
12553551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        data_address, net_log_.net_log(), net_log_.source());
12565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  net_log_.AddEvent(
12575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NetLog::TYPE_FTP_DATA_CONNECTION,
12585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      data_socket_->NetLog().source().ToEventParametersCallback());
12595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return data_socket_->Connect(io_callback_);
12605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
12615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoDataConnectComplete(int result) {
12635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (result != OK && use_epsv_) {
12645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // It's possible we hit a broken server, sadly. They can break in different
12655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // ways. Some time out, some reset a connection. Fall back to PASV.
12665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // TODO(phajdan.jr): remember it for future transactions with this server.
12675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // TODO(phajdan.jr): write a test for this code path.
12685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    use_epsv_ = false;
12695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    next_state_ = STATE_CTRL_WRITE_PASV;
12705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return OK;
12715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
12725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Only record the connection error after we've applied all our fallbacks.
12745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We want to capture the final error, one we're not going to recover from.
12755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RecordDataConnectionError(result);
12765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (result != OK)
12785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return Stop(result);
12795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  next_state_ = state_after_data_connect_complete_;
12815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OK;
12825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
12835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoDataRead() {
1285868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  DCHECK(read_data_buf_.get());
12865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_GT(read_data_buf_len_, 0);
12875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (data_socket_ == NULL || !data_socket_->IsConnected()) {
12895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If we don't destroy the data socket completely, some servers will wait
12905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // for us (http://crbug.com/21127). The half-closed TCP connection needs
12915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // to be closed on our side too.
12925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    data_socket_.reset();
12935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (ctrl_socket_->IsConnected()) {
12955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Wait for the server's response, we should get it before sending QUIT.
12965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_ = STATE_CTRL_READ;
12975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return OK;
12985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
12995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We are no longer connected to the server, so just finish the transaction.
13015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return Stop(OK);
13025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
13035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_DATA_READ_COMPLETE;
13055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  read_data_buf_->data()[0] = 0;
1306868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  return data_socket_->Read(
1307868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      read_data_buf_.get(), read_data_buf_len_, io_callback_);
13085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
13095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoDataReadComplete(int result) {
13115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return result;
13125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
13135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// We're using a histogram as a group of counters, with one bucket for each
13155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// enumeration value.  We're only interested in the values of the counters.
13165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Ignore the shape, average, and standard deviation of the histograms because
13175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// they are meaningless.
13185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
13195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// We use two histograms.  In the first histogram we tally whether the user has
13205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// seen an error of that type during the session.  In the second histogram we
13215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// tally the total number of times the users sees each errer.
13225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void FtpNetworkTransaction::RecordDataConnectionError(int result) {
13235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Gather data for http://crbug.com/3073. See how many users have trouble
13245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // establishing FTP data connection in passive FTP mode.
13255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  enum {
13265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Data connection successful.
13275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NET_ERROR_OK = 0,
13285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Local firewall blocked the connection.
13305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NET_ERROR_ACCESS_DENIED = 1,
13315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Connection timed out.
13335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NET_ERROR_TIMED_OUT = 2,
13345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Connection has been estabilished, but then got broken (either reset
13365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // or aborted).
13375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NET_ERROR_CONNECTION_BROKEN = 3,
13385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Connection has been refused.
13405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NET_ERROR_CONNECTION_REFUSED = 4,
13415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // No connection to the internet.
13435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NET_ERROR_INTERNET_DISCONNECTED = 5,
13445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Could not reach the destination address.
13465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NET_ERROR_ADDRESS_UNREACHABLE = 6,
13475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // A programming error in our network stack.
13495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NET_ERROR_UNEXPECTED = 7,
13505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Other kind of error.
13525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NET_ERROR_OTHER = 20,
13535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NUM_OF_NET_ERROR_TYPES
13555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } type;
13565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (result) {
13575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case OK:
13585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      type = NET_ERROR_OK;
13595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
13605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERR_ACCESS_DENIED:
13615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERR_NETWORK_ACCESS_DENIED:
13625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      type = NET_ERROR_ACCESS_DENIED;
13635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
13645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERR_TIMED_OUT:
13655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      type = NET_ERROR_TIMED_OUT;
13665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
13675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERR_CONNECTION_ABORTED:
13685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERR_CONNECTION_RESET:
13695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERR_CONNECTION_CLOSED:
13705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      type = NET_ERROR_CONNECTION_BROKEN;
13715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
13725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERR_CONNECTION_FAILED:
13735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERR_CONNECTION_REFUSED:
13745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      type = NET_ERROR_CONNECTION_REFUSED;
13755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
13765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERR_INTERNET_DISCONNECTED:
13775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      type = NET_ERROR_INTERNET_DISCONNECTED;
13785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
13795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERR_ADDRESS_INVALID:
13805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERR_ADDRESS_UNREACHABLE:
13815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      type = NET_ERROR_ADDRESS_UNREACHABLE;
13825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
13835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERR_UNEXPECTED:
13845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      type = NET_ERROR_UNEXPECTED;
13855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
13865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
13875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      type = NET_ERROR_OTHER;
13885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
13895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
13905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static bool had_error_type[NUM_OF_NET_ERROR_TYPES];
13915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(type >= 0 && type < NUM_OF_NET_ERROR_TYPES);
13935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!had_error_type[type]) {
13945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    had_error_type[type] = true;
13955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorHappened",
13965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        type, NUM_OF_NET_ERROR_TYPES);
13975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
13985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorCount",
13995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      type, NUM_OF_NET_ERROR_TYPES);
14005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
14015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace net
1403