ftp_network_transaction.cc revision 7d4cd473f85ac64c3747c96c277f9e506a0d2246
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/ftp/ftp_network_transaction.h"
6
7#include "base/bind.h"
8#include "base/bind_helpers.h"
9#include "base/compiler_specific.h"
10#include "base/metrics/histogram.h"
11#include "base/strings/string_number_conversions.h"
12#include "base/strings/string_util.h"
13#include "base/strings/string_split.h"
14#include "base/strings/utf_string_conversions.h"
15#include "base/values.h"
16#include "net/base/address_list.h"
17#include "net/base/connection_type_histograms.h"
18#include "net/base/escape.h"
19#include "net/base/net_errors.h"
20#include "net/base/net_log.h"
21#include "net/base/net_util.h"
22#include "net/ftp/ftp_network_session.h"
23#include "net/ftp/ftp_request_info.h"
24#include "net/ftp/ftp_util.h"
25#include "net/socket/client_socket_factory.h"
26#include "net/socket/stream_socket.h"
27
28const char kCRLF[] = "\r\n";
29
30const int kCtrlBufLen = 1024;
31
32namespace {
33
34// Returns true if |input| can be safely used as a part of FTP command.
35bool IsValidFTPCommandString(const std::string& input) {
36  // RFC 959 only allows ASCII strings, but at least Firefox can send non-ASCII
37  // characters in the command if the request path contains them. To be
38  // compatible, we do the same and allow non-ASCII characters in a command.
39
40  // Protect agains newline injection attack.
41  if (input.find_first_of("\r\n") != std::string::npos)
42    return false;
43
44  return true;
45}
46
47enum ErrorClass {
48  // The requested action was initiated. The client should expect another
49  // reply before issuing the next command.
50  ERROR_CLASS_INITIATED,
51
52  // The requested action has been successfully completed.
53  ERROR_CLASS_OK,
54
55  // The command has been accepted, but to complete the operation, more
56  // information must be sent by the client.
57  ERROR_CLASS_INFO_NEEDED,
58
59  // The command was not accepted and the requested action did not take place.
60  // This condition is temporary, and the client is encouraged to restart the
61  // command sequence.
62  ERROR_CLASS_TRANSIENT_ERROR,
63
64  // The command was not accepted and the requested action did not take place.
65  // This condition is rather permanent, and the client is discouraged from
66  // repeating the exact request.
67  ERROR_CLASS_PERMANENT_ERROR,
68};
69
70// Returns the error class for given response code. Caller should ensure
71// that |response_code| is in range 100-599.
72ErrorClass GetErrorClass(int response_code) {
73  if (response_code >= 100 && response_code <= 199)
74    return ERROR_CLASS_INITIATED;
75
76  if (response_code >= 200 && response_code <= 299)
77    return ERROR_CLASS_OK;
78
79  if (response_code >= 300 && response_code <= 399)
80    return ERROR_CLASS_INFO_NEEDED;
81
82  if (response_code >= 400 && response_code <= 499)
83    return ERROR_CLASS_TRANSIENT_ERROR;
84
85  if (response_code >= 500 && response_code <= 599)
86    return ERROR_CLASS_PERMANENT_ERROR;
87
88  // We should not be called on invalid error codes.
89  NOTREACHED() << response_code;
90  return ERROR_CLASS_PERMANENT_ERROR;
91}
92
93// Returns network error code for received FTP |response_code|.
94int GetNetErrorCodeForFtpResponseCode(int response_code) {
95  switch (response_code) {
96    case 421:
97      return net::ERR_FTP_SERVICE_UNAVAILABLE;
98    case 426:
99      return net::ERR_FTP_TRANSFER_ABORTED;
100    case 450:
101      return net::ERR_FTP_FILE_BUSY;
102    case 500:
103    case 501:
104      return net::ERR_FTP_SYNTAX_ERROR;
105    case 502:
106    case 504:
107      return net::ERR_FTP_COMMAND_NOT_SUPPORTED;
108    case 503:
109      return net::ERR_FTP_BAD_COMMAND_SEQUENCE;
110    default:
111      return net::ERR_FTP_FAILED;
112  }
113}
114
115// From RFC 2428 Section 3:
116//   The text returned in response to the EPSV command MUST be:
117//     <some text> (<d><d><d><tcp-port><d>)
118//   <d> is a delimiter character, ideally to be |
119bool ExtractPortFromEPSVResponse(const net::FtpCtrlResponse& response,
120                                 int* port) {
121  if (response.lines.size() != 1)
122    return false;
123  const char* ptr = response.lines[0].c_str();
124  while (*ptr && *ptr != '(')
125    ++ptr;
126  if (!*ptr)
127    return false;
128  char sep = *(++ptr);
129  if (!sep || isdigit(sep) || *(++ptr) != sep || *(++ptr) != sep)
130    return false;
131  if (!isdigit(*(++ptr)))
132    return false;
133  *port = *ptr - '0';
134  while (isdigit(*(++ptr))) {
135    *port *= 10;
136    *port += *ptr - '0';
137  }
138  if (*ptr != sep)
139    return false;
140
141  return true;
142}
143
144// There are two way we can receive IP address and port.
145// (127,0,0,1,23,21) IP address and port encapsulated in ().
146// 127,0,0,1,23,21  IP address and port without ().
147//
148// See RFC 959, Section 4.1.2
149bool ExtractPortFromPASVResponse(const net::FtpCtrlResponse& response,
150                                 int* port) {
151  if (response.lines.size() != 1)
152    return false;
153
154  std::string line(response.lines[0]);
155  if (!IsStringASCII(line))
156    return false;
157  if (line.length() < 2)
158    return false;
159
160  size_t paren_pos = line.find('(');
161  if (paren_pos == std::string::npos) {
162    // Find the first comma and use it to locate the beginning
163    // of the response data.
164    size_t comma_pos = line.find(',');
165    if (comma_pos == std::string::npos)
166      return false;
167
168    size_t space_pos = line.rfind(' ', comma_pos);
169    if (space_pos != std::string::npos)
170      line = line.substr(space_pos + 1);
171  } else {
172    // Remove the parentheses and use the text inside them.
173    size_t closing_paren_pos = line.rfind(')');
174    if (closing_paren_pos == std::string::npos)
175      return false;
176    if (closing_paren_pos <= paren_pos)
177      return false;
178
179    line = line.substr(paren_pos + 1, closing_paren_pos - paren_pos - 1);
180  }
181
182  // Split the line into comma-separated pieces and extract
183  // the last two.
184  std::vector<std::string> pieces;
185  base::SplitString(line, ',', &pieces);
186  if (pieces.size() != 6)
187    return false;
188
189  // Ignore the IP address supplied in the response. We are always going
190  // to connect back to the same server to prevent FTP PASV port scanning.
191  int p0, p1;
192  if (!base::StringToInt(pieces[4], &p0))
193    return false;
194  if (!base::StringToInt(pieces[5], &p1))
195    return false;
196  *port = (p0 << 8) + p1;
197
198  return true;
199}
200
201}  // namespace
202
203namespace net {
204
205FtpNetworkTransaction::FtpNetworkTransaction(
206    FtpNetworkSession* session,
207    ClientSocketFactory* socket_factory)
208    : command_sent_(COMMAND_NONE),
209      io_callback_(base::Bind(&FtpNetworkTransaction::OnIOComplete,
210                              base::Unretained(this))),
211      session_(session),
212      request_(NULL),
213      resolver_(session->host_resolver()),
214      read_ctrl_buf_(new IOBuffer(kCtrlBufLen)),
215      read_data_buf_len_(0),
216      last_error_(OK),
217      system_type_(SYSTEM_TYPE_UNKNOWN),
218      // Use image (binary) transfer by default. It should always work,
219      // whereas the ascii transfer may damage binary data.
220      data_type_(DATA_TYPE_IMAGE),
221      resource_type_(RESOURCE_TYPE_UNKNOWN),
222      use_epsv_(true),
223      data_connection_port_(0),
224      socket_factory_(socket_factory),
225      next_state_(STATE_NONE),
226      state_after_data_connect_complete_(STATE_CTRL_WRITE_SIZE) {}
227
228FtpNetworkTransaction::~FtpNetworkTransaction() {
229}
230
231int FtpNetworkTransaction::Stop(int error) {
232  if (command_sent_ == COMMAND_QUIT)
233    return error;
234
235  next_state_ = STATE_CTRL_WRITE_QUIT;
236  last_error_ = error;
237  return OK;
238}
239
240int FtpNetworkTransaction::RestartIgnoringLastError(
241    const CompletionCallback& callback) {
242  return ERR_NOT_IMPLEMENTED;
243}
244
245int FtpNetworkTransaction::Start(const FtpRequestInfo* request_info,
246                                 const CompletionCallback& callback,
247                                 const BoundNetLog& net_log) {
248  net_log_ = net_log;
249  request_ = request_info;
250
251  ctrl_response_buffer_.reset(new FtpCtrlResponseBuffer(net_log_));
252
253  if (request_->url.has_username()) {
254    base::string16 username;
255    base::string16 password;
256    GetIdentityFromURL(request_->url, &username, &password);
257    credentials_.Set(username, password);
258  } else {
259    credentials_.Set(ASCIIToUTF16("anonymous"),
260                     ASCIIToUTF16("chrome@example.com"));
261  }
262
263  DetectTypecode();
264
265  next_state_ = STATE_CTRL_RESOLVE_HOST;
266  int rv = DoLoop(OK);
267  if (rv == ERR_IO_PENDING)
268    user_callback_ = callback;
269  return rv;
270}
271
272int FtpNetworkTransaction::RestartWithAuth(const AuthCredentials& credentials,
273                                           const CompletionCallback& callback) {
274  ResetStateForRestart();
275
276  credentials_ = credentials;
277
278  next_state_ = STATE_CTRL_RESOLVE_HOST;
279  int rv = DoLoop(OK);
280  if (rv == ERR_IO_PENDING)
281    user_callback_ = callback;
282  return rv;
283}
284
285int FtpNetworkTransaction::Read(IOBuffer* buf,
286                                int buf_len,
287                                const CompletionCallback& callback) {
288  DCHECK(buf);
289  DCHECK_GT(buf_len, 0);
290
291  read_data_buf_ = buf;
292  read_data_buf_len_ = buf_len;
293
294  next_state_ = STATE_DATA_READ;
295  int rv = DoLoop(OK);
296  if (rv == ERR_IO_PENDING)
297    user_callback_ = callback;
298  return rv;
299}
300
301const FtpResponseInfo* FtpNetworkTransaction::GetResponseInfo() const {
302  return &response_;
303}
304
305LoadState FtpNetworkTransaction::GetLoadState() const {
306  if (next_state_ == STATE_CTRL_RESOLVE_HOST_COMPLETE)
307    return LOAD_STATE_RESOLVING_HOST;
308
309  if (next_state_ == STATE_CTRL_CONNECT_COMPLETE ||
310      next_state_ == STATE_DATA_CONNECT_COMPLETE)
311    return LOAD_STATE_CONNECTING;
312
313  if (next_state_ == STATE_DATA_READ_COMPLETE)
314    return LOAD_STATE_READING_RESPONSE;
315
316  if (command_sent_ == COMMAND_RETR && read_data_buf_.get())
317    return LOAD_STATE_READING_RESPONSE;
318
319  if (command_sent_ == COMMAND_QUIT)
320    return LOAD_STATE_IDLE;
321
322  if (command_sent_ != COMMAND_NONE)
323    return LOAD_STATE_SENDING_REQUEST;
324
325  return LOAD_STATE_IDLE;
326}
327
328uint64 FtpNetworkTransaction::GetUploadProgress() const {
329  return 0;
330}
331
332void FtpNetworkTransaction::ResetStateForRestart() {
333  command_sent_ = COMMAND_NONE;
334  user_callback_.Reset();
335  response_ = FtpResponseInfo();
336  read_ctrl_buf_ = new IOBuffer(kCtrlBufLen);
337  ctrl_response_buffer_.reset(new FtpCtrlResponseBuffer(net_log_));
338  read_data_buf_ = NULL;
339  read_data_buf_len_ = 0;
340  if (write_buf_.get())
341    write_buf_->SetOffset(0);
342  last_error_ = OK;
343  data_connection_port_ = 0;
344  ctrl_socket_.reset();
345  data_socket_.reset();
346  next_state_ = STATE_NONE;
347  state_after_data_connect_complete_ = STATE_CTRL_WRITE_SIZE;
348}
349
350void FtpNetworkTransaction::ResetDataConnectionAfterError(State next_state) {
351  // The server _might_ have reset the data connection
352  // (see RFC 959 3.2. ESTABLISHING DATA CONNECTIONS:
353  // "The server MUST close the data connection under the following
354  // conditions:
355  // ...
356  // 5. An irrecoverable error condition occurs.")
357  //
358  // It is ambiguous what an irrecoverable error condition is,
359  // so we take no chances.
360  state_after_data_connect_complete_ = next_state;
361  next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV;
362}
363
364void FtpNetworkTransaction::DoCallback(int rv) {
365  DCHECK(rv != ERR_IO_PENDING);
366  DCHECK(!user_callback_.is_null());
367
368  // Since Run may result in Read being called, clear callback_ up front.
369  CompletionCallback c = user_callback_;
370  user_callback_.Reset();
371  c.Run(rv);
372}
373
374void FtpNetworkTransaction::OnIOComplete(int result) {
375  int rv = DoLoop(result);
376  if (rv != ERR_IO_PENDING)
377    DoCallback(rv);
378}
379
380int FtpNetworkTransaction::ProcessCtrlResponse() {
381  FtpCtrlResponse response = ctrl_response_buffer_->PopResponse();
382
383  int rv = OK;
384  switch (command_sent_) {
385    case COMMAND_NONE:
386      // TODO(phajdan.jr): Check for errors in the welcome message.
387      next_state_ = STATE_CTRL_WRITE_USER;
388      break;
389    case COMMAND_USER:
390      rv = ProcessResponseUSER(response);
391      break;
392    case COMMAND_PASS:
393      rv = ProcessResponsePASS(response);
394      break;
395    case COMMAND_SYST:
396      rv = ProcessResponseSYST(response);
397      break;
398    case COMMAND_PWD:
399      rv = ProcessResponsePWD(response);
400      break;
401    case COMMAND_TYPE:
402      rv = ProcessResponseTYPE(response);
403      break;
404    case COMMAND_EPSV:
405      rv = ProcessResponseEPSV(response);
406      break;
407    case COMMAND_PASV:
408      rv = ProcessResponsePASV(response);
409      break;
410    case COMMAND_SIZE:
411      rv = ProcessResponseSIZE(response);
412      break;
413    case COMMAND_RETR:
414      rv = ProcessResponseRETR(response);
415      break;
416    case COMMAND_CWD:
417      rv = ProcessResponseCWD(response);
418      break;
419    case COMMAND_LIST:
420      rv = ProcessResponseLIST(response);
421      break;
422    case COMMAND_QUIT:
423      rv = ProcessResponseQUIT(response);
424      break;
425    default:
426      LOG(DFATAL) << "Unexpected value of command_sent_: " << command_sent_;
427      return ERR_UNEXPECTED;
428  }
429
430  // We may get multiple responses for some commands,
431  // see http://crbug.com/18036.
432  while (ctrl_response_buffer_->ResponseAvailable() && rv == OK) {
433    response = ctrl_response_buffer_->PopResponse();
434
435    switch (command_sent_) {
436      case COMMAND_RETR:
437        rv = ProcessResponseRETR(response);
438        break;
439      case COMMAND_LIST:
440        rv = ProcessResponseLIST(response);
441        break;
442      default:
443        // Multiple responses for other commands are invalid.
444        return Stop(ERR_INVALID_RESPONSE);
445    }
446  }
447
448  return rv;
449}
450
451// Used to prepare and send FTP command.
452int FtpNetworkTransaction::SendFtpCommand(const std::string& command,
453                                          const std::string& command_for_log,
454                                          Command cmd) {
455  // If we send a new command when we still have unprocessed responses
456  // for previous commands, the response receiving code will have no way to know
457  // which responses are for which command.
458  DCHECK(!ctrl_response_buffer_->ResponseAvailable());
459
460  DCHECK(!write_command_buf_.get());
461  DCHECK(!write_buf_.get());
462
463  if (!IsValidFTPCommandString(command)) {
464    // Callers should validate the command themselves and return a more specific
465    // error code.
466    NOTREACHED();
467    return Stop(ERR_UNEXPECTED);
468  }
469
470  command_sent_ = cmd;
471
472  write_command_buf_ = new IOBufferWithSize(command.length() + 2);
473  write_buf_ = new DrainableIOBuffer(write_command_buf_.get(),
474                                     write_command_buf_->size());
475  memcpy(write_command_buf_->data(), command.data(), command.length());
476  memcpy(write_command_buf_->data() + command.length(), kCRLF, 2);
477
478  net_log_.AddEvent(NetLog::TYPE_FTP_COMMAND_SENT,
479                    NetLog::StringCallback("command", &command_for_log));
480
481  next_state_ = STATE_CTRL_WRITE;
482  return OK;
483}
484
485std::string FtpNetworkTransaction::GetRequestPathForFtpCommand(
486    bool is_directory) const {
487  std::string path(current_remote_directory_);
488  if (request_->url.has_path()) {
489    std::string gurl_path(request_->url.path());
490
491    // Get rid of the typecode, see RFC 1738 section 3.2.2. FTP url-path.
492    std::string::size_type pos = gurl_path.rfind(';');
493    if (pos != std::string::npos)
494      gurl_path.resize(pos);
495
496    path.append(gurl_path);
497  }
498  // Make sure that if the path is expected to be a file, it won't end
499  // with a trailing slash.
500  if (!is_directory && path.length() > 1 && path[path.length() - 1] == '/')
501    path.erase(path.length() - 1);
502  UnescapeRule::Type unescape_rules = UnescapeRule::SPACES |
503                                      UnescapeRule::URL_SPECIAL_CHARS;
504  // This may unescape to non-ASCII characters, but we allow that. See the
505  // comment for IsValidFTPCommandString.
506  path = net::UnescapeURLComponent(path, unescape_rules);
507
508  if (system_type_ == SYSTEM_TYPE_VMS) {
509    if (is_directory)
510      path = FtpUtil::UnixDirectoryPathToVMS(path);
511    else
512      path = FtpUtil::UnixFilePathToVMS(path);
513  }
514
515  DCHECK(IsValidFTPCommandString(path));
516  return path;
517}
518
519void FtpNetworkTransaction::DetectTypecode() {
520  if (!request_->url.has_path())
521    return;
522  std::string gurl_path(request_->url.path());
523
524  // Extract the typecode, see RFC 1738 section 3.2.2. FTP url-path.
525  std::string::size_type pos = gurl_path.rfind(';');
526  if (pos == std::string::npos)
527    return;
528  std::string typecode_string(gurl_path.substr(pos));
529  if (typecode_string == ";type=a") {
530    data_type_ = DATA_TYPE_ASCII;
531    resource_type_ = RESOURCE_TYPE_FILE;
532  } else if (typecode_string == ";type=i") {
533    data_type_ = DATA_TYPE_IMAGE;
534    resource_type_ = RESOURCE_TYPE_FILE;
535  } else if (typecode_string == ";type=d") {
536    resource_type_ = RESOURCE_TYPE_DIRECTORY;
537  }
538}
539
540int FtpNetworkTransaction::DoLoop(int result) {
541  DCHECK(next_state_ != STATE_NONE);
542
543  int rv = result;
544  do {
545    State state = next_state_;
546    next_state_ = STATE_NONE;
547    switch (state) {
548      case STATE_CTRL_RESOLVE_HOST:
549        DCHECK(rv == OK);
550        rv = DoCtrlResolveHost();
551        break;
552      case STATE_CTRL_RESOLVE_HOST_COMPLETE:
553        rv = DoCtrlResolveHostComplete(rv);
554        break;
555      case STATE_CTRL_CONNECT:
556        DCHECK(rv == OK);
557        rv = DoCtrlConnect();
558        break;
559      case STATE_CTRL_CONNECT_COMPLETE:
560        rv = DoCtrlConnectComplete(rv);
561        break;
562      case STATE_CTRL_READ:
563        DCHECK(rv == OK);
564        rv = DoCtrlRead();
565        break;
566      case STATE_CTRL_READ_COMPLETE:
567        rv = DoCtrlReadComplete(rv);
568        break;
569      case STATE_CTRL_WRITE:
570        DCHECK(rv == OK);
571        rv = DoCtrlWrite();
572        break;
573      case STATE_CTRL_WRITE_COMPLETE:
574        rv = DoCtrlWriteComplete(rv);
575        break;
576      case STATE_CTRL_WRITE_USER:
577        DCHECK(rv == OK);
578        rv = DoCtrlWriteUSER();
579        break;
580      case STATE_CTRL_WRITE_PASS:
581        DCHECK(rv == OK);
582        rv = DoCtrlWritePASS();
583        break;
584      case STATE_CTRL_WRITE_SYST:
585        DCHECK(rv == OK);
586        rv = DoCtrlWriteSYST();
587        break;
588      case STATE_CTRL_WRITE_PWD:
589        DCHECK(rv == OK);
590        rv = DoCtrlWritePWD();
591        break;
592      case STATE_CTRL_WRITE_TYPE:
593        DCHECK(rv == OK);
594        rv = DoCtrlWriteTYPE();
595        break;
596      case STATE_CTRL_WRITE_EPSV:
597        DCHECK(rv == OK);
598        rv = DoCtrlWriteEPSV();
599        break;
600      case STATE_CTRL_WRITE_PASV:
601        DCHECK(rv == OK);
602        rv = DoCtrlWritePASV();
603        break;
604      case STATE_CTRL_WRITE_RETR:
605        DCHECK(rv == OK);
606        rv = DoCtrlWriteRETR();
607        break;
608      case STATE_CTRL_WRITE_SIZE:
609        DCHECK(rv == OK);
610        rv = DoCtrlWriteSIZE();
611        break;
612      case STATE_CTRL_WRITE_CWD:
613        DCHECK(rv == OK);
614        rv = DoCtrlWriteCWD();
615        break;
616      case STATE_CTRL_WRITE_LIST:
617        DCHECK(rv == OK);
618        rv = DoCtrlWriteLIST();
619        break;
620      case STATE_CTRL_WRITE_QUIT:
621        DCHECK(rv == OK);
622        rv = DoCtrlWriteQUIT();
623        break;
624      case STATE_DATA_CONNECT:
625        DCHECK(rv == OK);
626        rv = DoDataConnect();
627        break;
628      case STATE_DATA_CONNECT_COMPLETE:
629        rv = DoDataConnectComplete(rv);
630        break;
631      case STATE_DATA_READ:
632        DCHECK(rv == OK);
633        rv = DoDataRead();
634        break;
635      case STATE_DATA_READ_COMPLETE:
636        rv = DoDataReadComplete(rv);
637        break;
638      default:
639        NOTREACHED() << "bad state";
640        rv = ERR_UNEXPECTED;
641        break;
642    }
643  } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
644  return rv;
645}
646
647int FtpNetworkTransaction::DoCtrlResolveHost() {
648  next_state_ = STATE_CTRL_RESOLVE_HOST_COMPLETE;
649
650  HostResolver::RequestInfo info(HostPortPair::FromURL(request_->url));
651  // No known referrer.
652  return resolver_.Resolve(
653      info, &addresses_,
654      base::Bind(&FtpNetworkTransaction::OnIOComplete, base::Unretained(this)),
655      net_log_);
656}
657
658int FtpNetworkTransaction::DoCtrlResolveHostComplete(int result) {
659  if (result == OK)
660    next_state_ = STATE_CTRL_CONNECT;
661  return result;
662}
663
664int FtpNetworkTransaction::DoCtrlConnect() {
665  next_state_ = STATE_CTRL_CONNECT_COMPLETE;
666  ctrl_socket_.reset(socket_factory_->CreateTransportClientSocket(
667        addresses_, net_log_.net_log(), net_log_.source()));
668  net_log_.AddEvent(
669      NetLog::TYPE_FTP_CONTROL_CONNECTION,
670      ctrl_socket_->NetLog().source().ToEventParametersCallback());
671  return ctrl_socket_->Connect(io_callback_);
672}
673
674int FtpNetworkTransaction::DoCtrlConnectComplete(int result) {
675  if (result == OK) {
676    // Put the peer's IP address and port into the response.
677    IPEndPoint ip_endpoint;
678    result = ctrl_socket_->GetPeerAddress(&ip_endpoint);
679    if (result == OK) {
680      response_.socket_address = HostPortPair::FromIPEndPoint(ip_endpoint);
681      next_state_ = STATE_CTRL_READ;
682
683      if (ip_endpoint.GetFamily() == ADDRESS_FAMILY_IPV4) {
684        // Do not use EPSV for IPv4 connections. Some servers become confused
685        // and we time out while waiting to connect. PASV is perfectly fine for
686        // IPv4. Note that this blacklists IPv4 not to use EPSV instead of
687        // whitelisting IPv6 to use it, to make the code more future-proof:
688        // all future protocols should just use EPSV.
689        use_epsv_ = false;
690      }
691    }
692  }
693  return result;
694}
695
696int FtpNetworkTransaction::DoCtrlRead() {
697  next_state_ = STATE_CTRL_READ_COMPLETE;
698  return ctrl_socket_->Read(read_ctrl_buf_.get(), kCtrlBufLen, io_callback_);
699}
700
701int FtpNetworkTransaction::DoCtrlReadComplete(int result) {
702  if (result == 0) {
703    // Some servers (for example Pure-FTPd) apparently close the control
704    // connection when anonymous login is not permitted. For more details
705    // see http://crbug.com/25023.
706    if (command_sent_ == COMMAND_USER &&
707        credentials_.username() == ASCIIToUTF16("anonymous")) {
708      response_.needs_auth = true;
709    }
710    return Stop(ERR_EMPTY_RESPONSE);
711  }
712  if (result < 0)
713    return Stop(result);
714
715  ctrl_response_buffer_->ConsumeData(read_ctrl_buf_->data(), result);
716
717  if (!ctrl_response_buffer_->ResponseAvailable()) {
718    // Read more data from the control socket.
719    next_state_ = STATE_CTRL_READ;
720    return OK;
721  }
722
723  return ProcessCtrlResponse();
724}
725
726int FtpNetworkTransaction::DoCtrlWrite() {
727  next_state_ = STATE_CTRL_WRITE_COMPLETE;
728
729  return ctrl_socket_->Write(
730      write_buf_.get(), write_buf_->BytesRemaining(), io_callback_);
731}
732
733int FtpNetworkTransaction::DoCtrlWriteComplete(int result) {
734  if (result < 0)
735    return result;
736
737  write_buf_->DidConsume(result);
738  if (write_buf_->BytesRemaining() == 0) {
739    // Clear the write buffer.
740    write_buf_ = NULL;
741    write_command_buf_ = NULL;
742
743    next_state_ = STATE_CTRL_READ;
744  } else {
745    next_state_ = STATE_CTRL_WRITE;
746  }
747  return OK;
748}
749
750// FTP Commands and responses
751
752// USER Command.
753int FtpNetworkTransaction::DoCtrlWriteUSER() {
754  std::string command = "USER " + UTF16ToUTF8(credentials_.username());
755
756  if (!IsValidFTPCommandString(command))
757    return Stop(ERR_MALFORMED_IDENTITY);
758
759  next_state_ = STATE_CTRL_READ;
760  return SendFtpCommand(command, "USER ***", COMMAND_USER);
761}
762
763int FtpNetworkTransaction::ProcessResponseUSER(
764    const FtpCtrlResponse& response) {
765  switch (GetErrorClass(response.status_code)) {
766    case ERROR_CLASS_OK:
767      next_state_ = STATE_CTRL_WRITE_SYST;
768      break;
769    case ERROR_CLASS_INFO_NEEDED:
770      next_state_ = STATE_CTRL_WRITE_PASS;
771      break;
772    case ERROR_CLASS_TRANSIENT_ERROR:
773    case ERROR_CLASS_PERMANENT_ERROR:
774      response_.needs_auth = true;
775      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
776    default:
777      NOTREACHED();
778      return Stop(ERR_UNEXPECTED);
779  }
780  return OK;
781}
782
783// PASS command.
784int FtpNetworkTransaction::DoCtrlWritePASS() {
785  std::string command = "PASS " + UTF16ToUTF8(credentials_.password());
786
787  if (!IsValidFTPCommandString(command))
788    return Stop(ERR_MALFORMED_IDENTITY);
789
790  next_state_ = STATE_CTRL_READ;
791  return SendFtpCommand(command, "PASS ***", COMMAND_PASS);
792}
793
794int FtpNetworkTransaction::ProcessResponsePASS(
795    const FtpCtrlResponse& response) {
796  switch (GetErrorClass(response.status_code)) {
797    case ERROR_CLASS_OK:
798      next_state_ = STATE_CTRL_WRITE_SYST;
799      break;
800    case ERROR_CLASS_INFO_NEEDED:
801      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
802    case ERROR_CLASS_TRANSIENT_ERROR:
803    case ERROR_CLASS_PERMANENT_ERROR:
804      response_.needs_auth = true;
805      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
806    default:
807      NOTREACHED();
808      return Stop(ERR_UNEXPECTED);
809  }
810  return OK;
811}
812
813// SYST command.
814int FtpNetworkTransaction::DoCtrlWriteSYST() {
815  std::string command = "SYST";
816  next_state_ = STATE_CTRL_READ;
817  return SendFtpCommand(command, command, COMMAND_SYST);
818}
819
820int FtpNetworkTransaction::ProcessResponseSYST(
821    const FtpCtrlResponse& response) {
822  switch (GetErrorClass(response.status_code)) {
823    case ERROR_CLASS_INITIATED:
824      return Stop(ERR_INVALID_RESPONSE);
825    case ERROR_CLASS_OK: {
826      // All important info should be on the first line.
827      std::string line = response.lines[0];
828      // The response should be ASCII, which allows us to do case-insensitive
829      // comparisons easily. If it is not ASCII, we leave the system type
830      // as unknown.
831      if (IsStringASCII(line)) {
832        line = StringToLowerASCII(line);
833
834        // Remove all whitespace, to correctly handle cases like fancy "V M S"
835        // response instead of "VMS".
836        RemoveChars(line, kWhitespaceASCII, &line);
837
838        // The "magic" strings we test for below have been gathered by an
839        // empirical study. VMS needs to come first because some VMS systems
840        // also respond with "UNIX emulation", which is not perfect. It is much
841        // more reliable to talk to these servers in their native language.
842        if (line.find("vms") != std::string::npos) {
843          system_type_ = SYSTEM_TYPE_VMS;
844        } else if (line.find("l8") != std::string::npos ||
845                   line.find("unix") != std::string::npos ||
846                   line.find("bsd") != std::string::npos) {
847          system_type_ = SYSTEM_TYPE_UNIX;
848        } else if (line.find("win32") != std::string::npos ||
849                   line.find("windows") != std::string::npos) {
850          system_type_ = SYSTEM_TYPE_WINDOWS;
851        } else if (line.find("os/2") != std::string::npos) {
852          system_type_ = SYSTEM_TYPE_OS2;
853        }
854      }
855      next_state_ = STATE_CTRL_WRITE_PWD;
856      break;
857    }
858    case ERROR_CLASS_INFO_NEEDED:
859      return Stop(ERR_INVALID_RESPONSE);
860    case ERROR_CLASS_TRANSIENT_ERROR:
861      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
862    case ERROR_CLASS_PERMANENT_ERROR:
863      // Server does not recognize the SYST command so proceed.
864      next_state_ = STATE_CTRL_WRITE_PWD;
865      break;
866    default:
867      NOTREACHED();
868      return Stop(ERR_UNEXPECTED);
869  }
870  return OK;
871}
872
873// PWD command.
874int FtpNetworkTransaction::DoCtrlWritePWD() {
875  std::string command = "PWD";
876  next_state_ = STATE_CTRL_READ;
877  return SendFtpCommand(command, command, COMMAND_PWD);
878}
879
880int FtpNetworkTransaction::ProcessResponsePWD(const FtpCtrlResponse& response) {
881  switch (GetErrorClass(response.status_code)) {
882    case ERROR_CLASS_INITIATED:
883      return Stop(ERR_INVALID_RESPONSE);
884    case ERROR_CLASS_OK: {
885      // The info we look for should be on the first line.
886      std::string line = response.lines[0];
887      if (line.empty())
888        return Stop(ERR_INVALID_RESPONSE);
889      std::string::size_type quote_pos = line.find('"');
890      if (quote_pos != std::string::npos) {
891        line = line.substr(quote_pos + 1);
892        quote_pos = line.find('"');
893        if (quote_pos == std::string::npos)
894          return Stop(ERR_INVALID_RESPONSE);
895        line = line.substr(0, quote_pos);
896      }
897      if (system_type_ == SYSTEM_TYPE_VMS)
898        line = FtpUtil::VMSPathToUnix(line);
899      if (line.length() && line[line.length() - 1] == '/')
900        line.erase(line.length() - 1);
901      current_remote_directory_ = line;
902      next_state_ = STATE_CTRL_WRITE_TYPE;
903      break;
904    }
905    case ERROR_CLASS_INFO_NEEDED:
906      return Stop(ERR_INVALID_RESPONSE);
907    case ERROR_CLASS_TRANSIENT_ERROR:
908      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
909    case ERROR_CLASS_PERMANENT_ERROR:
910      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
911    default:
912      NOTREACHED();
913      return Stop(ERR_UNEXPECTED);
914  }
915  return OK;
916}
917
918// TYPE command.
919int FtpNetworkTransaction::DoCtrlWriteTYPE() {
920  std::string command = "TYPE ";
921  if (data_type_ == DATA_TYPE_ASCII) {
922    command += "A";
923  } else if (data_type_ == DATA_TYPE_IMAGE) {
924    command += "I";
925  } else {
926    NOTREACHED();
927    return Stop(ERR_UNEXPECTED);
928  }
929  next_state_ = STATE_CTRL_READ;
930  return SendFtpCommand(command, command, COMMAND_TYPE);
931}
932
933int FtpNetworkTransaction::ProcessResponseTYPE(
934    const FtpCtrlResponse& response) {
935  switch (GetErrorClass(response.status_code)) {
936    case ERROR_CLASS_INITIATED:
937      return Stop(ERR_INVALID_RESPONSE);
938    case ERROR_CLASS_OK:
939      next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV;
940      break;
941    case ERROR_CLASS_INFO_NEEDED:
942      return Stop(ERR_INVALID_RESPONSE);
943    case ERROR_CLASS_TRANSIENT_ERROR:
944      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
945    case ERROR_CLASS_PERMANENT_ERROR:
946      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
947    default:
948      NOTREACHED();
949      return Stop(ERR_UNEXPECTED);
950  }
951  return OK;
952}
953
954// EPSV command
955int FtpNetworkTransaction::DoCtrlWriteEPSV() {
956  const std::string command = "EPSV";
957  next_state_ = STATE_CTRL_READ;
958  return SendFtpCommand(command, command, COMMAND_EPSV);
959}
960
961int FtpNetworkTransaction::ProcessResponseEPSV(
962    const FtpCtrlResponse& response) {
963  switch (GetErrorClass(response.status_code)) {
964    case ERROR_CLASS_INITIATED:
965      return Stop(ERR_INVALID_RESPONSE);
966    case ERROR_CLASS_OK:
967      if (!ExtractPortFromEPSVResponse( response, &data_connection_port_))
968        return Stop(ERR_INVALID_RESPONSE);
969      if (data_connection_port_ < 1024 ||
970          !IsPortAllowedByFtp(data_connection_port_))
971        return Stop(ERR_UNSAFE_PORT);
972      next_state_ = STATE_DATA_CONNECT;
973      break;
974    case ERROR_CLASS_INFO_NEEDED:
975      return Stop(ERR_INVALID_RESPONSE);
976    case ERROR_CLASS_TRANSIENT_ERROR:
977    case ERROR_CLASS_PERMANENT_ERROR:
978      use_epsv_ = false;
979      next_state_ = STATE_CTRL_WRITE_PASV;
980      return OK;
981    default:
982      NOTREACHED();
983      return Stop(ERR_UNEXPECTED);
984  }
985  return OK;
986}
987
988// PASV command
989int FtpNetworkTransaction::DoCtrlWritePASV() {
990  std::string command = "PASV";
991  next_state_ = STATE_CTRL_READ;
992  return SendFtpCommand(command, command, COMMAND_PASV);
993}
994
995int FtpNetworkTransaction::ProcessResponsePASV(
996    const FtpCtrlResponse& response) {
997  switch (GetErrorClass(response.status_code)) {
998    case ERROR_CLASS_INITIATED:
999      return Stop(ERR_INVALID_RESPONSE);
1000    case ERROR_CLASS_OK:
1001      if (!ExtractPortFromPASVResponse(response, &data_connection_port_))
1002        return Stop(ERR_INVALID_RESPONSE);
1003      if (data_connection_port_ < 1024 ||
1004          !IsPortAllowedByFtp(data_connection_port_))
1005        return Stop(ERR_UNSAFE_PORT);
1006      next_state_ = STATE_DATA_CONNECT;
1007      break;
1008    case ERROR_CLASS_INFO_NEEDED:
1009      return Stop(ERR_INVALID_RESPONSE);
1010    case ERROR_CLASS_TRANSIENT_ERROR:
1011      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1012    case ERROR_CLASS_PERMANENT_ERROR:
1013      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1014    default:
1015      NOTREACHED();
1016      return Stop(ERR_UNEXPECTED);
1017  }
1018  return OK;
1019}
1020
1021// RETR command
1022int FtpNetworkTransaction::DoCtrlWriteRETR() {
1023  std::string command = "RETR " + GetRequestPathForFtpCommand(false);
1024  next_state_ = STATE_CTRL_READ;
1025  return SendFtpCommand(command, command, COMMAND_RETR);
1026}
1027
1028int FtpNetworkTransaction::ProcessResponseRETR(
1029    const FtpCtrlResponse& response) {
1030  switch (GetErrorClass(response.status_code)) {
1031    case ERROR_CLASS_INITIATED:
1032      // We want the client to start reading the response at this point.
1033      // It got here either through Start or RestartWithAuth. We want that
1034      // method to complete. Not setting next state here will make DoLoop exit
1035      // and in turn make Start/RestartWithAuth complete.
1036      resource_type_ = RESOURCE_TYPE_FILE;
1037      break;
1038    case ERROR_CLASS_OK:
1039      resource_type_ = RESOURCE_TYPE_FILE;
1040      next_state_ = STATE_CTRL_WRITE_QUIT;
1041      break;
1042    case ERROR_CLASS_INFO_NEEDED:
1043      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1044    case ERROR_CLASS_TRANSIENT_ERROR:
1045      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1046    case ERROR_CLASS_PERMANENT_ERROR:
1047      // Code 550 means "Failed to open file". Other codes are unrelated,
1048      // like "Not logged in" etc.
1049      if (response.status_code != 550 || resource_type_ == RESOURCE_TYPE_FILE)
1050        return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1051
1052      // It's possible that RETR failed because the path is a directory.
1053      resource_type_ = RESOURCE_TYPE_DIRECTORY;
1054
1055      // We're going to try CWD next, but first send a PASV one more time,
1056      // because some FTP servers, including FileZilla, require that.
1057      // See http://crbug.com/25316.
1058      next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV;
1059      break;
1060    default:
1061      NOTREACHED();
1062      return Stop(ERR_UNEXPECTED);
1063  }
1064
1065  // We should be sure about our resource type now. Otherwise we risk
1066  // an infinite loop (RETR can later send CWD, and CWD can later send RETR).
1067  DCHECK_NE(RESOURCE_TYPE_UNKNOWN, resource_type_);
1068
1069  return OK;
1070}
1071
1072// SIZE command
1073int FtpNetworkTransaction::DoCtrlWriteSIZE() {
1074  std::string command = "SIZE " + GetRequestPathForFtpCommand(false);
1075  next_state_ = STATE_CTRL_READ;
1076  return SendFtpCommand(command, command, COMMAND_SIZE);
1077}
1078
1079int FtpNetworkTransaction::ProcessResponseSIZE(
1080    const FtpCtrlResponse& response) {
1081  State state_after_size;
1082  if (resource_type_ == RESOURCE_TYPE_FILE)
1083    state_after_size = STATE_CTRL_WRITE_RETR;
1084  else
1085    state_after_size = STATE_CTRL_WRITE_CWD;
1086
1087  switch (GetErrorClass(response.status_code)) {
1088    case ERROR_CLASS_INITIATED:
1089      next_state_ = state_after_size;
1090      break;
1091    case ERROR_CLASS_OK:
1092      if (response.lines.size() != 1)
1093        return Stop(ERR_INVALID_RESPONSE);
1094      int64 size;
1095      if (!base::StringToInt64(response.lines[0], &size))
1096        return Stop(ERR_INVALID_RESPONSE);
1097      if (size < 0)
1098        return Stop(ERR_INVALID_RESPONSE);
1099
1100      // A successful response to SIZE does not mean the resource is a file.
1101      // Some FTP servers (for example, the qnx one) send a SIZE even for
1102      // directories.
1103      response_.expected_content_size = size;
1104
1105      next_state_ = state_after_size;
1106      break;
1107    case ERROR_CLASS_INFO_NEEDED:
1108      next_state_ = state_after_size;
1109      break;
1110    case ERROR_CLASS_TRANSIENT_ERROR:
1111      ResetDataConnectionAfterError(state_after_size);
1112      break;
1113    case ERROR_CLASS_PERMANENT_ERROR:
1114      // It's possible that SIZE failed because the path is a directory.
1115      if (resource_type_ == RESOURCE_TYPE_UNKNOWN &&
1116          response.status_code != 550) {
1117        return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1118      }
1119
1120      ResetDataConnectionAfterError(state_after_size);
1121      break;
1122    default:
1123      NOTREACHED();
1124      return Stop(ERR_UNEXPECTED);
1125  }
1126
1127  return OK;
1128}
1129
1130// CWD command
1131int FtpNetworkTransaction::DoCtrlWriteCWD() {
1132  std::string command = "CWD " + GetRequestPathForFtpCommand(true);
1133  next_state_ = STATE_CTRL_READ;
1134  return SendFtpCommand(command, command, COMMAND_CWD);
1135}
1136
1137int FtpNetworkTransaction::ProcessResponseCWD(const FtpCtrlResponse& response) {
1138  // We should never issue CWD if we know the target resource is a file.
1139  DCHECK_NE(RESOURCE_TYPE_FILE, resource_type_);
1140
1141  switch (GetErrorClass(response.status_code)) {
1142    case ERROR_CLASS_INITIATED:
1143      return Stop(ERR_INVALID_RESPONSE);
1144    case ERROR_CLASS_OK:
1145      next_state_ = STATE_CTRL_WRITE_LIST;
1146      break;
1147    case ERROR_CLASS_INFO_NEEDED:
1148      return Stop(ERR_INVALID_RESPONSE);
1149    case ERROR_CLASS_TRANSIENT_ERROR:
1150      // Some FTP servers send response 451 (not a valid CWD response according
1151      // to RFC 959) instead of 550.
1152      if (response.status_code == 451)
1153        return ProcessResponseCWDNotADirectory();
1154
1155      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1156    case ERROR_CLASS_PERMANENT_ERROR:
1157      if (response.status_code == 550)
1158        return ProcessResponseCWDNotADirectory();
1159
1160      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1161    default:
1162      NOTREACHED();
1163      return Stop(ERR_UNEXPECTED);
1164  }
1165
1166  return OK;
1167}
1168
1169int FtpNetworkTransaction::ProcessResponseCWDNotADirectory() {
1170  if (resource_type_ == RESOURCE_TYPE_DIRECTORY) {
1171    // We're assuming that the resource is a directory, but the server
1172    // says it's not true. The most probable interpretation is that it
1173    // doesn't exist (with FTP we can't be sure).
1174    return Stop(ERR_FILE_NOT_FOUND);
1175  }
1176
1177  // We are here because SIZE failed and we are not sure what the resource
1178  // type is. It could still be file, and SIZE could fail because of
1179  // an access error (http://crbug.com/56734). Try RETR just to be sure.
1180  resource_type_ = RESOURCE_TYPE_FILE;
1181
1182  ResetDataConnectionAfterError(STATE_CTRL_WRITE_RETR);
1183  return OK;
1184}
1185
1186// LIST command
1187int FtpNetworkTransaction::DoCtrlWriteLIST() {
1188  // Use the -l option for mod_ftp configured in LISTIsNLST mode: the option
1189  // forces LIST output instead of NLST (which would be ambiguous for us
1190  // to parse).
1191  std::string command("LIST -l");
1192  if (system_type_ == SYSTEM_TYPE_VMS)
1193    command = "LIST *.*;0";
1194
1195  next_state_ = STATE_CTRL_READ;
1196  return SendFtpCommand(command, command, COMMAND_LIST);
1197}
1198
1199int FtpNetworkTransaction::ProcessResponseLIST(
1200    const FtpCtrlResponse& response) {
1201  switch (GetErrorClass(response.status_code)) {
1202    case ERROR_CLASS_INITIATED:
1203      // We want the client to start reading the response at this point.
1204      // It got here either through Start or RestartWithAuth. We want that
1205      // method to complete. Not setting next state here will make DoLoop exit
1206      // and in turn make Start/RestartWithAuth complete.
1207      response_.is_directory_listing = true;
1208      break;
1209    case ERROR_CLASS_OK:
1210      response_.is_directory_listing = true;
1211      next_state_ = STATE_CTRL_WRITE_QUIT;
1212      break;
1213    case ERROR_CLASS_INFO_NEEDED:
1214      return Stop(ERR_INVALID_RESPONSE);
1215    case ERROR_CLASS_TRANSIENT_ERROR:
1216      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1217    case ERROR_CLASS_PERMANENT_ERROR:
1218      return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1219    default:
1220      NOTREACHED();
1221      return Stop(ERR_UNEXPECTED);
1222  }
1223  return OK;
1224}
1225
1226// QUIT command
1227int FtpNetworkTransaction::DoCtrlWriteQUIT() {
1228  std::string command = "QUIT";
1229  next_state_ = STATE_CTRL_READ;
1230  return SendFtpCommand(command, command, COMMAND_QUIT);
1231}
1232
1233int FtpNetworkTransaction::ProcessResponseQUIT(
1234    const FtpCtrlResponse& response) {
1235  ctrl_socket_->Disconnect();
1236  return last_error_;
1237}
1238
1239// Data Connection
1240
1241int FtpNetworkTransaction::DoDataConnect() {
1242  next_state_ = STATE_DATA_CONNECT_COMPLETE;
1243  IPEndPoint ip_endpoint;
1244  AddressList data_address;
1245  // Connect to the same host as the control socket to prevent PASV port
1246  // scanning attacks.
1247  int rv = ctrl_socket_->GetPeerAddress(&ip_endpoint);
1248  if (rv != OK)
1249    return Stop(rv);
1250  data_address = AddressList::CreateFromIPAddress(
1251      ip_endpoint.address(), data_connection_port_);
1252  data_socket_.reset(socket_factory_->CreateTransportClientSocket(
1253        data_address, net_log_.net_log(), net_log_.source()));
1254  net_log_.AddEvent(
1255      NetLog::TYPE_FTP_DATA_CONNECTION,
1256      data_socket_->NetLog().source().ToEventParametersCallback());
1257  return data_socket_->Connect(io_callback_);
1258}
1259
1260int FtpNetworkTransaction::DoDataConnectComplete(int result) {
1261  if (result != OK && use_epsv_) {
1262    // It's possible we hit a broken server, sadly. They can break in different
1263    // ways. Some time out, some reset a connection. Fall back to PASV.
1264    // TODO(phajdan.jr): remember it for future transactions with this server.
1265    // TODO(phajdan.jr): write a test for this code path.
1266    use_epsv_ = false;
1267    next_state_ = STATE_CTRL_WRITE_PASV;
1268    return OK;
1269  }
1270
1271  // Only record the connection error after we've applied all our fallbacks.
1272  // We want to capture the final error, one we're not going to recover from.
1273  RecordDataConnectionError(result);
1274
1275  if (result != OK)
1276    return Stop(result);
1277
1278  next_state_ = state_after_data_connect_complete_;
1279  return OK;
1280}
1281
1282int FtpNetworkTransaction::DoDataRead() {
1283  DCHECK(read_data_buf_.get());
1284  DCHECK_GT(read_data_buf_len_, 0);
1285
1286  if (data_socket_ == NULL || !data_socket_->IsConnected()) {
1287    // If we don't destroy the data socket completely, some servers will wait
1288    // for us (http://crbug.com/21127). The half-closed TCP connection needs
1289    // to be closed on our side too.
1290    data_socket_.reset();
1291
1292    if (ctrl_socket_->IsConnected()) {
1293      // Wait for the server's response, we should get it before sending QUIT.
1294      next_state_ = STATE_CTRL_READ;
1295      return OK;
1296    }
1297
1298    // We are no longer connected to the server, so just finish the transaction.
1299    return Stop(OK);
1300  }
1301
1302  next_state_ = STATE_DATA_READ_COMPLETE;
1303  read_data_buf_->data()[0] = 0;
1304  return data_socket_->Read(
1305      read_data_buf_.get(), read_data_buf_len_, io_callback_);
1306}
1307
1308int FtpNetworkTransaction::DoDataReadComplete(int result) {
1309  return result;
1310}
1311
1312// We're using a histogram as a group of counters, with one bucket for each
1313// enumeration value.  We're only interested in the values of the counters.
1314// Ignore the shape, average, and standard deviation of the histograms because
1315// they are meaningless.
1316//
1317// We use two histograms.  In the first histogram we tally whether the user has
1318// seen an error of that type during the session.  In the second histogram we
1319// tally the total number of times the users sees each errer.
1320void FtpNetworkTransaction::RecordDataConnectionError(int result) {
1321  // Gather data for http://crbug.com/3073. See how many users have trouble
1322  // establishing FTP data connection in passive FTP mode.
1323  enum {
1324    // Data connection successful.
1325    NET_ERROR_OK = 0,
1326
1327    // Local firewall blocked the connection.
1328    NET_ERROR_ACCESS_DENIED = 1,
1329
1330    // Connection timed out.
1331    NET_ERROR_TIMED_OUT = 2,
1332
1333    // Connection has been estabilished, but then got broken (either reset
1334    // or aborted).
1335    NET_ERROR_CONNECTION_BROKEN = 3,
1336
1337    // Connection has been refused.
1338    NET_ERROR_CONNECTION_REFUSED = 4,
1339
1340    // No connection to the internet.
1341    NET_ERROR_INTERNET_DISCONNECTED = 5,
1342
1343    // Could not reach the destination address.
1344    NET_ERROR_ADDRESS_UNREACHABLE = 6,
1345
1346    // A programming error in our network stack.
1347    NET_ERROR_UNEXPECTED = 7,
1348
1349    // Other kind of error.
1350    NET_ERROR_OTHER = 20,
1351
1352    NUM_OF_NET_ERROR_TYPES
1353  } type;
1354  switch (result) {
1355    case OK:
1356      type = NET_ERROR_OK;
1357      break;
1358    case ERR_ACCESS_DENIED:
1359    case ERR_NETWORK_ACCESS_DENIED:
1360      type = NET_ERROR_ACCESS_DENIED;
1361      break;
1362    case ERR_TIMED_OUT:
1363      type = NET_ERROR_TIMED_OUT;
1364      break;
1365    case ERR_CONNECTION_ABORTED:
1366    case ERR_CONNECTION_RESET:
1367    case ERR_CONNECTION_CLOSED:
1368      type = NET_ERROR_CONNECTION_BROKEN;
1369      break;
1370    case ERR_CONNECTION_FAILED:
1371    case ERR_CONNECTION_REFUSED:
1372      type = NET_ERROR_CONNECTION_REFUSED;
1373      break;
1374    case ERR_INTERNET_DISCONNECTED:
1375      type = NET_ERROR_INTERNET_DISCONNECTED;
1376      break;
1377    case ERR_ADDRESS_INVALID:
1378    case ERR_ADDRESS_UNREACHABLE:
1379      type = NET_ERROR_ADDRESS_UNREACHABLE;
1380      break;
1381    case ERR_UNEXPECTED:
1382      type = NET_ERROR_UNEXPECTED;
1383      break;
1384    default:
1385      type = NET_ERROR_OTHER;
1386      break;
1387  };
1388  static bool had_error_type[NUM_OF_NET_ERROR_TYPES];
1389
1390  DCHECK(type >= 0 && type < NUM_OF_NET_ERROR_TYPES);
1391  if (!had_error_type[type]) {
1392    had_error_type[type] = true;
1393    UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorHappened",
1394        type, NUM_OF_NET_ERROR_TYPES);
1395  }
1396  UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorCount",
1397      type, NUM_OF_NET_ERROR_TYPES);
1398}
1399
1400}  // namespace net
1401