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