ftp_network_transaction.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
11320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// Copyright (c) 2012 The Chromium Authors. All rights reserved.
21320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// Use of this source code is governed by a BSD-style license that can be
31320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// found in the LICENSE file.
41320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
51320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "net/ftp/ftp_network_transaction.h"
61320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
71320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/bind.h"
81320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/bind_helpers.h"
91320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/compiler_specific.h"
101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/metrics/histogram.h"
111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/string_number_conversions.h"
121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/string_util.h"
131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/strings/string_split.h"
141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/utf_string_conversions.h"
151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/values.h"
161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "net/base/address_list.h"
171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "net/base/connection_type_histograms.h"
181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "net/base/escape.h"
191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "net/base/net_errors.h"
201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "net/base/net_log.h"
211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "net/base/net_util.h"
221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "net/ftp/ftp_network_session.h"
231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "net/ftp/ftp_request_info.h"
241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "net/ftp/ftp_util.h"
251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "net/socket/client_socket_factory.h"
261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "net/socket/stream_socket.h"
271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
281320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciconst char kCRLF[] = "\r\n";
291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciconst int kCtrlBufLen = 1024;
311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccinamespace {
331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// Returns true if |input| can be safely used as a part of FTP command.
351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccibool IsValidFTPCommandString(const std::string& input) {
361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // RFC 959 only allows ASCII strings, but at least Firefox can send non-ASCII
371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // characters in the command if the request path contains them. To be
381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // compatible, we do the same and allow non-ASCII characters in a command.
391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // Protect agains newline injection attack.
411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (input.find_first_of("\r\n") != std::string::npos)
421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return false;
431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  return true;
451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccienum ErrorClass {
481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // The requested action was initiated. The client should expect another
491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // reply before issuing the next command.
501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  ERROR_CLASS_INITIATED,
511320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
521320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // The requested action has been successfully completed.
531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  ERROR_CLASS_OK,
541320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // The command has been accepted, but to complete the operation, more
561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // information must be sent by the client.
571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  ERROR_CLASS_INFO_NEEDED,
581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // The command was not accepted and the requested action did not take place.
601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // This condition is temporary, and the client is encouraged to restart the
611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // command sequence.
621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  ERROR_CLASS_TRANSIENT_ERROR,
631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // The command was not accepted and the requested action did not take place.
651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // This condition is rather permanent, and the client is discouraged from
661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // repeating the exact request.
671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  ERROR_CLASS_PERMANENT_ERROR,
681320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci};
691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// Returns the error class for given response code. Caller should ensure
711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// that |response_code| is in range 100-599.
721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciErrorClass GetErrorClass(int response_code) {
731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (response_code >= 100 && response_code <= 199)
741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return ERROR_CLASS_INITIATED;
751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (response_code >= 200 && response_code <= 299)
771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return ERROR_CLASS_OK;
781320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (response_code >= 300 && response_code <= 399)
801320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return ERROR_CLASS_INFO_NEEDED;
811320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (response_code >= 400 && response_code <= 499)
831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return ERROR_CLASS_TRANSIENT_ERROR;
841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
851320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (response_code >= 500 && response_code <= 599)
861320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return ERROR_CLASS_PERMANENT_ERROR;
871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // We should not be called on invalid error codes.
891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  NOTREACHED() << response_code;
901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  return ERROR_CLASS_PERMANENT_ERROR;
911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
921320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// Returns network error code for received FTP |response_code|.
941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciint GetNetErrorCodeForFtpResponseCode(int response_code) {
951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  switch (response_code) {
961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    case 421:
971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      return net::ERR_FTP_SERVICE_UNAVAILABLE;
981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    case 426:
991320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      return net::ERR_FTP_TRANSFER_ABORTED;
1001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    case 450:
1011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      return net::ERR_FTP_FILE_BUSY;
1021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    case 500:
1031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    case 501:
1041320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      return net::ERR_FTP_SYNTAX_ERROR;
1051320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    case 502:
1061320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    case 504:
1071320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      return net::ERR_FTP_COMMAND_NOT_SUPPORTED;
1081320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    case 503:
1091320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      return net::ERR_FTP_BAD_COMMAND_SEQUENCE;
1101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    default:
1111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      return net::ERR_FTP_FAILED;
1121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  }
1131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
1141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// From RFC 2428 Section 3:
1161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci//   The text returned in response to the EPSV command MUST be:
1171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci//     <some text> (<d><d><d><tcp-port><d>)
1181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci//   <d> is a delimiter character, ideally to be |
1191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccibool ExtractPortFromEPSVResponse(const net::FtpCtrlResponse& response,
1201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                                 int* port) {
1211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (response.lines.size() != 1)
1221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return false;
1231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  const char* ptr = response.lines[0].c_str();
1241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  while (*ptr && *ptr != '(')
1251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    ++ptr;
1261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (!*ptr)
1271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return false;
1281320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  char sep = *(++ptr);
1291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (!sep || isdigit(sep) || *(++ptr) != sep || *(++ptr) != sep)
1301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return false;
1311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (!isdigit(*(++ptr)))
1321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return false;
1331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  *port = *ptr - '0';
1341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  while (isdigit(*(++ptr))) {
1351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    *port *= 10;
1361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    *port += *ptr - '0';
1371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  }
1381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (*ptr != sep)
1391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return false;
1401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  return true;
1421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
1431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// There are two way we can receive IP address and port.
1451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// (127,0,0,1,23,21) IP address and port encapsulated in ().
1461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// 127,0,0,1,23,21  IP address and port without ().
1471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci//
1481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// See RFC 959, Section 4.1.2
1491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccibool ExtractPortFromPASVResponse(const net::FtpCtrlResponse& response,
1501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                                 int* port) {
1511320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (response.lines.size() != 1)
1521320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return false;
1531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1541320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  std::string line(response.lines[0]);
1551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (!IsStringASCII(line))
1561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return false;
1571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (line.length() < 2)
1581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return false;
1591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  size_t paren_pos = line.find('(');
1611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (paren_pos == std::string::npos) {
1621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // Find the first comma and use it to locate the beginning
1631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // of the response data.
1641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    size_t comma_pos = line.find(',');
1651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (comma_pos == std::string::npos)
1661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      return false;
1671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1681320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    size_t space_pos = line.rfind(' ', comma_pos);
1691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (space_pos != std::string::npos)
1701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      line = line.substr(space_pos + 1);
1711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  } else {
1721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // Remove the parentheses and use the text inside them.
1731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    size_t closing_paren_pos = line.rfind(')');
1741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (closing_paren_pos == std::string::npos)
1751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      return false;
1761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (closing_paren_pos <= paren_pos)
1771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      return false;
1781320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    line = line.substr(paren_pos + 1, closing_paren_pos - paren_pos - 1);
1801320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  }
1811320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // Split the line into comma-separated pieces and extract
1831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // the last two.
1841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  std::vector<std::string> pieces;
1851320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  base::SplitString(line, ',', &pieces);
1861320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (pieces.size() != 6)
1871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return false;
1881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // Ignore the IP address supplied in the response. We are always going
1901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // to connect back to the same server to prevent FTP PASV port scanning.
1911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  int p0, p1;
1921320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (!base::StringToInt(pieces[4], &p0))
1931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return false;
1941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (!base::StringToInt(pieces[5], &p1))
1951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return false;
1961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  *port = (p0 << 8) + p1;
1971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  return true;
1991320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
2001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}  // namespace
2021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccinamespace net {
2041320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2051320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciFtpNetworkTransaction::FtpNetworkTransaction(
2061320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    FtpNetworkSession* session,
2071320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    ClientSocketFactory* socket_factory)
2081320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    : command_sent_(COMMAND_NONE),
2091320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      ALLOW_THIS_IN_INITIALIZER_LIST(
2101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci          io_callback_(base::Bind(&FtpNetworkTransaction::OnIOComplete,
2111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                                  base::Unretained(this)))),
2121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      session_(session),
2131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      request_(NULL),
2141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      resolver_(session->host_resolver()),
2151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      read_ctrl_buf_(new IOBuffer(kCtrlBufLen)),
2161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      ctrl_response_buffer_(NULL),
2171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      read_data_buf_len_(0),
2181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      last_error_(OK),
2191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      system_type_(SYSTEM_TYPE_UNKNOWN),
2201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      // Use image (binary) transfer by default. It should always work,
2211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      // whereas the ascii transfer may damage binary data.
2221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      data_type_(DATA_TYPE_IMAGE),
2231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      resource_type_(RESOURCE_TYPE_UNKNOWN),
2241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      use_epsv_(true),
2251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      data_connection_port_(0),
2261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      socket_factory_(socket_factory),
2271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      next_state_(STATE_NONE),
2281320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      state_after_data_connect_complete_(STATE_CTRL_WRITE_SIZE) {
2291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
2301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciFtpNetworkTransaction::~FtpNetworkTransaction() {
2321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
2331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciint FtpNetworkTransaction::Stop(int error) {
2351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (command_sent_ == COMMAND_QUIT)
2361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return error;
2371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  next_state_ = STATE_CTRL_WRITE_QUIT;
2391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  last_error_ = error;
2401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  return OK;
2411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
2421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciint FtpNetworkTransaction::RestartIgnoringLastError(
2441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    const CompletionCallback& callback) {
2451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  return ERR_NOT_IMPLEMENTED;
2461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
2471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciint FtpNetworkTransaction::Start(const FtpRequestInfo* request_info,
2491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                                 const CompletionCallback& callback,
2501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                                 const BoundNetLog& net_log) {
2511320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  net_log_ = net_log;
2521320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  request_ = request_info;
2531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2541320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  ctrl_response_buffer_.reset(new FtpCtrlResponseBuffer(net_log_));
2551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (request_->url.has_username()) {
2571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    string16 username;
2581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    string16 password;
2591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    GetIdentityFromURL(request_->url, &username, &password);
2601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    credentials_.Set(username, password);
2611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  } else {
2621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    credentials_.Set(ASCIIToUTF16("anonymous"),
2631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                     ASCIIToUTF16("chrome@example.com"));
2641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  }
2651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  DetectTypecode();
2671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2681320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  next_state_ = STATE_CTRL_RESOLVE_HOST;
2691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  int rv = DoLoop(OK);
2701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (rv == ERR_IO_PENDING)
2711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    user_callback_ = callback;
2721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  return rv;
2731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
2741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciint FtpNetworkTransaction::RestartWithAuth(const AuthCredentials& credentials,
2761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                                           const CompletionCallback& callback) {
2771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  ResetStateForRestart();
2781320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  credentials_ = credentials;
2801320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2811320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  next_state_ = STATE_CTRL_RESOLVE_HOST;
2821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  int rv = DoLoop(OK);
2831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (rv == ERR_IO_PENDING)
2841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    user_callback_ = callback;
2851320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  return rv;
2861320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
2871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciint FtpNetworkTransaction::Read(IOBuffer* buf,
2891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                                int buf_len,
2901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                                const CompletionCallback& callback) {
2911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  DCHECK(buf);
2921320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  DCHECK_GT(buf_len, 0);
2931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  read_data_buf_ = buf;
2951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  read_data_buf_len_ = buf_len;
2961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  next_state_ = STATE_DATA_READ;
2981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  int rv = DoLoop(OK);
2991320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (rv == ERR_IO_PENDING)
3001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    user_callback_ = callback;
3011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  return rv;
3021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
3031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
3041320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciconst FtpResponseInfo* FtpNetworkTransaction::GetResponseInfo() const {
3051320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  return &response_;
3061320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
3071320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
3081320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciLoadState FtpNetworkTransaction::GetLoadState() const {
3091320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (next_state_ == STATE_CTRL_RESOLVE_HOST_COMPLETE)
3101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return LOAD_STATE_RESOLVING_HOST;
3111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
3121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (next_state_ == STATE_CTRL_CONNECT_COMPLETE ||
3131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      next_state_ == STATE_DATA_CONNECT_COMPLETE)
3141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return LOAD_STATE_CONNECTING;
3151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
3161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (next_state_ == STATE_DATA_READ_COMPLETE)
3171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return LOAD_STATE_READING_RESPONSE;
3181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
3191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (command_sent_ == COMMAND_RETR && read_data_buf_.get())
3201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return LOAD_STATE_READING_RESPONSE;
3211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
3221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (command_sent_ == COMMAND_QUIT)
3231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return LOAD_STATE_IDLE;
3241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
3251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (command_sent_ != COMMAND_NONE)
3261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return LOAD_STATE_SENDING_REQUEST;
3271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
3281320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  return LOAD_STATE_IDLE;
3291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
3301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
3311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciuint64 FtpNetworkTransaction::GetUploadProgress() const {
3321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  return 0;
3331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
3341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
3351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid FtpNetworkTransaction::ResetStateForRestart() {
3361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  command_sent_ = COMMAND_NONE;
3371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  user_callback_.Reset();
3381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  response_ = FtpResponseInfo();
3391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  read_ctrl_buf_ = new IOBuffer(kCtrlBufLen);
3401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  ctrl_response_buffer_.reset(new FtpCtrlResponseBuffer(net_log_));
3411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  read_data_buf_ = NULL;
3421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  read_data_buf_len_ = 0;
3431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (write_buf_)
3441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    write_buf_->SetOffset(0);
3451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  last_error_ = OK;
3461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  data_connection_port_ = 0;
3471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  ctrl_socket_.reset();
3481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  data_socket_.reset();
3491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  next_state_ = STATE_NONE;
3501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  state_after_data_connect_complete_ = STATE_CTRL_WRITE_SIZE;
3511320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
3521320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
3531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid FtpNetworkTransaction::ResetDataConnectionAfterError(State next_state) {
3541320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // The server _might_ have reset the data connection
3551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // (see RFC 959 3.2. ESTABLISHING DATA CONNECTIONS:
3561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // "The server MUST close the data connection under the following
3571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // conditions:
3581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // ...
3591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // 5. An irrecoverable error condition occurs.")
3601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  //
3611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // It is ambiguous what an irrecoverable error condition is,
3621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // so we take no chances.
3631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  state_after_data_connect_complete_ = next_state;
3641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV;
3651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
3661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
3671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid FtpNetworkTransaction::DoCallback(int rv) {
3681320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  DCHECK(rv != ERR_IO_PENDING);
3691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  DCHECK(!user_callback_.is_null());
3701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
3711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // Since Run may result in Read being called, clear callback_ up front.
3721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  CompletionCallback c = user_callback_;
3731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  user_callback_.Reset();
3741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  c.Run(rv);
3751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
3761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
3771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid FtpNetworkTransaction::OnIOComplete(int result) {
3781320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  int rv = DoLoop(result);
3791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (rv != ERR_IO_PENDING)
3801320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    DoCallback(rv);
3811320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
3821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
3831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciint FtpNetworkTransaction::ProcessCtrlResponse() {
3841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  FtpCtrlResponse response = ctrl_response_buffer_->PopResponse();
3851320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
3861320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  int rv = OK;
3871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  switch (command_sent_) {
3881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    case COMMAND_NONE:
3891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      // TODO(phajdan.jr): Check for errors in the welcome message.
3901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      next_state_ = STATE_CTRL_WRITE_USER;
3911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      break;
3921320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    case COMMAND_USER:
3931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      rv = ProcessResponseUSER(response);
3941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      break;
3951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    case COMMAND_PASS:
3961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      rv = ProcessResponsePASS(response);
3971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      break;
3981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    case COMMAND_SYST:
3991320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      rv = ProcessResponseSYST(response);
4001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      break;
4011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    case COMMAND_PWD:
4021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      rv = ProcessResponsePWD(response);
4031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      break;
4041320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    case COMMAND_TYPE:
4051320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      rv = ProcessResponseTYPE(response);
4061320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      break;
4071320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    case COMMAND_EPSV:
4081320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      rv = ProcessResponseEPSV(response);
4091320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      break;
4101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    case COMMAND_PASV:
4111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      rv = ProcessResponsePASV(response);
4121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      break;
4131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    case COMMAND_SIZE:
4141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      rv = ProcessResponseSIZE(response);
4151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      break;
4161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    case COMMAND_RETR:
4171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      rv = ProcessResponseRETR(response);
4181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      break;
4191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    case COMMAND_CWD:
4201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      rv = ProcessResponseCWD(response);
4211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      break;
4221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    case COMMAND_LIST:
4231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      rv = ProcessResponseLIST(response);
4241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      break;
4251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    case COMMAND_QUIT:
4261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      rv = ProcessResponseQUIT(response);
4271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      break;
4281320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    default:
4291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      LOG(DFATAL) << "Unexpected value of command_sent_: " << command_sent_;
4301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      return ERR_UNEXPECTED;
4311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  }
4321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
4331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // We may get multiple responses for some commands,
4341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // see http://crbug.com/18036.
4351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  while (ctrl_response_buffer_->ResponseAvailable() && rv == OK) {
4361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    response = ctrl_response_buffer_->PopResponse();
4371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
4381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    switch (command_sent_) {
4391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      case COMMAND_RETR:
4401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        rv = ProcessResponseRETR(response);
4411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        break;
4421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      case COMMAND_LIST:
4431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        rv = ProcessResponseLIST(response);
4441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        break;
4451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      default:
4461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        // Multiple responses for other commands are invalid.
4471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        return Stop(ERR_INVALID_RESPONSE);
4481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    }
4491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  }
4501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
4511320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  return rv;
4521320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
4531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
4541320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// Used to prepare and send FTP command.
4551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciint FtpNetworkTransaction::SendFtpCommand(const std::string& command,
4561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                                          const std::string& command_for_log,
4571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                                          Command cmd) {
4581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // If we send a new command when we still have unprocessed responses
4591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // for previous commands, the response receiving code will have no way to know
4601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // which responses are for which command.
4611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  DCHECK(!ctrl_response_buffer_->ResponseAvailable());
4621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
4631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  DCHECK(!write_command_buf_);
4641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  DCHECK(!write_buf_);
4651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
4661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (!IsValidFTPCommandString(command)) {
4671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // Callers should validate the command themselves and return a more specific
4681320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // error code.
4691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    NOTREACHED();
4701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return Stop(ERR_UNEXPECTED);
4711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  }
4721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
4731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  command_sent_ = cmd;
4741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
4751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  write_command_buf_ = new IOBufferWithSize(command.length() + 2);
4761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  write_buf_ = new DrainableIOBuffer(write_command_buf_,
4771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                                     write_command_buf_->size());
4781320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  memcpy(write_command_buf_->data(), command.data(), command.length());
4791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  memcpy(write_command_buf_->data() + command.length(), kCRLF, 2);
4801320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
4811320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  net_log_.AddEvent(NetLog::TYPE_FTP_COMMAND_SENT,
4821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                    NetLog::StringCallback("command", &command_for_log));
4831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
4841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  next_state_ = STATE_CTRL_WRITE;
4851320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  return OK;
4861320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
4871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
4881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccistd::string FtpNetworkTransaction::GetRequestPathForFtpCommand(
4891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    bool is_directory) const {
4901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  std::string path(current_remote_directory_);
4911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (request_->url.has_path()) {
4921320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    std::string gurl_path(request_->url.path());
4931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
4941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // Get rid of the typecode, see RFC 1738 section 3.2.2. FTP url-path.
4951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    std::string::size_type pos = gurl_path.rfind(';');
4961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (pos != std::string::npos)
4971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      gurl_path.resize(pos);
4981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
4991320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    path.append(gurl_path);
5001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  }
5011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // Make sure that if the path is expected to be a file, it won't end
5021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // with a trailing slash.
5031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (!is_directory && path.length() > 1 && path[path.length() - 1] == '/')
5041320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    path.erase(path.length() - 1);
5051320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  UnescapeRule::Type unescape_rules = UnescapeRule::SPACES |
5061320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                                      UnescapeRule::URL_SPECIAL_CHARS;
5071320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // This may unescape to non-ASCII characters, but we allow that. See the
5081320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // comment for IsValidFTPCommandString.
5091320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  path = net::UnescapeURLComponent(path, unescape_rules);
5101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
5111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (system_type_ == SYSTEM_TYPE_VMS) {
5121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (is_directory)
5131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      path = FtpUtil::UnixDirectoryPathToVMS(path);
514    else
515      path = FtpUtil::UnixFilePathToVMS(path);
516  }
517
518  DCHECK(IsValidFTPCommandString(path));
519  return path;
520}
521
522void FtpNetworkTransaction::DetectTypecode() {
523  if (!request_->url.has_path())
524    return;
525  std::string gurl_path(request_->url.path());
526
527  // Extract the typecode, see RFC 1738 section 3.2.2. FTP url-path.
528  std::string::size_type pos = gurl_path.rfind(';');
529  if (pos == std::string::npos)
530    return;
531  std::string typecode_string(gurl_path.substr(pos));
532  if (typecode_string == ";type=a") {
533    data_type_ = DATA_TYPE_ASCII;
534    resource_type_ = RESOURCE_TYPE_FILE;
535  } else if (typecode_string == ";type=i") {
536    data_type_ = DATA_TYPE_IMAGE;
537    resource_type_ = RESOURCE_TYPE_FILE;
538  } else if (typecode_string == ";type=d") {
539    resource_type_ = RESOURCE_TYPE_DIRECTORY;
540  }
541}
542
543int FtpNetworkTransaction::DoLoop(int result) {
544  DCHECK(next_state_ != STATE_NONE);
545
546  int rv = result;
547  do {
548    State state = next_state_;
549    next_state_ = STATE_NONE;
550    switch (state) {
551      case STATE_CTRL_RESOLVE_HOST:
552        DCHECK(rv == OK);
553        rv = DoCtrlResolveHost();
554        break;
555      case STATE_CTRL_RESOLVE_HOST_COMPLETE:
556        rv = DoCtrlResolveHostComplete(rv);
557        break;
558      case STATE_CTRL_CONNECT:
559        DCHECK(rv == OK);
560        rv = DoCtrlConnect();
561        break;
562      case STATE_CTRL_CONNECT_COMPLETE:
563        rv = DoCtrlConnectComplete(rv);
564        break;
565      case STATE_CTRL_READ:
566        DCHECK(rv == OK);
567        rv = DoCtrlRead();
568        break;
569      case STATE_CTRL_READ_COMPLETE:
570        rv = DoCtrlReadComplete(rv);
571        break;
572      case STATE_CTRL_WRITE:
573        DCHECK(rv == OK);
574        rv = DoCtrlWrite();
575        break;
576      case STATE_CTRL_WRITE_COMPLETE:
577        rv = DoCtrlWriteComplete(rv);
578        break;
579      case STATE_CTRL_WRITE_USER:
580        DCHECK(rv == OK);
581        rv = DoCtrlWriteUSER();
582        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