ftp_network_transaction.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/string_number_conversions.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/string_split.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/string_util.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/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]);
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!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),
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ALLOW_THIS_IN_INITIALIZER_LIST(
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          io_callback_(base::Bind(&FtpNetworkTransaction::OnIOComplete,
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  base::Unretained(this)))),
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      session_(session),
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      request_(NULL),
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      resolver_(session->host_resolver()),
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      read_ctrl_buf_(new IOBuffer(kCtrlBufLen)),
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ctrl_response_buffer_(NULL),
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      read_data_buf_len_(0),
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      last_error_(OK),
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      system_type_(SYSTEM_TYPE_UNKNOWN),
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Use image (binary) transfer by default. It should always work,
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // whereas the ascii transfer may damage binary data.
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      data_type_(DATA_TYPE_IMAGE),
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      resource_type_(RESOURCE_TYPE_UNKNOWN),
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      use_epsv_(true),
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      data_connection_port_(0),
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      socket_factory_(socket_factory),
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_(STATE_NONE) {
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)FtpNetworkTransaction::~FtpNetworkTransaction() {
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::Stop(int error) {
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (command_sent_ == COMMAND_QUIT)
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return error;
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_WRITE_QUIT;
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  last_error_ = error;
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OK;
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::RestartIgnoringLastError(
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const CompletionCallback& callback) {
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ERR_NOT_IMPLEMENTED;
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::Start(const FtpRequestInfo* request_info,
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 const CompletionCallback& callback,
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 const BoundNetLog& net_log) {
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  net_log_ = net_log;
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  request_ = request_info;
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ctrl_response_buffer_.reset(new FtpCtrlResponseBuffer(net_log_));
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (request_->url.has_username()) {
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    string16 username;
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    string16 password;
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GetIdentityFromURL(request_->url, &username, &password);
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    credentials_.Set(username, password);
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    credentials_.Set(ASCIIToUTF16("anonymous"),
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     ASCIIToUTF16("chrome@example.com"));
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DetectTypecode();
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_RESOLVE_HOST;
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int rv = DoLoop(OK);
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (rv == ERR_IO_PENDING)
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    user_callback_ = callback;
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return rv;
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::RestartWithAuth(const AuthCredentials& credentials,
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                           const CompletionCallback& callback) {
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ResetStateForRestart();
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  credentials_ = credentials;
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_RESOLVE_HOST;
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int rv = DoLoop(OK);
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (rv == ERR_IO_PENDING)
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    user_callback_ = callback;
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return rv;
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::Read(IOBuffer* buf,
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                int buf_len,
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                const CompletionCallback& callback) {
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(buf);
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_GT(buf_len, 0);
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  read_data_buf_ = buf;
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  read_data_buf_len_ = buf_len;
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_DATA_READ;
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int rv = DoLoop(OK);
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (rv == ERR_IO_PENDING)
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    user_callback_ = callback;
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return rv;
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const FtpResponseInfo* FtpNetworkTransaction::GetResponseInfo() const {
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return &response_;
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)LoadState FtpNetworkTransaction::GetLoadState() const {
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (next_state_ == STATE_CTRL_RESOLVE_HOST_COMPLETE)
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return LOAD_STATE_RESOLVING_HOST;
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (next_state_ == STATE_CTRL_CONNECT_COMPLETE ||
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_ == STATE_DATA_CONNECT_COMPLETE)
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return LOAD_STATE_CONNECTING;
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (next_state_ == STATE_DATA_READ_COMPLETE)
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return LOAD_STATE_READING_RESPONSE;
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (command_sent_ == COMMAND_RETR && read_data_buf_.get())
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return LOAD_STATE_READING_RESPONSE;
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (command_sent_ == COMMAND_QUIT)
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return LOAD_STATE_IDLE;
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (command_sent_ != COMMAND_NONE)
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return LOAD_STATE_SENDING_REQUEST;
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return LOAD_STATE_IDLE;
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)uint64 FtpNetworkTransaction::GetUploadProgress() const {
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return 0;
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void FtpNetworkTransaction::ResetStateForRestart() {
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  command_sent_ = COMMAND_NONE;
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  user_callback_.Reset();
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  response_ = FtpResponseInfo();
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  read_ctrl_buf_ = new IOBuffer(kCtrlBufLen);
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ctrl_response_buffer_.reset(new FtpCtrlResponseBuffer(net_log_));
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  read_data_buf_ = NULL;
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  read_data_buf_len_ = 0;
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (write_buf_)
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    write_buf_->SetOffset(0);
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  last_error_ = OK;
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  data_connection_port_ = 0;
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ctrl_socket_.reset();
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  data_socket_.reset();
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_NONE;
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void FtpNetworkTransaction::DoCallback(int rv) {
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(rv != ERR_IO_PENDING);
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!user_callback_.is_null());
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Since Run may result in Read being called, clear callback_ up front.
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CompletionCallback c = user_callback_;
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  user_callback_.Reset();
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  c.Run(rv);
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void FtpNetworkTransaction::OnIOComplete(int result) {
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int rv = DoLoop(result);
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (rv != ERR_IO_PENDING)
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DoCallback(rv);
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::ProcessCtrlResponse() {
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  FtpCtrlResponse response = ctrl_response_buffer_->PopResponse();
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int rv = OK;
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (command_sent_) {
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case COMMAND_NONE:
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // TODO(phajdan.jr): Check for errors in the welcome message.
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_ = STATE_CTRL_WRITE_USER;
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case COMMAND_USER:
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rv = ProcessResponseUSER(response);
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case COMMAND_PASS:
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rv = ProcessResponsePASS(response);
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case COMMAND_SYST:
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rv = ProcessResponseSYST(response);
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case COMMAND_PWD:
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rv = ProcessResponsePWD(response);
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case COMMAND_TYPE:
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rv = ProcessResponseTYPE(response);
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case COMMAND_EPSV:
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rv = ProcessResponseEPSV(response);
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case COMMAND_PASV:
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rv = ProcessResponsePASV(response);
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case COMMAND_SIZE:
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rv = ProcessResponseSIZE(response);
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case COMMAND_RETR:
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rv = ProcessResponseRETR(response);
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case COMMAND_CWD:
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rv = ProcessResponseCWD(response);
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case COMMAND_LIST:
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rv = ProcessResponseLIST(response);
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case COMMAND_QUIT:
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      rv = ProcessResponseQUIT(response);
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      LOG(DFATAL) << "Unexpected value of command_sent_: " << command_sent_;
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return ERR_UNEXPECTED;
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We may get multiple responses for some commands,
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // see http://crbug.com/18036.
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (ctrl_response_buffer_->ResponseAvailable() && rv == OK) {
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    response = ctrl_response_buffer_->PopResponse();
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    switch (command_sent_) {
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case COMMAND_RETR:
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = ProcessResponseRETR(response);
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case COMMAND_LIST:
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = ProcessResponseLIST(response);
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      default:
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Multiple responses for other commands are invalid.
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return Stop(ERR_INVALID_RESPONSE);
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return rv;
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Used to prepare and send FTP command.
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::SendFtpCommand(const std::string& command,
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                          const std::string& command_for_log,
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                          Command cmd) {
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If we send a new command when we still have unprocessed responses
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // for previous commands, the response receiving code will have no way to know
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // which responses are for which command.
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!ctrl_response_buffer_->ResponseAvailable());
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!write_command_buf_);
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!write_buf_);
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!IsValidFTPCommandString(command)) {
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Callers should validate the command themselves and return a more specific
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // error code.
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED();
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return Stop(ERR_UNEXPECTED);
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  command_sent_ = cmd;
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  write_command_buf_ = new IOBufferWithSize(command.length() + 2);
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  write_buf_ = new DrainableIOBuffer(write_command_buf_,
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     write_command_buf_->size());
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  memcpy(write_command_buf_->data(), command.data(), command.length());
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  memcpy(write_command_buf_->data() + command.length(), kCRLF, 2);
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  net_log_.AddEvent(NetLog::TYPE_FTP_COMMAND_SENT,
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    NetLog::StringCallback("command", &command_for_log));
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_WRITE;
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OK;
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::string FtpNetworkTransaction::GetRequestPathForFtpCommand(
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bool is_directory) const {
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string path(current_remote_directory_);
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (request_->url.has_path()) {
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string gurl_path(request_->url.path());
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Get rid of the typecode, see RFC 1738 section 3.2.2. FTP url-path.
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string::size_type pos = gurl_path.rfind(';');
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (pos != std::string::npos)
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gurl_path.resize(pos);
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    path.append(gurl_path);
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Make sure that if the path is expected to be a file, it won't end
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // with a trailing slash.
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!is_directory && path.length() > 1 && path[path.length() - 1] == '/')
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    path.erase(path.length() - 1);
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UnescapeRule::Type unescape_rules = UnescapeRule::SPACES |
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      UnescapeRule::URL_SPECIAL_CHARS;
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This may unescape to non-ASCII characters, but we allow that. See the
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // comment for IsValidFTPCommandString.
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  path = net::UnescapeURLComponent(path, unescape_rules);
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (system_type_ == SYSTEM_TYPE_VMS) {
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (is_directory)
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      path = FtpUtil::UnixDirectoryPathToVMS(path);
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      path = FtpUtil::UnixFilePathToVMS(path);
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(IsValidFTPCommandString(path));
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return path;
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void FtpNetworkTransaction::DetectTypecode() {
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!request_->url.has_path())
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string gurl_path(request_->url.path());
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Extract the typecode, see RFC 1738 section 3.2.2. FTP url-path.
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string::size_type pos = gurl_path.rfind(';');
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (pos == std::string::npos)
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string typecode_string(gurl_path.substr(pos));
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (typecode_string == ";type=a") {
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    data_type_ = DATA_TYPE_ASCII;
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    resource_type_ = RESOURCE_TYPE_FILE;
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (typecode_string == ";type=i") {
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    data_type_ = DATA_TYPE_IMAGE;
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    resource_type_ = RESOURCE_TYPE_FILE;
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (typecode_string == ";type=d") {
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    resource_type_ = RESOURCE_TYPE_DIRECTORY;
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoLoop(int result) {
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(next_state_ != STATE_NONE);
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int rv = result;
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  do {
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    State state = next_state_;
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    next_state_ = STATE_NONE;
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    switch (state) {
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_RESOLVE_HOST:
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(rv == OK);
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlResolveHost();
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_RESOLVE_HOST_COMPLETE:
5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlResolveHostComplete(rv);
5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_CONNECT:
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(rv == OK);
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlConnect();
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_CONNECT_COMPLETE:
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlConnectComplete(rv);
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_READ:
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(rv == OK);
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlRead();
5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
5535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_READ_COMPLETE:
5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlReadComplete(rv);
5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_WRITE:
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(rv == OK);
5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlWrite();
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_WRITE_COMPLETE:
5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlWriteComplete(rv);
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_WRITE_USER:
5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(rv == OK);
5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlWriteUSER();
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_WRITE_PASS:
5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(rv == OK);
5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlWritePASS();
5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_WRITE_SYST:
5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(rv == OK);
5735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlWriteSYST();
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_WRITE_PWD:
5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(rv == OK);
5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlWritePWD();
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_WRITE_TYPE:
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(rv == OK);
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlWriteTYPE();
5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_WRITE_EPSV:
5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(rv == OK);
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlWriteEPSV();
5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_WRITE_PASV:
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(rv == OK);
5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlWritePASV();
5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_WRITE_RETR:
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(rv == OK);
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlWriteRETR();
5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_WRITE_SIZE:
5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(rv == OK);
5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlWriteSIZE();
5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_WRITE_CWD:
6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(rv == OK);
6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlWriteCWD();
6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_WRITE_LIST:
6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(rv == OK);
6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlWriteLIST();
6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_CTRL_WRITE_QUIT:
6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(rv == OK);
6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoCtrlWriteQUIT();
6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_DATA_CONNECT:
6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(rv == OK);
6135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoDataConnect();
6145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
6155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_DATA_CONNECT_COMPLETE:
6165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoDataConnectComplete(rv);
6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_DATA_READ:
6195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        DCHECK(rv == OK);
6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoDataRead();
6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case STATE_DATA_READ_COMPLETE:
6235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = DoDataReadComplete(rv);
6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      default:
6265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        NOTREACHED() << "bad state";
6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        rv = ERR_UNEXPECTED;
6285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return rv;
6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlResolveHost() {
6355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_RESOLVE_HOST_COMPLETE;
6365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HostResolver::RequestInfo info(HostPortPair::FromURL(request_->url));
6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // No known referrer.
6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return resolver_.Resolve(
6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      info, &addresses_,
6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::Bind(&FtpNetworkTransaction::OnIOComplete, base::Unretained(this)),
6425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      net_log_);
6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlResolveHostComplete(int result) {
6465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (result == OK)
6475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    next_state_ = STATE_CTRL_CONNECT;
6485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return result;
6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlConnect() {
6525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_CONNECT_COMPLETE;
6535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ctrl_socket_.reset(socket_factory_->CreateTransportClientSocket(
6545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        addresses_, net_log_.net_log(), net_log_.source()));
6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  net_log_.AddEvent(
6565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NetLog::TYPE_FTP_CONTROL_CONNECTION,
6575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ctrl_socket_->NetLog().source().ToEventParametersCallback());
6585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ctrl_socket_->Connect(io_callback_);
6595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlConnectComplete(int result) {
6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (result == OK) {
6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Put the peer's IP address and port into the response.
6645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    IPEndPoint ip_endpoint;
6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    result = ctrl_socket_->GetPeerAddress(&ip_endpoint);
6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (result == OK) {
6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      response_.socket_address = HostPortPair::FromIPEndPoint(ip_endpoint);
6685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_ = STATE_CTRL_READ;
6695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
6705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return result;
6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlRead() {
6755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_READ_COMPLETE;
6765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ctrl_socket_->Read(read_ctrl_buf_, kCtrlBufLen, io_callback_);
6775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlReadComplete(int result) {
6805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (result == 0) {
6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Some servers (for example Pure-FTPd) apparently close the control
6825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // connection when anonymous login is not permitted. For more details
6835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // see http://crbug.com/25023.
6845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (command_sent_ == COMMAND_USER &&
6855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        credentials_.username() == ASCIIToUTF16("anonymous")) {
6865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      response_.needs_auth = true;
6875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
6885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return Stop(ERR_EMPTY_RESPONSE);
6895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (result < 0)
6915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return Stop(result);
6925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ctrl_response_buffer_->ConsumeData(read_ctrl_buf_->data(), result);
6945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!ctrl_response_buffer_->ResponseAvailable()) {
6965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Read more data from the control socket.
6975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    next_state_ = STATE_CTRL_READ;
6985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return OK;
6995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ProcessCtrlResponse();
7025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlWrite() {
7055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_WRITE_COMPLETE;
7065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ctrl_socket_->Write(write_buf_,
7085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             write_buf_->BytesRemaining(),
7095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             io_callback_);
7105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlWriteComplete(int result) {
7135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (result < 0)
7145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return result;
7155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  write_buf_->DidConsume(result);
7175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (write_buf_->BytesRemaining() == 0) {
7185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Clear the write buffer.
7195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    write_buf_ = NULL;
7205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    write_command_buf_ = NULL;
7215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    next_state_ = STATE_CTRL_READ;
7235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
7245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    next_state_ = STATE_CTRL_WRITE;
7255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OK;
7275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// FTP Commands and responses
7305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// USER Command.
7325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlWriteUSER() {
7335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string command = "USER " + UTF16ToUTF8(credentials_.username());
7345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!IsValidFTPCommandString(command))
7365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return Stop(ERR_MALFORMED_IDENTITY);
7375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_READ;
7395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return SendFtpCommand(command, "USER ***", COMMAND_USER);
7405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::ProcessResponseUSER(
7435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const FtpCtrlResponse& response) {
7445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (GetErrorClass(response.status_code)) {
7455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_OK:
7465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_ = STATE_CTRL_WRITE_SYST;
7475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
7485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INFO_NEEDED:
7495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_ = STATE_CTRL_WRITE_PASS;
7505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
7515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_TRANSIENT_ERROR:
7525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_PERMANENT_ERROR:
7535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      response_.needs_auth = true;
7545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
7555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
7565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
7575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_UNEXPECTED);
7585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OK;
7605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// PASS command.
7635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlWritePASS() {
7645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string command = "PASS " + UTF16ToUTF8(credentials_.password());
7655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!IsValidFTPCommandString(command))
7675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return Stop(ERR_MALFORMED_IDENTITY);
7685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_READ;
7705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return SendFtpCommand(command, "PASS ***", COMMAND_PASS);
7715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::ProcessResponsePASS(
7745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const FtpCtrlResponse& response) {
7755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (GetErrorClass(response.status_code)) {
7765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_OK:
7775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_ = STATE_CTRL_WRITE_SYST;
7785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
7795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INFO_NEEDED:
7805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
7815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_TRANSIENT_ERROR:
7825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_PERMANENT_ERROR:
7835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      response_.needs_auth = true;
7845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
7855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
7865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
7875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_UNEXPECTED);
7885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OK;
7905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// SYST command.
7935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlWriteSYST() {
7945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string command = "SYST";
7955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_READ;
7965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return SendFtpCommand(command, command, COMMAND_SYST);
7975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::ProcessResponseSYST(
8005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const FtpCtrlResponse& response) {
8015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (GetErrorClass(response.status_code)) {
8025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INITIATED:
8035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_INVALID_RESPONSE);
8045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_OK: {
8055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // All important info should be on the first line.
8065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::string line = response.lines[0];
8075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // The response should be ASCII, which allows us to do case-insensitive
8085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // comparisons easily. If it is not ASCII, we leave the system type
8095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // as unknown.
8105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (IsStringASCII(line)) {
8115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        line = StringToLowerASCII(line);
8125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // The "magic" strings we test for below have been gathered by an
8135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // empirical study.
8145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (line.find("l8") != std::string::npos ||
8155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            line.find("unix") != std::string::npos ||
8165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            line.find("bsd") != std::string::npos) {
8175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          system_type_ = SYSTEM_TYPE_UNIX;
8185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        } else if (line.find("win32") != std::string::npos ||
8195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   line.find("windows") != std::string::npos) {
8205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          system_type_ = SYSTEM_TYPE_WINDOWS;
8215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        } else if (line.find("os/2") != std::string::npos) {
8225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          system_type_ = SYSTEM_TYPE_OS2;
8235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        } else if (line.find("vms") != std::string::npos) {
8245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          system_type_ = SYSTEM_TYPE_VMS;
8255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
8265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
8275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_ = STATE_CTRL_WRITE_PWD;
8285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
8295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
8305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INFO_NEEDED:
8315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_INVALID_RESPONSE);
8325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_TRANSIENT_ERROR:
8335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
8345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_PERMANENT_ERROR:
8355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Server does not recognize the SYST command so proceed.
8365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_ = STATE_CTRL_WRITE_PWD;
8375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
8385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
8395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
8405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_UNEXPECTED);
8415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OK;
8435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// PWD command.
8465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlWritePWD() {
8475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string command = "PWD";
8485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_READ;
8495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return SendFtpCommand(command, command, COMMAND_PWD);
8505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::ProcessResponsePWD(const FtpCtrlResponse& response) {
8535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (GetErrorClass(response.status_code)) {
8545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INITIATED:
8555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_INVALID_RESPONSE);
8565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_OK: {
8575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // The info we look for should be on the first line.
8585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::string line = response.lines[0];
8595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (line.empty())
8605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return Stop(ERR_INVALID_RESPONSE);
8615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::string::size_type quote_pos = line.find('"');
8625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (quote_pos != std::string::npos) {
8635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        line = line.substr(quote_pos + 1);
8645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        quote_pos = line.find('"');
8655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (quote_pos == std::string::npos)
8665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return Stop(ERR_INVALID_RESPONSE);
8675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        line = line.substr(0, quote_pos);
8685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
8695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (system_type_ == SYSTEM_TYPE_VMS)
8705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        line = FtpUtil::VMSPathToUnix(line);
8715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (line.length() && line[line.length() - 1] == '/')
8725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        line.erase(line.length() - 1);
8735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      current_remote_directory_ = line;
8745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_ = STATE_CTRL_WRITE_TYPE;
8755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
8765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
8775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INFO_NEEDED:
8785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_INVALID_RESPONSE);
8795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_TRANSIENT_ERROR:
8805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
8815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_PERMANENT_ERROR:
8825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
8835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
8845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
8855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_UNEXPECTED);
8865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OK;
8885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TYPE command.
8915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlWriteTYPE() {
8925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string command = "TYPE ";
8935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (data_type_ == DATA_TYPE_ASCII) {
8945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    command += "A";
8955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (data_type_ == DATA_TYPE_IMAGE) {
8965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    command += "I";
8975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
8985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED();
8995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return Stop(ERR_UNEXPECTED);
9005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_READ;
9025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return SendFtpCommand(command, command, COMMAND_TYPE);
9035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::ProcessResponseTYPE(
9065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const FtpCtrlResponse& response) {
9075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (GetErrorClass(response.status_code)) {
9085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INITIATED:
9095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_INVALID_RESPONSE);
9105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_OK:
9115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV;
9125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
9135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INFO_NEEDED:
9145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_INVALID_RESPONSE);
9155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_TRANSIENT_ERROR:
9165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
9175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_PERMANENT_ERROR:
9185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
9195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
9205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
9215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_UNEXPECTED);
9225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OK;
9245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// EPSV command
9275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlWriteEPSV() {
9285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const std::string command = "EPSV";
9295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_READ;
9305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return SendFtpCommand(command, command, COMMAND_EPSV);
9315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::ProcessResponseEPSV(
9345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const FtpCtrlResponse& response) {
9355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (GetErrorClass(response.status_code)) {
9365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INITIATED:
9375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_INVALID_RESPONSE);
9385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_OK:
9395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!ExtractPortFromEPSVResponse( response, &data_connection_port_))
9405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return Stop(ERR_INVALID_RESPONSE);
9415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (data_connection_port_ < 1024 ||
9425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          !IsPortAllowedByFtp(data_connection_port_))
9435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return Stop(ERR_UNSAFE_PORT);
9445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_ = STATE_DATA_CONNECT;
9455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
9465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INFO_NEEDED:
9475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_INVALID_RESPONSE);
9485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_TRANSIENT_ERROR:
9495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_PERMANENT_ERROR:
9505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      use_epsv_ = false;
9515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_ = STATE_CTRL_WRITE_PASV;
9525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return OK;
9535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
9545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
9555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_UNEXPECTED);
9565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OK;
9585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// PASV command
9615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlWritePASV() {
9625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string command = "PASV";
9635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_READ;
9645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return SendFtpCommand(command, command, COMMAND_PASV);
9655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::ProcessResponsePASV(
9685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const FtpCtrlResponse& response) {
9695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (GetErrorClass(response.status_code)) {
9705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INITIATED:
9715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_INVALID_RESPONSE);
9725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_OK:
9735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!ExtractPortFromPASVResponse(response, &data_connection_port_))
9745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return Stop(ERR_INVALID_RESPONSE);
9755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (data_connection_port_ < 1024 ||
9765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          !IsPortAllowedByFtp(data_connection_port_))
9775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return Stop(ERR_UNSAFE_PORT);
9785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_ = STATE_DATA_CONNECT;
9795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
9805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INFO_NEEDED:
9815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_INVALID_RESPONSE);
9825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_TRANSIENT_ERROR:
9835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
9845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_PERMANENT_ERROR:
9855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
9865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
9875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
9885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_UNEXPECTED);
9895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OK;
9915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// RETR command
9945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlWriteRETR() {
9955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string command = "RETR " + GetRequestPathForFtpCommand(false);
9965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_READ;
9975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return SendFtpCommand(command, command, COMMAND_RETR);
9985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::ProcessResponseRETR(
10015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const FtpCtrlResponse& response) {
10025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (GetErrorClass(response.status_code)) {
10035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INITIATED:
10045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // We want the client to start reading the response at this point.
10055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // It got here either through Start or RestartWithAuth. We want that
10065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // method to complete. Not setting next state here will make DoLoop exit
10075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // and in turn make Start/RestartWithAuth complete.
10085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      resource_type_ = RESOURCE_TYPE_FILE;
10095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
10105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_OK:
10115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      resource_type_ = RESOURCE_TYPE_FILE;
10125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_ = STATE_CTRL_WRITE_QUIT;
10135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
10145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INFO_NEEDED:
10155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
10165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_TRANSIENT_ERROR:
10175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
10185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_PERMANENT_ERROR:
10195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Code 550 means "Failed to open file". Other codes are unrelated,
10205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // like "Not logged in" etc.
10215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (response.status_code != 550 || resource_type_ == RESOURCE_TYPE_FILE)
10225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
10235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // It's possible that RETR failed because the path is a directory.
10255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      resource_type_ = RESOURCE_TYPE_DIRECTORY;
10265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // We're going to try CWD next, but first send a PASV one more time,
10285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // because some FTP servers, including FileZilla, require that.
10295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // See http://crbug.com/25316.
10305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV;
10315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
10325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
10335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
10345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_UNEXPECTED);
10355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
10365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We should be sure about our resource type now. Otherwise we risk
10385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // an infinite loop (RETR can later send CWD, and CWD can later send RETR).
10395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_NE(RESOURCE_TYPE_UNKNOWN, resource_type_);
10405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OK;
10425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
10435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// SIZE command
10455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlWriteSIZE() {
10465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string command = "SIZE " + GetRequestPathForFtpCommand(false);
10475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_READ;
10485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return SendFtpCommand(command, command, COMMAND_SIZE);
10495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
10505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::ProcessResponseSIZE(
10525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const FtpCtrlResponse& response) {
10535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (GetErrorClass(response.status_code)) {
10545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INITIATED:
10555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
10565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_OK:
10575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (response.lines.size() != 1)
10585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return Stop(ERR_INVALID_RESPONSE);
10595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int64 size;
10605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!base::StringToInt64(response.lines[0], &size))
10615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return Stop(ERR_INVALID_RESPONSE);
10625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (size < 0)
10635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return Stop(ERR_INVALID_RESPONSE);
10645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // A successful response to SIZE does not mean the resource is a file.
10665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Some FTP servers (for example, the qnx one) send a SIZE even for
10675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // directories.
10685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      response_.expected_content_size = size;
10695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
10705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INFO_NEEDED:
10715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
10725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_TRANSIENT_ERROR:
10735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
10745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_PERMANENT_ERROR:
10755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // It's possible that SIZE failed because the path is a directory.
10765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (resource_type_ == RESOURCE_TYPE_UNKNOWN &&
10775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          response.status_code != 550) {
10785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
10795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
10805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
10815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
10825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
10835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_UNEXPECTED);
10845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
10855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (resource_type_ == RESOURCE_TYPE_FILE)
10875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    next_state_ = STATE_CTRL_WRITE_RETR;
10885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
10895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    next_state_ = STATE_CTRL_WRITE_CWD;
10905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OK;
10925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
10935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// CWD command
10955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlWriteCWD() {
10965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string command = "CWD " + GetRequestPathForFtpCommand(true);
10975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_READ;
10985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return SendFtpCommand(command, command, COMMAND_CWD);
10995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
11005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::ProcessResponseCWD(const FtpCtrlResponse& response) {
11025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We should never issue CWD if we know the target resource is a file.
11035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_NE(RESOURCE_TYPE_FILE, resource_type_);
11045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (GetErrorClass(response.status_code)) {
11065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INITIATED:
11075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_INVALID_RESPONSE);
11085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_OK:
11095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_ = STATE_CTRL_WRITE_LIST;
11105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
11115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INFO_NEEDED:
11125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_INVALID_RESPONSE);
11135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_TRANSIENT_ERROR:
11145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Some FTP servers send response 451 (not a valid CWD response according
11155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // to RFC 959) instead of 550.
11165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (response.status_code == 451)
11175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return ProcessResponseCWDNotADirectory();
11185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
11205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_PERMANENT_ERROR:
11215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (response.status_code == 550)
11225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return ProcessResponseCWDNotADirectory();
11235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
11255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
11265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
11275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_UNEXPECTED);
11285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
11295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OK;
11315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
11325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::ProcessResponseCWDNotADirectory() {
11345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (resource_type_ == RESOURCE_TYPE_DIRECTORY) {
11355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We're assuming that the resource is a directory, but the server
11365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // says it's not true. The most probable interpretation is that it
11375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // doesn't exist (with FTP we can't be sure).
11385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return Stop(ERR_FILE_NOT_FOUND);
11395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
11405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We are here because SIZE failed and we are not sure what the resource
11425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // type is. It could still be file, and SIZE could fail because of
11435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // an access error (http://crbug.com/56734). Try RETR just to be sure.
11445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  resource_type_ = RESOURCE_TYPE_FILE;
11455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_WRITE_RETR;
11465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OK;
11485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
11495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// LIST command
11515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlWriteLIST() {
11525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string command(system_type_ == SYSTEM_TYPE_VMS ? "LIST *.*;0" : "LIST");
11535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_READ;
11545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return SendFtpCommand(command, command, COMMAND_LIST);
11555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
11565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::ProcessResponseLIST(
11585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const FtpCtrlResponse& response) {
11595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (GetErrorClass(response.status_code)) {
11605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INITIATED:
11615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // We want the client to start reading the response at this point.
11625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // It got here either through Start or RestartWithAuth. We want that
11635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // method to complete. Not setting next state here will make DoLoop exit
11645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // and in turn make Start/RestartWithAuth complete.
11655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      response_.is_directory_listing = true;
11665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
11675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_OK:
11685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      response_.is_directory_listing = true;
11695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_ = STATE_CTRL_WRITE_QUIT;
11705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
11715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_INFO_NEEDED:
11725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_INVALID_RESPONSE);
11735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_TRANSIENT_ERROR:
11745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
11755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERROR_CLASS_PERMANENT_ERROR:
11765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
11775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
11785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
11795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return Stop(ERR_UNEXPECTED);
11805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
11815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OK;
11825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
11835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// QUIT command
11855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoCtrlWriteQUIT() {
11865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string command = "QUIT";
11875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_READ;
11885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return SendFtpCommand(command, command, COMMAND_QUIT);
11895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
11905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::ProcessResponseQUIT(
11925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const FtpCtrlResponse& response) {
11935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ctrl_socket_->Disconnect();
11945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return last_error_;
11955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
11965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Data Connection
11985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoDataConnect() {
12005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_DATA_CONNECT_COMPLETE;
12015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  IPEndPoint ip_endpoint;
12025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AddressList data_address;
12035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Connect to the same host as the control socket to prevent PASV port
12045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // scanning attacks.
12055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int rv = ctrl_socket_->GetPeerAddress(&ip_endpoint);
12065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (rv != OK)
12075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return Stop(rv);
12085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  data_address = AddressList::CreateFromIPAddress(
12095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ip_endpoint.address(), data_connection_port_);
12105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  data_socket_.reset(socket_factory_->CreateTransportClientSocket(
12115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        data_address, net_log_.net_log(), net_log_.source()));
12125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  net_log_.AddEvent(
12135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NetLog::TYPE_FTP_DATA_CONNECTION,
12145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      data_socket_->NetLog().source().ToEventParametersCallback());
12155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return data_socket_->Connect(io_callback_);
12165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
12175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoDataConnectComplete(int result) {
12195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (result != OK && use_epsv_) {
12205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // It's possible we hit a broken server, sadly. They can break in different
12215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // ways. Some time out, some reset a connection. Fall back to PASV.
12225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // TODO(phajdan.jr): remember it for future transactions with this server.
12235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // TODO(phajdan.jr): write a test for this code path.
12245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    use_epsv_ = false;
12255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    next_state_ = STATE_CTRL_WRITE_PASV;
12265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return OK;
12275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
12285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Only record the connection error after we've applied all our fallbacks.
12305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We want to capture the final error, one we're not going to recover from.
12315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RecordDataConnectionError(result);
12325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (result != OK)
12345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return Stop(result);
12355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_CTRL_WRITE_SIZE;
12375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OK;
12385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
12395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoDataRead() {
12415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(read_data_buf_);
12425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_GT(read_data_buf_len_, 0);
12435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (data_socket_ == NULL || !data_socket_->IsConnected()) {
12455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If we don't destroy the data socket completely, some servers will wait
12465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // for us (http://crbug.com/21127). The half-closed TCP connection needs
12475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // to be closed on our side too.
12485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    data_socket_.reset();
12495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (ctrl_socket_->IsConnected()) {
12515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Wait for the server's response, we should get it before sending QUIT.
12525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_state_ = STATE_CTRL_READ;
12535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return OK;
12545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
12555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We are no longer connected to the server, so just finish the transaction.
12575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return Stop(OK);
12585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
12595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  next_state_ = STATE_DATA_READ_COMPLETE;
12615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  read_data_buf_->data()[0] = 0;
12625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return data_socket_->Read(read_data_buf_, read_data_buf_len_, io_callback_);
12635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
12645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int FtpNetworkTransaction::DoDataReadComplete(int result) {
12665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return result;
12675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
12685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// We're using a histogram as a group of counters, with one bucket for each
12705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// enumeration value.  We're only interested in the values of the counters.
12715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Ignore the shape, average, and standard deviation of the histograms because
12725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// they are meaningless.
12735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
12745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// We use two histograms.  In the first histogram we tally whether the user has
12755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// seen an error of that type during the session.  In the second histogram we
12765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// tally the total number of times the users sees each errer.
12775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void FtpNetworkTransaction::RecordDataConnectionError(int result) {
12785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Gather data for http://crbug.com/3073. See how many users have trouble
12795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // establishing FTP data connection in passive FTP mode.
12805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  enum {
12815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Data connection successful.
12825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NET_ERROR_OK = 0,
12835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Local firewall blocked the connection.
12855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NET_ERROR_ACCESS_DENIED = 1,
12865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Connection timed out.
12885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NET_ERROR_TIMED_OUT = 2,
12895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Connection has been estabilished, but then got broken (either reset
12915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // or aborted).
12925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NET_ERROR_CONNECTION_BROKEN = 3,
12935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Connection has been refused.
12955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NET_ERROR_CONNECTION_REFUSED = 4,
12965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // No connection to the internet.
12985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NET_ERROR_INTERNET_DISCONNECTED = 5,
12995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Could not reach the destination address.
13015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NET_ERROR_ADDRESS_UNREACHABLE = 6,
13025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // A programming error in our network stack.
13045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NET_ERROR_UNEXPECTED = 7,
13055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Other kind of error.
13075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NET_ERROR_OTHER = 20,
13085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NUM_OF_NET_ERROR_TYPES
13105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } type;
13115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (result) {
13125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case OK:
13135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      type = NET_ERROR_OK;
13145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
13155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERR_ACCESS_DENIED:
13165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERR_NETWORK_ACCESS_DENIED:
13175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      type = NET_ERROR_ACCESS_DENIED;
13185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
13195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERR_TIMED_OUT:
13205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      type = NET_ERROR_TIMED_OUT;
13215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
13225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERR_CONNECTION_ABORTED:
13235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERR_CONNECTION_RESET:
13245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERR_CONNECTION_CLOSED:
13255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      type = NET_ERROR_CONNECTION_BROKEN;
13265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
13275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERR_CONNECTION_FAILED:
13285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERR_CONNECTION_REFUSED:
13295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      type = NET_ERROR_CONNECTION_REFUSED;
13305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
13315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERR_INTERNET_DISCONNECTED:
13325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      type = NET_ERROR_INTERNET_DISCONNECTED;
13335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
13345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERR_ADDRESS_INVALID:
13355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERR_ADDRESS_UNREACHABLE:
13365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      type = NET_ERROR_ADDRESS_UNREACHABLE;
13375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
13385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ERR_UNEXPECTED:
13395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      type = NET_ERROR_UNEXPECTED;
13405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
13415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
13425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      type = NET_ERROR_OTHER;
13435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
13445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
13455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static bool had_error_type[NUM_OF_NET_ERROR_TYPES];
13465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(type >= 0 && type < NUM_OF_NET_ERROR_TYPES);
13485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!had_error_type[type]) {
13495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    had_error_type[type] = true;
13505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorHappened",
13515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        type, NUM_OF_NET_ERROR_TYPES);
13525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
13535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorCount",
13545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      type, NUM_OF_NET_ERROR_TYPES);
13555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
13565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace net
1358