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