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