ftp_network_transaction.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
13d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes// Copyright (c) 2012 The Chromium Authors. All rights reserved.
23d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes// Use of this source code is governed by a BSD-style license that can be
33d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes// found in the LICENSE file.
43d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
53d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes#include "net/ftp/ftp_network_transaction.h"
63d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
73d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes#include "base/bind.h"
83d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes#include "base/bind_helpers.h"
93d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes#include "base/compiler_specific.h"
103d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes#include "base/metrics/histogram.h"
113d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes#include "base/string_number_conversions.h"
123d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes#include "base/string_util.h"
133d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes#include "base/strings/string_split.h"
143d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes#include "base/utf_string_conversions.h"
153d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes#include "base/values.h"
163d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes#include "net/base/address_list.h"
173d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes#include "net/base/connection_type_histograms.h"
183d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes#include "net/base/escape.h"
193d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes#include "net/base/net_errors.h"
203d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes#include "net/base/net_log.h"
21318de6070119f7ace92fa542ffb85b0a1c9f9479Douglas Gregor#include "net/base/net_util.h"
223d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes#include "net/ftp/ftp_network_session.h"
233d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes#include "net/ftp/ftp_request_info.h"
243d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes#include "net/ftp/ftp_util.h"
253d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes#include "net/socket/client_socket_factory.h"
263d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes#include "net/socket/stream_socket.h"
27318de6070119f7ace92fa542ffb85b0a1c9f9479Douglas Gregor
283d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentesconst char kCRLF[] = "\r\n";
293d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
303d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentesconst int kCtrlBufLen = 1024;
31318de6070119f7ace92fa542ffb85b0a1c9f9479Douglas Gregor
323d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentesnamespace {
333d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
343d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes// Returns true if |input| can be safely used as a part of FTP command.
353d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentesbool IsValidFTPCommandString(const std::string& input) {
363d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // RFC 959 only allows ASCII strings, but at least Firefox can send non-ASCII
373d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // characters in the command if the request path contains them. To be
383d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // compatible, we do the same and allow non-ASCII characters in a command.
393d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
403d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // Protect agains newline injection attack.
413d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (input.find_first_of("\r\n") != std::string::npos)
423d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    return false;
433d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
443d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  return true;
453d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes}
463d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
473d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentesenum ErrorClass {
483d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // The requested action was initiated. The client should expect another
493d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // reply before issuing the next command.
503d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  ERROR_CLASS_INITIATED,
513d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
523d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // The requested action has been successfully completed.
533d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  ERROR_CLASS_OK,
543d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
553d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // The command has been accepted, but to complete the operation, more
563d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // information must be sent by the client.
573d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  ERROR_CLASS_INFO_NEEDED,
583d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
593d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // The command was not accepted and the requested action did not take place.
603d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // This condition is temporary, and the client is encouraged to restart the
613d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // command sequence.
623d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  ERROR_CLASS_TRANSIENT_ERROR,
633d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
64de98db33fb10a13ead2fa56d6d4c944cedb8fbadOscar Fuentes  // The command was not accepted and the requested action did not take place.
653d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // This condition is rather permanent, and the client is discouraged from
663d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // repeating the exact request.
673d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  ERROR_CLASS_PERMANENT_ERROR,
683d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes};
693d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
703d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes// Returns the error class for given response code. Caller should ensure
713d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes// that |response_code| is in range 100-599.
723d01fc7de86c75926e4e5ac7cc49f0116018893dOscar FuentesErrorClass GetErrorClass(int response_code) {
733d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (response_code >= 100 && response_code <= 199)
743d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    return ERROR_CLASS_INITIATED;
753d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
763d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (response_code >= 200 && response_code <= 299)
773d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    return ERROR_CLASS_OK;
783d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
793d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (response_code >= 300 && response_code <= 399)
803d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    return ERROR_CLASS_INFO_NEEDED;
813d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
823d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (response_code >= 400 && response_code <= 499)
833d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    return ERROR_CLASS_TRANSIENT_ERROR;
843d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
853d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (response_code >= 500 && response_code <= 599)
863d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    return ERROR_CLASS_PERMANENT_ERROR;
873d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
883d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // We should not be called on invalid error codes.
893d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  NOTREACHED() << response_code;
903d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  return ERROR_CLASS_PERMANENT_ERROR;
913d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes}
923d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
933d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes// Returns network error code for received FTP |response_code|.
943d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentesint GetNetErrorCodeForFtpResponseCode(int response_code) {
953d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  switch (response_code) {
963d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    case 421:
973d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      return net::ERR_FTP_SERVICE_UNAVAILABLE;
983d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    case 426:
993d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      return net::ERR_FTP_TRANSFER_ABORTED;
1003d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    case 450:
1013d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      return net::ERR_FTP_FILE_BUSY;
1023d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    case 500:
1033d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    case 501:
1043d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      return net::ERR_FTP_SYNTAX_ERROR;
1053d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    case 502:
1063d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    case 504:
1073d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      return net::ERR_FTP_COMMAND_NOT_SUPPORTED;
1083d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    case 503:
1093d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      return net::ERR_FTP_BAD_COMMAND_SEQUENCE;
1103d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    default:
1113d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      return net::ERR_FTP_FAILED;
1123d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  }
1133d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes}
1143d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
1153d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes// From RFC 2428 Section 3:
1163d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes//   The text returned in response to the EPSV command MUST be:
1173d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes//     <some text> (<d><d><d><tcp-port><d>)
1183d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes//   <d> is a delimiter character, ideally to be |
119de98db33fb10a13ead2fa56d6d4c944cedb8fbadOscar Fuentesbool ExtractPortFromEPSVResponse(const net::FtpCtrlResponse& response,
1203d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes                                 int* port) {
1213d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (response.lines.size() != 1)
1223d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    return false;
1233d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  const char* ptr = response.lines[0].c_str();
1243d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  while (*ptr && *ptr != '(')
1253d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    ++ptr;
1263d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (!*ptr)
1273d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    return false;
1283d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  char sep = *(++ptr);
1293d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (!sep || isdigit(sep) || *(++ptr) != sep || *(++ptr) != sep)
1303d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    return false;
1313d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (!isdigit(*(++ptr)))
1323d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    return false;
1333d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  *port = *ptr - '0';
1343d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  while (isdigit(*(++ptr))) {
1353d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    *port *= 10;
1363d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    *port += *ptr - '0';
1373d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  }
1383d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (*ptr != sep)
1393d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    return false;
1403d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
1413d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  return true;
1423d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes}
1433d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
1443d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes// There are two way we can receive IP address and port.
1453d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes// (127,0,0,1,23,21) IP address and port encapsulated in ().
1463d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes// 127,0,0,1,23,21  IP address and port without ().
1473d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes//
1483d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes// See RFC 959, Section 4.1.2
1493d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentesbool ExtractPortFromPASVResponse(const net::FtpCtrlResponse& response,
1503d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes                                 int* port) {
1513d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (response.lines.size() != 1)
1523d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    return false;
1533d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
1543d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  std::string line(response.lines[0]);
1553d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (!IsStringASCII(line))
1563d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    return false;
1573d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (line.length() < 2)
1583d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    return false;
1593d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
1603d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  size_t paren_pos = line.find('(');
1613d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (paren_pos == std::string::npos) {
1623d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    // Find the first comma and use it to locate the beginning
1633d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    // of the response data.
1643d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    size_t comma_pos = line.find(',');
1653d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    if (comma_pos == std::string::npos)
1663d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      return false;
16701746745f1287effa1772ef51b973988afcea699Douglas Gregor
1683d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    size_t space_pos = line.rfind(' ', comma_pos);
1693d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    if (space_pos != std::string::npos)
1703d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      line = line.substr(space_pos + 1);
1713d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  } else {
1723d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    // Remove the parentheses and use the text inside them.
1733d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    size_t closing_paren_pos = line.rfind(')');
1743d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    if (closing_paren_pos == std::string::npos)
1753d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      return false;
1763d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    if (closing_paren_pos <= paren_pos)
1773d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      return false;
1783d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
1793d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    line = line.substr(paren_pos + 1, closing_paren_pos - paren_pos - 1);
1803d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  }
1813d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
1823d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // Split the line into comma-separated pieces and extract
1833d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // the last two.
1843d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  std::vector<std::string> pieces;
1853d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  base::SplitString(line, ',', &pieces);
1863d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (pieces.size() != 6)
1873d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    return false;
1883d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
1893d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // Ignore the IP address supplied in the response. We are always going
1903d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // to connect back to the same server to prevent FTP PASV port scanning.
1913d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  int p0, p1;
1923d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (!base::StringToInt(pieces[4], &p0))
1933d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    return false;
1943d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (!base::StringToInt(pieces[5], &p1))
1953d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    return false;
1963d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  *port = (p0 << 8) + p1;
197d413c01088ff0bf9ab1d856f75ef2ba84427fd6dOscar Fuentes
1983d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  return true;
1993d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes}
2003d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
2013d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes}  // namespace
2023d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
2033d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentesnamespace net {
2043d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
2053d01fc7de86c75926e4e5ac7cc49f0116018893dOscar FuentesFtpNetworkTransaction::FtpNetworkTransaction(
2063d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    FtpNetworkSession* session,
2073d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    ClientSocketFactory* socket_factory)
2083d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    : command_sent_(COMMAND_NONE),
2093d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      ALLOW_THIS_IN_INITIALIZER_LIST(
2103d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes          io_callback_(base::Bind(&FtpNetworkTransaction::OnIOComplete,
2113d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes                                  base::Unretained(this)))),
2123d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      session_(session),
2133d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      request_(NULL),
2143d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      resolver_(session->host_resolver()),
2153d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      read_ctrl_buf_(new IOBuffer(kCtrlBufLen)),
2163d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      ctrl_response_buffer_(NULL),
2173d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      read_data_buf_len_(0),
2183d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      last_error_(OK),
2193d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      system_type_(SYSTEM_TYPE_UNKNOWN),
2203d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      // Use image (binary) transfer by default. It should always work,
2213d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      // whereas the ascii transfer may damage binary data.
2223d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      data_type_(DATA_TYPE_IMAGE),
2233d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      resource_type_(RESOURCE_TYPE_UNKNOWN),
2243d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      use_epsv_(true),
2253d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      data_connection_port_(0),
2263d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      socket_factory_(socket_factory),
2273d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      next_state_(STATE_NONE),
2283d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      state_after_data_connect_complete_(STATE_CTRL_WRITE_SIZE) {
2293d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes}
2303d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
231ef23d711bc4ff72799693f40c15b29cfdabd89a0Douglas GregorFtpNetworkTransaction::~FtpNetworkTransaction() {
2323d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes}
2333d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
2343d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentesint FtpNetworkTransaction::Stop(int error) {
2353d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (command_sent_ == COMMAND_QUIT)
2363d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    return error;
2373d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
2383d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  next_state_ = STATE_CTRL_WRITE_QUIT;
2393d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  last_error_ = error;
2403d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  return OK;
2413d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes}
2423d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
2433d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentesint FtpNetworkTransaction::RestartIgnoringLastError(
2443d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    const CompletionCallback& callback) {
2453d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  return ERR_NOT_IMPLEMENTED;
2463d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes}
2473d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
2483d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentesint FtpNetworkTransaction::Start(const FtpRequestInfo* request_info,
2493d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes                                 const CompletionCallback& callback,
2503d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes                                 const BoundNetLog& net_log) {
2513d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  net_log_ = net_log;
2523d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  request_ = request_info;
2533d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
2543d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  ctrl_response_buffer_.reset(new FtpCtrlResponseBuffer(net_log_));
2553d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
2563d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (request_->url.has_username()) {
2573d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    string16 username;
2583d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    string16 password;
2593d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    GetIdentityFromURL(request_->url, &username, &password);
2603d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    credentials_.Set(username, password);
2613d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  } else {
2623d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    credentials_.Set(ASCIIToUTF16("anonymous"),
2633d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes                     ASCIIToUTF16("chrome@example.com"));
2643d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  }
2653d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
2663d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  DetectTypecode();
2673d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
2683d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  next_state_ = STATE_CTRL_RESOLVE_HOST;
2693d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  int rv = DoLoop(OK);
2703d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (rv == ERR_IO_PENDING)
2713d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    user_callback_ = callback;
2723d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  return rv;
2733d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes}
2743d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
2753d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentesint FtpNetworkTransaction::RestartWithAuth(const AuthCredentials& credentials,
2763d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes                                           const CompletionCallback& callback) {
2773d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  ResetStateForRestart();
2783d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
279ec8b2a95303ef51acdf5b034ab8f4371665fc136Oscar Fuentes  credentials_ = credentials;
280ec8b2a95303ef51acdf5b034ab8f4371665fc136Oscar Fuentes
281ec8b2a95303ef51acdf5b034ab8f4371665fc136Oscar Fuentes  next_state_ = STATE_CTRL_RESOLVE_HOST;
2823d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  int rv = DoLoop(OK);
2833d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (rv == ERR_IO_PENDING)
2843d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    user_callback_ = callback;
2853d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  return rv;
2863d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes}
2873d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
2883d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentesint FtpNetworkTransaction::Read(IOBuffer* buf,
2893d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes                                int buf_len,
2903d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes                                const CompletionCallback& callback) {
2913d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  DCHECK(buf);
2923d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  DCHECK_GT(buf_len, 0);
2933d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
2943d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  read_data_buf_ = buf;
2953d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  read_data_buf_len_ = buf_len;
2963d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
2973d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  next_state_ = STATE_DATA_READ;
2983d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  int rv = DoLoop(OK);
2993d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (rv == ERR_IO_PENDING)
3003d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    user_callback_ = callback;
3013d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  return rv;
3023d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes}
3033d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
3043d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentesconst FtpResponseInfo* FtpNetworkTransaction::GetResponseInfo() const {
3053d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  return &response_;
3063d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes}
3073d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
3083d01fc7de86c75926e4e5ac7cc49f0116018893dOscar FuentesLoadState FtpNetworkTransaction::GetLoadState() const {
3093d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (next_state_ == STATE_CTRL_RESOLVE_HOST_COMPLETE)
3103d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    return LOAD_STATE_RESOLVING_HOST;
3113d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
3123d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (next_state_ == STATE_CTRL_CONNECT_COMPLETE ||
3133d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      next_state_ == STATE_DATA_CONNECT_COMPLETE)
3143d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    return LOAD_STATE_CONNECTING;
3153d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
3163d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (next_state_ == STATE_DATA_READ_COMPLETE)
3173d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    return LOAD_STATE_READING_RESPONSE;
3183d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
3193d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (command_sent_ == COMMAND_RETR && read_data_buf_.get())
3203d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    return LOAD_STATE_READING_RESPONSE;
3213d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
3223d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (command_sent_ == COMMAND_QUIT)
3233d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    return LOAD_STATE_IDLE;
3243d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
3253d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (command_sent_ != COMMAND_NONE)
3263d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    return LOAD_STATE_SENDING_REQUEST;
3273d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
3283d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  return LOAD_STATE_IDLE;
3293d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes}
3303d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
3313d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentesuint64 FtpNetworkTransaction::GetUploadProgress() const {
3323d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  return 0;
3333d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes}
3343d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
3353d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentesvoid FtpNetworkTransaction::ResetStateForRestart() {
3363d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  command_sent_ = COMMAND_NONE;
3373d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  user_callback_.Reset();
3383d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  response_ = FtpResponseInfo();
3393d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  read_ctrl_buf_ = new IOBuffer(kCtrlBufLen);
3403d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  ctrl_response_buffer_.reset(new FtpCtrlResponseBuffer(net_log_));
3413d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  read_data_buf_ = NULL;
3423d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  read_data_buf_len_ = 0;
3433d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (write_buf_)
3443d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    write_buf_->SetOffset(0);
3453d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  last_error_ = OK;
3463d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  data_connection_port_ = 0;
3473d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  ctrl_socket_.reset();
3483d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  data_socket_.reset();
3493d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  next_state_ = STATE_NONE;
3503d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  state_after_data_connect_complete_ = STATE_CTRL_WRITE_SIZE;
3513d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes}
3523d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
3533d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentesvoid FtpNetworkTransaction::ResetDataConnectionAfterError(State next_state) {
3543d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // The server _might_ have reset the data connection
3553d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // (see RFC 959 3.2. ESTABLISHING DATA CONNECTIONS:
3563d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // "The server MUST close the data connection under the following
3573d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // conditions:
3583d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // ...
3593d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // 5. An irrecoverable error condition occurs.")
3603d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  //
3613d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // It is ambiguous what an irrecoverable error condition is,
3623d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // so we take no chances.
3633d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  state_after_data_connect_complete_ = next_state;
3643d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV;
3653d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes}
3663d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
3673d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentesvoid FtpNetworkTransaction::DoCallback(int rv) {
3683d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  DCHECK(rv != ERR_IO_PENDING);
3693d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  DCHECK(!user_callback_.is_null());
370de98db33fb10a13ead2fa56d6d4c944cedb8fbadOscar Fuentes
3713d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // Since Run may result in Read being called, clear callback_ up front.
3723d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  CompletionCallback c = user_callback_;
3733d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  user_callback_.Reset();
3743d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  c.Run(rv);
3753d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes}
376de98db33fb10a13ead2fa56d6d4c944cedb8fbadOscar Fuentes
3773d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentesvoid FtpNetworkTransaction::OnIOComplete(int result) {
3783d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  int rv = DoLoop(result);
3793d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (rv != ERR_IO_PENDING)
3803d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    DoCallback(rv);
3813d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes}
3823d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
3833d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentesint FtpNetworkTransaction::ProcessCtrlResponse() {
3843d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  FtpCtrlResponse response = ctrl_response_buffer_->PopResponse();
3853d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
3863d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  int rv = OK;
3873d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  switch (command_sent_) {
3883d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    case COMMAND_NONE:
3893d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      // TODO(phajdan.jr): Check for errors in the welcome message.
3903d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      next_state_ = STATE_CTRL_WRITE_USER;
39101746745f1287effa1772ef51b973988afcea699Douglas Gregor      break;
39201746745f1287effa1772ef51b973988afcea699Douglas Gregor    case COMMAND_USER:
39301746745f1287effa1772ef51b973988afcea699Douglas Gregor      rv = ProcessResponseUSER(response);
3943d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      break;
3953d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    case COMMAND_PASS:
3963d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      rv = ProcessResponsePASS(response);
3973d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      break;
3983d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    case COMMAND_SYST:
3993d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      rv = ProcessResponseSYST(response);
4003d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      break;
4013d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    case COMMAND_PWD:
4023d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      rv = ProcessResponsePWD(response);
4033d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      break;
4043d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    case COMMAND_TYPE:
4053d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      rv = ProcessResponseTYPE(response);
4063d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      break;
4073d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    case COMMAND_EPSV:
4083d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      rv = ProcessResponseEPSV(response);
4093d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      break;
4103d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    case COMMAND_PASV:
4113d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      rv = ProcessResponsePASV(response);
4123d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      break;
4133d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    case COMMAND_SIZE:
4143d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      rv = ProcessResponseSIZE(response);
4153d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      break;
4163d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    case COMMAND_RETR:
417318de6070119f7ace92fa542ffb85b0a1c9f9479Douglas Gregor      rv = ProcessResponseRETR(response);
4183d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      break;
4193d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    case COMMAND_CWD:
4203d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      rv = ProcessResponseCWD(response);
4213d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      break;
422071d73d67e7cea60e7334f6ae96c1e8f8050a662Douglas Gregor    case COMMAND_LIST:
423071d73d67e7cea60e7334f6ae96c1e8f8050a662Douglas Gregor      rv = ProcessResponseLIST(response);
424071d73d67e7cea60e7334f6ae96c1e8f8050a662Douglas Gregor      break;
4253d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    case COMMAND_QUIT:
4263d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      rv = ProcessResponseQUIT(response);
4273d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      break;
4283d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    default:
4293d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      LOG(DFATAL) << "Unexpected value of command_sent_: " << command_sent_;
4303d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      return ERR_UNEXPECTED;
4313d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  }
4323d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
4333d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // We may get multiple responses for some commands,
4343d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // see http://crbug.com/18036.
435de98db33fb10a13ead2fa56d6d4c944cedb8fbadOscar Fuentes  while (ctrl_response_buffer_->ResponseAvailable() && rv == OK) {
4363d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    response = ctrl_response_buffer_->PopResponse();
4373d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
4383d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    switch (command_sent_) {
4393d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      case COMMAND_RETR:
4403d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes        rv = ProcessResponseRETR(response);
4413d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes        break;
4423d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      case COMMAND_LIST:
4433d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes        rv = ProcessResponseLIST(response);
4443d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes        break;
4453d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      default:
4463d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes        // Multiple responses for other commands are invalid.
4473d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes        return Stop(ERR_INVALID_RESPONSE);
4483d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    }
449579e43a44ac118d432cb053470dbb977dfffe9dbOscar Fuentes  }
450579e43a44ac118d432cb053470dbb977dfffe9dbOscar Fuentes
451579e43a44ac118d432cb053470dbb977dfffe9dbOscar Fuentes  return rv;
4523d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes}
4533d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
4543d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes// Used to prepare and send FTP command.
4553d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentesint FtpNetworkTransaction::SendFtpCommand(const std::string& command,
4563d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes                                          const std::string& command_for_log,
4573d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes                                          Command cmd) {
4583d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // If we send a new command when we still have unprocessed responses
4593d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // for previous commands, the response receiving code will have no way to know
4603d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // which responses are for which command.
4613d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  DCHECK(!ctrl_response_buffer_->ResponseAvailable());
4623d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
4633d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  DCHECK(!write_command_buf_);
4643d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  DCHECK(!write_buf_);
4653d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
4663d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (!IsValidFTPCommandString(command)) {
4673d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    // Callers should validate the command themselves and return a more specific
4683d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    // error code.
4693d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    NOTREACHED();
4703d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    return Stop(ERR_UNEXPECTED);
4713d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  }
4723d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
4733d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  command_sent_ = cmd;
4743d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
4753d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  write_command_buf_ = new IOBufferWithSize(command.length() + 2);
4763d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  write_buf_ = new DrainableIOBuffer(write_command_buf_,
4773d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes                                     write_command_buf_->size());
4783d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  memcpy(write_command_buf_->data(), command.data(), command.length());
4793d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  memcpy(write_command_buf_->data() + command.length(), kCRLF, 2);
4803d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
4813d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  net_log_.AddEvent(NetLog::TYPE_FTP_COMMAND_SENT,
4823d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes                    NetLog::StringCallback("command", &command_for_log));
4833d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
4843d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  next_state_ = STATE_CTRL_WRITE;
4853d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  return OK;
4863d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes}
4873d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
4883d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentesstd::string FtpNetworkTransaction::GetRequestPathForFtpCommand(
489de98db33fb10a13ead2fa56d6d4c944cedb8fbadOscar Fuentes    bool is_directory) const {
4903d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  std::string path(current_remote_directory_);
4913d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (request_->url.has_path()) {
4923d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    std::string gurl_path(request_->url.path());
4933d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
4943d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    // Get rid of the typecode, see RFC 1738 section 3.2.2. FTP url-path.
4953d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    std::string::size_type pos = gurl_path.rfind(';');
4963d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    if (pos != std::string::npos)
497de98db33fb10a13ead2fa56d6d4c944cedb8fbadOscar Fuentes      gurl_path.resize(pos);
4983d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
4993d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    path.append(gurl_path);
5003d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  }
5013d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // Make sure that if the path is expected to be a file, it won't end
5023d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // with a trailing slash.
503de98db33fb10a13ead2fa56d6d4c944cedb8fbadOscar Fuentes  if (!is_directory && path.length() > 1 && path[path.length() - 1] == '/')
5043d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    path.erase(path.length() - 1);
5053d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  UnescapeRule::Type unescape_rules = UnescapeRule::SPACES |
5063d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes                                      UnescapeRule::URL_SPECIAL_CHARS;
5073d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // This may unescape to non-ASCII characters, but we allow that. See the
5083d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // comment for IsValidFTPCommandString.
5093d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  path = net::UnescapeURLComponent(path, unescape_rules);
5103d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
5113d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (system_type_ == SYSTEM_TYPE_VMS) {
5123d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    if (is_directory)
5133d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      path = FtpUtil::UnixDirectoryPathToVMS(path);
5143d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    else
5153d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      path = FtpUtil::UnixFilePathToVMS(path);
5163d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  }
5173d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
5183d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  DCHECK(IsValidFTPCommandString(path));
5198c46e8573e2c8b3e1f048c68c61e2fc452703234Chris Lattner  return path;
5203d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes}
5213d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
5223d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentesvoid FtpNetworkTransaction::DetectTypecode() {
5233d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (!request_->url.has_path())
5243d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    return;
5253d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  std::string gurl_path(request_->url.path());
5263d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
5273d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  // Extract the typecode, see RFC 1738 section 3.2.2. FTP url-path.
5283d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  std::string::size_type pos = gurl_path.rfind(';');
5293d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (pos == std::string::npos)
5303d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    return;
5313d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  std::string typecode_string(gurl_path.substr(pos));
5323d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  if (typecode_string == ";type=a") {
5333d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    data_type_ = DATA_TYPE_ASCII;
5343d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    resource_type_ = RESOURCE_TYPE_FILE;
5353d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  } else if (typecode_string == ";type=i") {
5363d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    data_type_ = DATA_TYPE_IMAGE;
5373d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    resource_type_ = RESOURCE_TYPE_FILE;
5383d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  } else if (typecode_string == ";type=d") {
5393d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    resource_type_ = RESOURCE_TYPE_DIRECTORY;
5403d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  }
5413d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes}
5423d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
5433d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentesint FtpNetworkTransaction::DoLoop(int result) {
5443d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  DCHECK(next_state_ != STATE_NONE);
5453d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes
5463d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  int rv = result;
5473d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes  do {
5483d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    State state = next_state_;
5493d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    next_state_ = STATE_NONE;
5503d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes    switch (state) {
5513d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      case STATE_CTRL_RESOLVE_HOST:
5523d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes        DCHECK(rv == OK);
5533d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes        rv = DoCtrlResolveHost();
5543d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes        break;
5553d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      case STATE_CTRL_RESOLVE_HOST_COMPLETE:
5563d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes        rv = DoCtrlResolveHostComplete(rv);
5573d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes        break;
5583d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      case STATE_CTRL_CONNECT:
5593d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes        DCHECK(rv == OK);
5603d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes        rv = DoCtrlConnect();
561de98db33fb10a13ead2fa56d6d4c944cedb8fbadOscar Fuentes        break;
5623d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      case STATE_CTRL_CONNECT_COMPLETE:
56307b9d69ad1d491635dddab2ed59940c6efb72f22Cedric Venet        rv = DoCtrlConnectComplete(rv);
56407b9d69ad1d491635dddab2ed59940c6efb72f22Cedric Venet        break;
56507b9d69ad1d491635dddab2ed59940c6efb72f22Cedric Venet      case STATE_CTRL_READ:
5663d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes        DCHECK(rv == OK);
5673d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes        rv = DoCtrlRead();
5683d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes        break;
5693d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes      case STATE_CTRL_READ_COMPLETE:
5703d01fc7de86c75926e4e5ac7cc49f0116018893dOscar Fuentes        rv = DoCtrlReadComplete(rv);
571de98db33fb10a13ead2fa56d6d4c944cedb8fbadOscar Fuentes        break;
572de98db33fb10a13ead2fa56d6d4c944cedb8fbadOscar Fuentes      case STATE_CTRL_WRITE:
573de98db33fb10a13ead2fa56d6d4c944cedb8fbadOscar Fuentes        DCHECK(rv == OK);
574de98db33fb10a13ead2fa56d6d4c944cedb8fbadOscar Fuentes        rv = DoCtrlWrite();
575de98db33fb10a13ead2fa56d6d4c944cedb8fbadOscar Fuentes        break;
576de98db33fb10a13ead2fa56d6d4c944cedb8fbadOscar Fuentes      case STATE_CTRL_WRITE_COMPLETE:
577de98db33fb10a13ead2fa56d6d4c944cedb8fbadOscar Fuentes        rv = DoCtrlWriteComplete(rv);
578de98db33fb10a13ead2fa56d6d4c944cedb8fbadOscar Fuentes        break;
579de98db33fb10a13ead2fa56d6d4c944cedb8fbadOscar Fuentes      case STATE_CTRL_WRITE_USER:
580de98db33fb10a13ead2fa56d6d4c944cedb8fbadOscar Fuentes        DCHECK(rv == OK);
581de98db33fb10a13ead2fa56d6d4c944cedb8fbadOscar Fuentes        rv = DoCtrlWriteUSER();
582de98db33fb10a13ead2fa56d6d4c944cedb8fbadOscar Fuentes        break;
583      case STATE_CTRL_WRITE_PASS:
584        DCHECK(rv == OK);
585        rv = DoCtrlWritePASS();
586        break;
587      case STATE_CTRL_WRITE_SYST:
588        DCHECK(rv == OK);
589        rv = DoCtrlWriteSYST();
590        break;
591      case STATE_CTRL_WRITE_PWD:
592        DCHECK(rv == OK);
593        rv = DoCtrlWritePWD();
594        break;
595      case STATE_CTRL_WRITE_TYPE:
596        DCHECK(rv == OK);
597        rv = DoCtrlWriteTYPE();
598        break;
599      case STATE_CTRL_WRITE_EPSV:
600        DCHECK(rv == OK);
601        rv = DoCtrlWriteEPSV();
602        break;
603      case STATE_CTRL_WRITE_PASV:
604        DCHECK(rv == OK);
605        rv = DoCtrlWritePASV();
606        break;
607      case STATE_CTRL_WRITE_RETR:
608        DCHECK(rv == OK);
609        rv = DoCtrlWriteRETR();
610        break;
611      case STATE_CTRL_WRITE_SIZE:
612        DCHECK(rv == OK);
613        rv = DoCtrlWriteSIZE();
614        break;
615      case STATE_CTRL_WRITE_CWD:
616        DCHECK(rv == OK);
617        rv = DoCtrlWriteCWD();
618        break;
619      case STATE_CTRL_WRITE_LIST:
620        DCHECK(rv == OK);
621        rv = DoCtrlWriteLIST();
622        break;
623      case STATE_CTRL_WRITE_QUIT:
624        DCHECK(rv == OK);
625        rv = DoCtrlWriteQUIT();
626        break;
627      case STATE_DATA_CONNECT:
628        DCHECK(rv == OK);
629        rv = DoDataConnect();
630        break;
631      case STATE_DATA_CONNECT_COMPLETE:
632        rv = DoDataConnectComplete(rv);
633        break;
634      case STATE_DATA_READ:
635        DCHECK(rv == OK);
636        rv = DoDataRead();
637        break;
638      case STATE_DATA_READ_COMPLETE:
639        rv = DoDataReadComplete(rv);
640        break;
641      default:
642        NOTREACHED() << "bad state";
643        rv = ERR_UNEXPECTED;
644        break;
645    }
646  } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
647  return rv;
648}
649
650int FtpNetworkTransaction::DoCtrlResolveHost() {
651  next_state_ = STATE_CTRL_RESOLVE_HOST_COMPLETE;
652
653  HostResolver::RequestInfo info(HostPortPair::FromURL(request_->url));
654  // No known referrer.
655  return resolver_.Resolve(
656      info, &addresses_,
657      base::Bind(&FtpNetworkTransaction::OnIOComplete, base::Unretained(this)),
658      net_log_);
659}
660
661int FtpNetworkTransaction::DoCtrlResolveHostComplete(int result) {
662  if (result == OK)
663    next_state_ = STATE_CTRL_CONNECT;
664  return result;
665}
666
667int FtpNetworkTransaction::DoCtrlConnect() {
668  next_state_ = STATE_CTRL_CONNECT_COMPLETE;
669  ctrl_socket_.reset(socket_factory_->CreateTransportClientSocket(
670        addresses_, net_log_.net_log(), net_log_.source()));
671  net_log_.AddEvent(
672      NetLog::TYPE_FTP_CONTROL_CONNECTION,
673      ctrl_socket_->NetLog().source().ToEventParametersCallback());
674  return ctrl_socket_->Connect(io_callback_);
675}
676
677int FtpNetworkTransaction::DoCtrlConnectComplete(int result) {
678  if (result == OK) {
679    // Put the peer's IP address and port into the response.
680    IPEndPoint ip_endpoint;
681    result = ctrl_socket_->GetPeerAddress(&ip_endpoint);
682    if (result == OK) {
683      response_.socket_address = HostPortPair::FromIPEndPoint(ip_endpoint);
684      next_state_ = STATE_CTRL_READ;
685
686      if (ip_endpoint.GetFamily() == ADDRESS_FAMILY_IPV4) {
687        // Do not use EPSV for IPv4 connections. Some servers become confused
688        // and we time out while waiting to connect. PASV is perfectly fine for
689        // IPv4. Note that this blacklists IPv4 not to use EPSV instead of
690        // whitelisting IPv6 to use it, to make the code more future-proof:
691        // all future protocols should just use EPSV.
692        use_epsv_ = false;
693      }
694    }
695  }
696  return result;
697}
698
699int FtpNetworkTransaction::DoCtrlRead() {
700  next_state_ = STATE_CTRL_READ_COMPLETE;
701  return ctrl_socket_->Read(read_ctrl_buf_, kCtrlBufLen, io_callback_);
702}
703
704int FtpNetworkTransaction::DoCtrlReadComplete(int result) {
705  if (result == 0) {
706    // Some servers (for example Pure-FTPd) apparently close the control
707    // connection when anonymous login is not permitted. For more details
708    // see http://crbug.com/25023.
709    if (command_sent_ == COMMAND_USER &&
710        credentials_.username() == ASCIIToUTF16("anonymous")) {
711      response_.needs_auth = true;
712    }
713    return Stop(ERR_EMPTY_RESPONSE);
714  }
715  if (result < 0)
716    return Stop(result);
717
718  ctrl_response_buffer_->ConsumeData(read_ctrl_buf_->data(), result);
719
720  if (!ctrl_response_buffer_->ResponseAvailable()) {
721    // Read more data from the control socket.
722    next_state_ = STATE_CTRL_READ;
723    return OK;
724  }
725
726  return ProcessCtrlResponse();
727}
728
729int FtpNetworkTransaction::DoCtrlWrite() {
730  next_state_ = STATE_CTRL_WRITE_COMPLETE;
731
732  return ctrl_socket_->Write(write_buf_,
733                             write_buf_->BytesRemaining(),
734                             io_callback_);
735}
736
737int FtpNetworkTransaction::DoCtrlWriteComplete(int result) {
738  if (result < 0)
739    return result;
740
741  write_buf_->DidConsume(result);
742  if (write_buf_->BytesRemaining() == 0) {
743    // Clear the write buffer.
744    write_buf_ = NULL;
745    write_command_buf_ = NULL;
746
747    next_state_ = STATE_CTRL_READ;
748  } else {
749    next_state_ = STATE_CTRL_WRITE;
750  }
751  return OK;
752}
753
754// FTP Commands and responses
755
756// USER Command.
757int FtpNetworkTransaction::DoCtrlWriteUSER() {
758  std::string command = "USER " + UTF16ToUTF8(credentials_.username());
759
760  if (!IsValidFTPCommandString(command))
761    return Stop(ERR_MALFORMED_IDENTITY);
762
763  next_state_ = STATE_CTRL_READ;
764  return SendFtpCommand(command, "USER ***", COMMAND_USER);
765}
766
767int FtpNetworkTransaction::ProcessResponseUSER(
768    const FtpCtrlResponse& response) {
769  switch (GetErrorClass(response.status_code)) {
770    case ERROR_CLASS_OK:
771      next_state_ = STATE_CTRL_WRITE_SYST;
772      break;
773    case ERROR_CLASS_INFO_NEEDED:
774      next_state_ = STATE_CTRL_WRITE_PASS;
775      break;
776    case ERROR_CLASS_TRANSIENT_ERROR:
777    case ERROR_CLASS_PERMANENT_ERROR:
778      response_.needs_auth = true;
779      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
780    default:
781      NOTREACHED();
782      return Stop(ERR_UNEXPECTED);
783  }
784  return OK;
785}
786
787// PASS command.
788int FtpNetworkTransaction::DoCtrlWritePASS() {
789  std::string command = "PASS " + UTF16ToUTF8(credentials_.password());
790
791  if (!IsValidFTPCommandString(command))
792    return Stop(ERR_MALFORMED_IDENTITY);
793
794  next_state_ = STATE_CTRL_READ;
795  return SendFtpCommand(command, "PASS ***", COMMAND_PASS);
796}
797
798int FtpNetworkTransaction::ProcessResponsePASS(
799    const FtpCtrlResponse& response) {
800  switch (GetErrorClass(response.status_code)) {
801    case ERROR_CLASS_OK:
802      next_state_ = STATE_CTRL_WRITE_SYST;
803      break;
804    case ERROR_CLASS_INFO_NEEDED:
805      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
806    case ERROR_CLASS_TRANSIENT_ERROR:
807    case ERROR_CLASS_PERMANENT_ERROR:
808      response_.needs_auth = true;
809      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
810    default:
811      NOTREACHED();
812      return Stop(ERR_UNEXPECTED);
813  }
814  return OK;
815}
816
817// SYST command.
818int FtpNetworkTransaction::DoCtrlWriteSYST() {
819  std::string command = "SYST";
820  next_state_ = STATE_CTRL_READ;
821  return SendFtpCommand(command, command, COMMAND_SYST);
822}
823
824int FtpNetworkTransaction::ProcessResponseSYST(
825    const FtpCtrlResponse& response) {
826  switch (GetErrorClass(response.status_code)) {
827    case ERROR_CLASS_INITIATED:
828      return Stop(ERR_INVALID_RESPONSE);
829    case ERROR_CLASS_OK: {
830      // All important info should be on the first line.
831      std::string line = response.lines[0];
832      // The response should be ASCII, which allows us to do case-insensitive
833      // comparisons easily. If it is not ASCII, we leave the system type
834      // as unknown.
835      if (IsStringASCII(line)) {
836        line = StringToLowerASCII(line);
837
838        // Remove all whitespace, to correctly handle cases like fancy "V M S"
839        // response instead of "VMS".
840        RemoveChars(line, kWhitespaceASCII, &line);
841
842        // The "magic" strings we test for below have been gathered by an
843        // empirical study. VMS needs to come first because some VMS systems
844        // also respond with "UNIX emulation", which is not perfect. It is much
845        // more reliable to talk to these servers in their native language.
846        if (line.find("vms") != std::string::npos) {
847          system_type_ = SYSTEM_TYPE_VMS;
848        } else if (line.find("l8") != std::string::npos ||
849                   line.find("unix") != std::string::npos ||
850                   line.find("bsd") != std::string::npos) {
851          system_type_ = SYSTEM_TYPE_UNIX;
852        } else if (line.find("win32") != std::string::npos ||
853                   line.find("windows") != std::string::npos) {
854          system_type_ = SYSTEM_TYPE_WINDOWS;
855        } else if (line.find("os/2") != std::string::npos) {
856          system_type_ = SYSTEM_TYPE_OS2;
857        }
858      }
859      next_state_ = STATE_CTRL_WRITE_PWD;
860      break;
861    }
862    case ERROR_CLASS_INFO_NEEDED:
863      return Stop(ERR_INVALID_RESPONSE);
864    case ERROR_CLASS_TRANSIENT_ERROR:
865      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
866    case ERROR_CLASS_PERMANENT_ERROR:
867      // Server does not recognize the SYST command so proceed.
868      next_state_ = STATE_CTRL_WRITE_PWD;
869      break;
870    default:
871      NOTREACHED();
872      return Stop(ERR_UNEXPECTED);
873  }
874  return OK;
875}
876
877// PWD command.
878int FtpNetworkTransaction::DoCtrlWritePWD() {
879  std::string command = "PWD";
880  next_state_ = STATE_CTRL_READ;
881  return SendFtpCommand(command, command, COMMAND_PWD);
882}
883
884int FtpNetworkTransaction::ProcessResponsePWD(const FtpCtrlResponse& response) {
885  switch (GetErrorClass(response.status_code)) {
886    case ERROR_CLASS_INITIATED:
887      return Stop(ERR_INVALID_RESPONSE);
888    case ERROR_CLASS_OK: {
889      // The info we look for should be on the first line.
890      std::string line = response.lines[0];
891      if (line.empty())
892        return Stop(ERR_INVALID_RESPONSE);
893      std::string::size_type quote_pos = line.find('"');
894      if (quote_pos != std::string::npos) {
895        line = line.substr(quote_pos + 1);
896        quote_pos = line.find('"');
897        if (quote_pos == std::string::npos)
898          return Stop(ERR_INVALID_RESPONSE);
899        line = line.substr(0, quote_pos);
900      }
901      if (system_type_ == SYSTEM_TYPE_VMS)
902        line = FtpUtil::VMSPathToUnix(line);
903      if (line.length() && line[line.length() - 1] == '/')
904        line.erase(line.length() - 1);
905      current_remote_directory_ = line;
906      next_state_ = STATE_CTRL_WRITE_TYPE;
907      break;
908    }
909    case ERROR_CLASS_INFO_NEEDED:
910      return Stop(ERR_INVALID_RESPONSE);
911    case ERROR_CLASS_TRANSIENT_ERROR:
912      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
913    case ERROR_CLASS_PERMANENT_ERROR:
914      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
915    default:
916      NOTREACHED();
917      return Stop(ERR_UNEXPECTED);
918  }
919  return OK;
920}
921
922// TYPE command.
923int FtpNetworkTransaction::DoCtrlWriteTYPE() {
924  std::string command = "TYPE ";
925  if (data_type_ == DATA_TYPE_ASCII) {
926    command += "A";
927  } else if (data_type_ == DATA_TYPE_IMAGE) {
928    command += "I";
929  } else {
930    NOTREACHED();
931    return Stop(ERR_UNEXPECTED);
932  }
933  next_state_ = STATE_CTRL_READ;
934  return SendFtpCommand(command, command, COMMAND_TYPE);
935}
936
937int FtpNetworkTransaction::ProcessResponseTYPE(
938    const FtpCtrlResponse& response) {
939  switch (GetErrorClass(response.status_code)) {
940    case ERROR_CLASS_INITIATED:
941      return Stop(ERR_INVALID_RESPONSE);
942    case ERROR_CLASS_OK:
943      next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV;
944      break;
945    case ERROR_CLASS_INFO_NEEDED:
946      return Stop(ERR_INVALID_RESPONSE);
947    case ERROR_CLASS_TRANSIENT_ERROR:
948      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
949    case ERROR_CLASS_PERMANENT_ERROR:
950      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
951    default:
952      NOTREACHED();
953      return Stop(ERR_UNEXPECTED);
954  }
955  return OK;
956}
957
958// EPSV command
959int FtpNetworkTransaction::DoCtrlWriteEPSV() {
960  const std::string command = "EPSV";
961  next_state_ = STATE_CTRL_READ;
962  return SendFtpCommand(command, command, COMMAND_EPSV);
963}
964
965int FtpNetworkTransaction::ProcessResponseEPSV(
966    const FtpCtrlResponse& response) {
967  switch (GetErrorClass(response.status_code)) {
968    case ERROR_CLASS_INITIATED:
969      return Stop(ERR_INVALID_RESPONSE);
970    case ERROR_CLASS_OK:
971      if (!ExtractPortFromEPSVResponse( response, &data_connection_port_))
972        return Stop(ERR_INVALID_RESPONSE);
973      if (data_connection_port_ < 1024 ||
974          !IsPortAllowedByFtp(data_connection_port_))
975        return Stop(ERR_UNSAFE_PORT);
976      next_state_ = STATE_DATA_CONNECT;
977      break;
978    case ERROR_CLASS_INFO_NEEDED:
979      return Stop(ERR_INVALID_RESPONSE);
980    case ERROR_CLASS_TRANSIENT_ERROR:
981    case ERROR_CLASS_PERMANENT_ERROR:
982      use_epsv_ = false;
983      next_state_ = STATE_CTRL_WRITE_PASV;
984      return OK;
985    default:
986      NOTREACHED();
987      return Stop(ERR_UNEXPECTED);
988  }
989  return OK;
990}
991
992// PASV command
993int FtpNetworkTransaction::DoCtrlWritePASV() {
994  std::string command = "PASV";
995  next_state_ = STATE_CTRL_READ;
996  return SendFtpCommand(command, command, COMMAND_PASV);
997}
998
999int FtpNetworkTransaction::ProcessResponsePASV(
1000    const FtpCtrlResponse& response) {
1001  switch (GetErrorClass(response.status_code)) {
1002    case ERROR_CLASS_INITIATED:
1003      return Stop(ERR_INVALID_RESPONSE);
1004    case ERROR_CLASS_OK:
1005      if (!ExtractPortFromPASVResponse(response, &data_connection_port_))
1006        return Stop(ERR_INVALID_RESPONSE);
1007      if (data_connection_port_ < 1024 ||
1008          !IsPortAllowedByFtp(data_connection_port_))
1009        return Stop(ERR_UNSAFE_PORT);
1010      next_state_ = STATE_DATA_CONNECT;
1011      break;
1012    case ERROR_CLASS_INFO_NEEDED:
1013      return Stop(ERR_INVALID_RESPONSE);
1014    case ERROR_CLASS_TRANSIENT_ERROR:
1015      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1016    case ERROR_CLASS_PERMANENT_ERROR:
1017      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1018    default:
1019      NOTREACHED();
1020      return Stop(ERR_UNEXPECTED);
1021  }
1022  return OK;
1023}
1024
1025// RETR command
1026int FtpNetworkTransaction::DoCtrlWriteRETR() {
1027  std::string command = "RETR " + GetRequestPathForFtpCommand(false);
1028  next_state_ = STATE_CTRL_READ;
1029  return SendFtpCommand(command, command, COMMAND_RETR);
1030}
1031
1032int FtpNetworkTransaction::ProcessResponseRETR(
1033    const FtpCtrlResponse& response) {
1034  switch (GetErrorClass(response.status_code)) {
1035    case ERROR_CLASS_INITIATED:
1036      // We want the client to start reading the response at this point.
1037      // It got here either through Start or RestartWithAuth. We want that
1038      // method to complete. Not setting next state here will make DoLoop exit
1039      // and in turn make Start/RestartWithAuth complete.
1040      resource_type_ = RESOURCE_TYPE_FILE;
1041      break;
1042    case ERROR_CLASS_OK:
1043      resource_type_ = RESOURCE_TYPE_FILE;
1044      next_state_ = STATE_CTRL_WRITE_QUIT;
1045      break;
1046    case ERROR_CLASS_INFO_NEEDED:
1047      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1048    case ERROR_CLASS_TRANSIENT_ERROR:
1049      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1050    case ERROR_CLASS_PERMANENT_ERROR:
1051      // Code 550 means "Failed to open file". Other codes are unrelated,
1052      // like "Not logged in" etc.
1053      if (response.status_code != 550 || resource_type_ == RESOURCE_TYPE_FILE)
1054        return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1055
1056      // It's possible that RETR failed because the path is a directory.
1057      resource_type_ = RESOURCE_TYPE_DIRECTORY;
1058
1059      // We're going to try CWD next, but first send a PASV one more time,
1060      // because some FTP servers, including FileZilla, require that.
1061      // See http://crbug.com/25316.
1062      next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV;
1063      break;
1064    default:
1065      NOTREACHED();
1066      return Stop(ERR_UNEXPECTED);
1067  }
1068
1069  // We should be sure about our resource type now. Otherwise we risk
1070  // an infinite loop (RETR can later send CWD, and CWD can later send RETR).
1071  DCHECK_NE(RESOURCE_TYPE_UNKNOWN, resource_type_);
1072
1073  return OK;
1074}
1075
1076// SIZE command
1077int FtpNetworkTransaction::DoCtrlWriteSIZE() {
1078  std::string command = "SIZE " + GetRequestPathForFtpCommand(false);
1079  next_state_ = STATE_CTRL_READ;
1080  return SendFtpCommand(command, command, COMMAND_SIZE);
1081}
1082
1083int FtpNetworkTransaction::ProcessResponseSIZE(
1084    const FtpCtrlResponse& response) {
1085  State state_after_size;
1086  if (resource_type_ == RESOURCE_TYPE_FILE)
1087    state_after_size = STATE_CTRL_WRITE_RETR;
1088  else
1089    state_after_size = STATE_CTRL_WRITE_CWD;
1090
1091  switch (GetErrorClass(response.status_code)) {
1092    case ERROR_CLASS_INITIATED:
1093      next_state_ = state_after_size;
1094      break;
1095    case ERROR_CLASS_OK:
1096      if (response.lines.size() != 1)
1097        return Stop(ERR_INVALID_RESPONSE);
1098      int64 size;
1099      if (!base::StringToInt64(response.lines[0], &size))
1100        return Stop(ERR_INVALID_RESPONSE);
1101      if (size < 0)
1102        return Stop(ERR_INVALID_RESPONSE);
1103
1104      // A successful response to SIZE does not mean the resource is a file.
1105      // Some FTP servers (for example, the qnx one) send a SIZE even for
1106      // directories.
1107      response_.expected_content_size = size;
1108
1109      next_state_ = state_after_size;
1110      break;
1111    case ERROR_CLASS_INFO_NEEDED:
1112      next_state_ = state_after_size;
1113      break;
1114    case ERROR_CLASS_TRANSIENT_ERROR:
1115      ResetDataConnectionAfterError(state_after_size);
1116      break;
1117    case ERROR_CLASS_PERMANENT_ERROR:
1118      // It's possible that SIZE failed because the path is a directory.
1119      if (resource_type_ == RESOURCE_TYPE_UNKNOWN &&
1120          response.status_code != 550) {
1121        return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1122      }
1123
1124      ResetDataConnectionAfterError(state_after_size);
1125      break;
1126    default:
1127      NOTREACHED();
1128      return Stop(ERR_UNEXPECTED);
1129  }
1130
1131  return OK;
1132}
1133
1134// CWD command
1135int FtpNetworkTransaction::DoCtrlWriteCWD() {
1136  std::string command = "CWD " + GetRequestPathForFtpCommand(true);
1137  next_state_ = STATE_CTRL_READ;
1138  return SendFtpCommand(command, command, COMMAND_CWD);
1139}
1140
1141int FtpNetworkTransaction::ProcessResponseCWD(const FtpCtrlResponse& response) {
1142  // We should never issue CWD if we know the target resource is a file.
1143  DCHECK_NE(RESOURCE_TYPE_FILE, resource_type_);
1144
1145  switch (GetErrorClass(response.status_code)) {
1146    case ERROR_CLASS_INITIATED:
1147      return Stop(ERR_INVALID_RESPONSE);
1148    case ERROR_CLASS_OK:
1149      next_state_ = STATE_CTRL_WRITE_LIST;
1150      break;
1151    case ERROR_CLASS_INFO_NEEDED:
1152      return Stop(ERR_INVALID_RESPONSE);
1153    case ERROR_CLASS_TRANSIENT_ERROR:
1154      // Some FTP servers send response 451 (not a valid CWD response according
1155      // to RFC 959) instead of 550.
1156      if (response.status_code == 451)
1157        return ProcessResponseCWDNotADirectory();
1158
1159      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1160    case ERROR_CLASS_PERMANENT_ERROR:
1161      if (response.status_code == 550)
1162        return ProcessResponseCWDNotADirectory();
1163
1164      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1165    default:
1166      NOTREACHED();
1167      return Stop(ERR_UNEXPECTED);
1168  }
1169
1170  return OK;
1171}
1172
1173int FtpNetworkTransaction::ProcessResponseCWDNotADirectory() {
1174  if (resource_type_ == RESOURCE_TYPE_DIRECTORY) {
1175    // We're assuming that the resource is a directory, but the server
1176    // says it's not true. The most probable interpretation is that it
1177    // doesn't exist (with FTP we can't be sure).
1178    return Stop(ERR_FILE_NOT_FOUND);
1179  }
1180
1181  // We are here because SIZE failed and we are not sure what the resource
1182  // type is. It could still be file, and SIZE could fail because of
1183  // an access error (http://crbug.com/56734). Try RETR just to be sure.
1184  resource_type_ = RESOURCE_TYPE_FILE;
1185
1186  ResetDataConnectionAfterError(STATE_CTRL_WRITE_RETR);
1187  return OK;
1188}
1189
1190// LIST command
1191int FtpNetworkTransaction::DoCtrlWriteLIST() {
1192  // Use the -l option for mod_ftp configured in LISTIsNLST mode: the option
1193  // forces LIST output instead of NLST (which would be ambiguous for us
1194  // to parse).
1195  std::string command("LIST -l");
1196  if (system_type_ == SYSTEM_TYPE_VMS)
1197    command = "LIST *.*;0";
1198
1199  next_state_ = STATE_CTRL_READ;
1200  return SendFtpCommand(command, command, COMMAND_LIST);
1201}
1202
1203int FtpNetworkTransaction::ProcessResponseLIST(
1204    const FtpCtrlResponse& response) {
1205  switch (GetErrorClass(response.status_code)) {
1206    case ERROR_CLASS_INITIATED:
1207      // We want the client to start reading the response at this point.
1208      // It got here either through Start or RestartWithAuth. We want that
1209      // method to complete. Not setting next state here will make DoLoop exit
1210      // and in turn make Start/RestartWithAuth complete.
1211      response_.is_directory_listing = true;
1212      break;
1213    case ERROR_CLASS_OK:
1214      response_.is_directory_listing = true;
1215      next_state_ = STATE_CTRL_WRITE_QUIT;
1216      break;
1217    case ERROR_CLASS_INFO_NEEDED:
1218      return Stop(ERR_INVALID_RESPONSE);
1219    case ERROR_CLASS_TRANSIENT_ERROR:
1220      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1221    case ERROR_CLASS_PERMANENT_ERROR:
1222      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1223    default:
1224      NOTREACHED();
1225      return Stop(ERR_UNEXPECTED);
1226  }
1227  return OK;
1228}
1229
1230// QUIT command
1231int FtpNetworkTransaction::DoCtrlWriteQUIT() {
1232  std::string command = "QUIT";
1233  next_state_ = STATE_CTRL_READ;
1234  return SendFtpCommand(command, command, COMMAND_QUIT);
1235}
1236
1237int FtpNetworkTransaction::ProcessResponseQUIT(
1238    const FtpCtrlResponse& response) {
1239  ctrl_socket_->Disconnect();
1240  return last_error_;
1241}
1242
1243// Data Connection
1244
1245int FtpNetworkTransaction::DoDataConnect() {
1246  next_state_ = STATE_DATA_CONNECT_COMPLETE;
1247  IPEndPoint ip_endpoint;
1248  AddressList data_address;
1249  // Connect to the same host as the control socket to prevent PASV port
1250  // scanning attacks.
1251  int rv = ctrl_socket_->GetPeerAddress(&ip_endpoint);
1252  if (rv != OK)
1253    return Stop(rv);
1254  data_address = AddressList::CreateFromIPAddress(
1255      ip_endpoint.address(), data_connection_port_);
1256  data_socket_.reset(socket_factory_->CreateTransportClientSocket(
1257        data_address, net_log_.net_log(), net_log_.source()));
1258  net_log_.AddEvent(
1259      NetLog::TYPE_FTP_DATA_CONNECTION,
1260      data_socket_->NetLog().source().ToEventParametersCallback());
1261  return data_socket_->Connect(io_callback_);
1262}
1263
1264int FtpNetworkTransaction::DoDataConnectComplete(int result) {
1265  if (result != OK && use_epsv_) {
1266    // It's possible we hit a broken server, sadly. They can break in different
1267    // ways. Some time out, some reset a connection. Fall back to PASV.
1268    // TODO(phajdan.jr): remember it for future transactions with this server.
1269    // TODO(phajdan.jr): write a test for this code path.
1270    use_epsv_ = false;
1271    next_state_ = STATE_CTRL_WRITE_PASV;
1272    return OK;
1273  }
1274
1275  // Only record the connection error after we've applied all our fallbacks.
1276  // We want to capture the final error, one we're not going to recover from.
1277  RecordDataConnectionError(result);
1278
1279  if (result != OK)
1280    return Stop(result);
1281
1282  next_state_ = state_after_data_connect_complete_;
1283  return OK;
1284}
1285
1286int FtpNetworkTransaction::DoDataRead() {
1287  DCHECK(read_data_buf_);
1288  DCHECK_GT(read_data_buf_len_, 0);
1289
1290  if (data_socket_ == NULL || !data_socket_->IsConnected()) {
1291    // If we don't destroy the data socket completely, some servers will wait
1292    // for us (http://crbug.com/21127). The half-closed TCP connection needs
1293    // to be closed on our side too.
1294    data_socket_.reset();
1295
1296    if (ctrl_socket_->IsConnected()) {
1297      // Wait for the server's response, we should get it before sending QUIT.
1298      next_state_ = STATE_CTRL_READ;
1299      return OK;
1300    }
1301
1302    // We are no longer connected to the server, so just finish the transaction.
1303    return Stop(OK);
1304  }
1305
1306  next_state_ = STATE_DATA_READ_COMPLETE;
1307  read_data_buf_->data()[0] = 0;
1308  return data_socket_->Read(read_data_buf_, read_data_buf_len_, io_callback_);
1309}
1310
1311int FtpNetworkTransaction::DoDataReadComplete(int result) {
1312  return result;
1313}
1314
1315// We're using a histogram as a group of counters, with one bucket for each
1316// enumeration value.  We're only interested in the values of the counters.
1317// Ignore the shape, average, and standard deviation of the histograms because
1318// they are meaningless.
1319//
1320// We use two histograms.  In the first histogram we tally whether the user has
1321// seen an error of that type during the session.  In the second histogram we
1322// tally the total number of times the users sees each errer.
1323void FtpNetworkTransaction::RecordDataConnectionError(int result) {
1324  // Gather data for http://crbug.com/3073. See how many users have trouble
1325  // establishing FTP data connection in passive FTP mode.
1326  enum {
1327    // Data connection successful.
1328    NET_ERROR_OK = 0,
1329
1330    // Local firewall blocked the connection.
1331    NET_ERROR_ACCESS_DENIED = 1,
1332
1333    // Connection timed out.
1334    NET_ERROR_TIMED_OUT = 2,
1335
1336    // Connection has been estabilished, but then got broken (either reset
1337    // or aborted).
1338    NET_ERROR_CONNECTION_BROKEN = 3,
1339
1340    // Connection has been refused.
1341    NET_ERROR_CONNECTION_REFUSED = 4,
1342
1343    // No connection to the internet.
1344    NET_ERROR_INTERNET_DISCONNECTED = 5,
1345
1346    // Could not reach the destination address.
1347    NET_ERROR_ADDRESS_UNREACHABLE = 6,
1348
1349    // A programming error in our network stack.
1350    NET_ERROR_UNEXPECTED = 7,
1351
1352    // Other kind of error.
1353    NET_ERROR_OTHER = 20,
1354
1355    NUM_OF_NET_ERROR_TYPES
1356  } type;
1357  switch (result) {
1358    case OK:
1359      type = NET_ERROR_OK;
1360      break;
1361    case ERR_ACCESS_DENIED:
1362    case ERR_NETWORK_ACCESS_DENIED:
1363      type = NET_ERROR_ACCESS_DENIED;
1364      break;
1365    case ERR_TIMED_OUT:
1366      type = NET_ERROR_TIMED_OUT;
1367      break;
1368    case ERR_CONNECTION_ABORTED:
1369    case ERR_CONNECTION_RESET:
1370    case ERR_CONNECTION_CLOSED:
1371      type = NET_ERROR_CONNECTION_BROKEN;
1372      break;
1373    case ERR_CONNECTION_FAILED:
1374    case ERR_CONNECTION_REFUSED:
1375      type = NET_ERROR_CONNECTION_REFUSED;
1376      break;
1377    case ERR_INTERNET_DISCONNECTED:
1378      type = NET_ERROR_INTERNET_DISCONNECTED;
1379      break;
1380    case ERR_ADDRESS_INVALID:
1381    case ERR_ADDRESS_UNREACHABLE:
1382      type = NET_ERROR_ADDRESS_UNREACHABLE;
1383      break;
1384    case ERR_UNEXPECTED:
1385      type = NET_ERROR_UNEXPECTED;
1386      break;
1387    default:
1388      type = NET_ERROR_OTHER;
1389      break;
1390  };
1391  static bool had_error_type[NUM_OF_NET_ERROR_TYPES];
1392
1393  DCHECK(type >= 0 && type < NUM_OF_NET_ERROR_TYPES);
1394  if (!had_error_type[type]) {
1395    had_error_type[type] = true;
1396    UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorHappened",
1397        type, NUM_OF_NET_ERROR_TYPES);
1398  }
1399  UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorCount",
1400      type, NUM_OF_NET_ERROR_TYPES);
1401}
1402
1403}  // namespace net
1404