Add signalSyncPoint to the WebGraphicsContext3D command buffer impls.
[chromium-blink-merge.git] / net / ftp / ftp_network_transaction.cc
blob16adff4db5655cdfb37f3d08d5ff1d5f3bc08627
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.
5 #include "net/ftp/ftp_network_transaction.h"
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/string_number_conversions.h"
12 #include "base/string_util.h"
13 #include "base/strings/string_split.h"
14 #include "base/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"
28 const char kCRLF[] = "\r\n";
30 const int kCtrlBufLen = 1024;
32 namespace {
34 // Returns true if |input| can be safely used as a part of FTP command.
35 bool 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.
40 // Protect agains newline injection attack.
41 if (input.find_first_of("\r\n") != std::string::npos)
42 return false;
44 return true;
47 enum ErrorClass {
48 // The requested action was initiated. The client should expect another
49 // reply before issuing the next command.
50 ERROR_CLASS_INITIATED,
52 // The requested action has been successfully completed.
53 ERROR_CLASS_OK,
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,
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,
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,
70 // Returns the error class for given response code. Caller should ensure
71 // that |response_code| is in range 100-599.
72 ErrorClass GetErrorClass(int response_code) {
73 if (response_code >= 100 && response_code <= 199)
74 return ERROR_CLASS_INITIATED;
76 if (response_code >= 200 && response_code <= 299)
77 return ERROR_CLASS_OK;
79 if (response_code >= 300 && response_code <= 399)
80 return ERROR_CLASS_INFO_NEEDED;
82 if (response_code >= 400 && response_code <= 499)
83 return ERROR_CLASS_TRANSIENT_ERROR;
85 if (response_code >= 500 && response_code <= 599)
86 return ERROR_CLASS_PERMANENT_ERROR;
88 // We should not be called on invalid error codes.
89 NOTREACHED() << response_code;
90 return ERROR_CLASS_PERMANENT_ERROR;
93 // Returns network error code for received FTP |response_code|.
94 int 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;
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 |
119 bool 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';
138 if (*ptr != sep)
139 return false;
141 return true;
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 ().
148 // See RFC 959, Section 4.1.2
149 bool ExtractPortFromPASVResponse(const net::FtpCtrlResponse& response,
150 int* port) {
151 if (response.lines.size() != 1)
152 return false;
154 std::string line(response.lines[0]);
155 if (!IsStringASCII(line))
156 return false;
157 if (line.length() < 2)
158 return false;
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;
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;
179 line = line.substr(paren_pos + 1, closing_paren_pos - paren_pos - 1);
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;
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;
198 return true;
201 } // namespace
203 namespace net {
205 FtpNetworkTransaction::FtpNetworkTransaction(
206 FtpNetworkSession* session,
207 ClientSocketFactory* socket_factory)
208 : command_sent_(COMMAND_NONE),
209 ALLOW_THIS_IN_INITIALIZER_LIST(
210 io_callback_(base::Bind(&FtpNetworkTransaction::OnIOComplete,
211 base::Unretained(this)))),
212 session_(session),
213 request_(NULL),
214 resolver_(session->host_resolver()),
215 read_ctrl_buf_(new IOBuffer(kCtrlBufLen)),
216 ctrl_response_buffer_(NULL),
217 read_data_buf_len_(0),
218 last_error_(OK),
219 system_type_(SYSTEM_TYPE_UNKNOWN),
220 // Use image (binary) transfer by default. It should always work,
221 // whereas the ascii transfer may damage binary data.
222 data_type_(DATA_TYPE_IMAGE),
223 resource_type_(RESOURCE_TYPE_UNKNOWN),
224 use_epsv_(true),
225 data_connection_port_(0),
226 socket_factory_(socket_factory),
227 next_state_(STATE_NONE),
228 state_after_data_connect_complete_(STATE_CTRL_WRITE_SIZE) {
231 FtpNetworkTransaction::~FtpNetworkTransaction() {
234 int FtpNetworkTransaction::Stop(int error) {
235 if (command_sent_ == COMMAND_QUIT)
236 return error;
238 next_state_ = STATE_CTRL_WRITE_QUIT;
239 last_error_ = error;
240 return OK;
243 int FtpNetworkTransaction::RestartIgnoringLastError(
244 const CompletionCallback& callback) {
245 return ERR_NOT_IMPLEMENTED;
248 int FtpNetworkTransaction::Start(const FtpRequestInfo* request_info,
249 const CompletionCallback& callback,
250 const BoundNetLog& net_log) {
251 net_log_ = net_log;
252 request_ = request_info;
254 ctrl_response_buffer_.reset(new FtpCtrlResponseBuffer(net_log_));
256 if (request_->url.has_username()) {
257 base::string16 username;
258 base::string16 password;
259 GetIdentityFromURL(request_->url, &username, &password);
260 credentials_.Set(username, password);
261 } else {
262 credentials_.Set(ASCIIToUTF16("anonymous"),
263 ASCIIToUTF16("chrome@example.com"));
266 DetectTypecode();
268 next_state_ = STATE_CTRL_RESOLVE_HOST;
269 int rv = DoLoop(OK);
270 if (rv == ERR_IO_PENDING)
271 user_callback_ = callback;
272 return rv;
275 int FtpNetworkTransaction::RestartWithAuth(const AuthCredentials& credentials,
276 const CompletionCallback& callback) {
277 ResetStateForRestart();
279 credentials_ = credentials;
281 next_state_ = STATE_CTRL_RESOLVE_HOST;
282 int rv = DoLoop(OK);
283 if (rv == ERR_IO_PENDING)
284 user_callback_ = callback;
285 return rv;
288 int FtpNetworkTransaction::Read(IOBuffer* buf,
289 int buf_len,
290 const CompletionCallback& callback) {
291 DCHECK(buf);
292 DCHECK_GT(buf_len, 0);
294 read_data_buf_ = buf;
295 read_data_buf_len_ = buf_len;
297 next_state_ = STATE_DATA_READ;
298 int rv = DoLoop(OK);
299 if (rv == ERR_IO_PENDING)
300 user_callback_ = callback;
301 return rv;
304 const FtpResponseInfo* FtpNetworkTransaction::GetResponseInfo() const {
305 return &response_;
308 LoadState FtpNetworkTransaction::GetLoadState() const {
309 if (next_state_ == STATE_CTRL_RESOLVE_HOST_COMPLETE)
310 return LOAD_STATE_RESOLVING_HOST;
312 if (next_state_ == STATE_CTRL_CONNECT_COMPLETE ||
313 next_state_ == STATE_DATA_CONNECT_COMPLETE)
314 return LOAD_STATE_CONNECTING;
316 if (next_state_ == STATE_DATA_READ_COMPLETE)
317 return LOAD_STATE_READING_RESPONSE;
319 if (command_sent_ == COMMAND_RETR && read_data_buf_.get())
320 return LOAD_STATE_READING_RESPONSE;
322 if (command_sent_ == COMMAND_QUIT)
323 return LOAD_STATE_IDLE;
325 if (command_sent_ != COMMAND_NONE)
326 return LOAD_STATE_SENDING_REQUEST;
328 return LOAD_STATE_IDLE;
331 uint64 FtpNetworkTransaction::GetUploadProgress() const {
332 return 0;
335 void FtpNetworkTransaction::ResetStateForRestart() {
336 command_sent_ = COMMAND_NONE;
337 user_callback_.Reset();
338 response_ = FtpResponseInfo();
339 read_ctrl_buf_ = new IOBuffer(kCtrlBufLen);
340 ctrl_response_buffer_.reset(new FtpCtrlResponseBuffer(net_log_));
341 read_data_buf_ = NULL;
342 read_data_buf_len_ = 0;
343 if (write_buf_)
344 write_buf_->SetOffset(0);
345 last_error_ = OK;
346 data_connection_port_ = 0;
347 ctrl_socket_.reset();
348 data_socket_.reset();
349 next_state_ = STATE_NONE;
350 state_after_data_connect_complete_ = STATE_CTRL_WRITE_SIZE;
353 void FtpNetworkTransaction::ResetDataConnectionAfterError(State next_state) {
354 // The server _might_ have reset the data connection
355 // (see RFC 959 3.2. ESTABLISHING DATA CONNECTIONS:
356 // "The server MUST close the data connection under the following
357 // conditions:
358 // ...
359 // 5. An irrecoverable error condition occurs.")
361 // It is ambiguous what an irrecoverable error condition is,
362 // so we take no chances.
363 state_after_data_connect_complete_ = next_state;
364 next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV;
367 void FtpNetworkTransaction::DoCallback(int rv) {
368 DCHECK(rv != ERR_IO_PENDING);
369 DCHECK(!user_callback_.is_null());
371 // Since Run may result in Read being called, clear callback_ up front.
372 CompletionCallback c = user_callback_;
373 user_callback_.Reset();
374 c.Run(rv);
377 void FtpNetworkTransaction::OnIOComplete(int result) {
378 int rv = DoLoop(result);
379 if (rv != ERR_IO_PENDING)
380 DoCallback(rv);
383 int FtpNetworkTransaction::ProcessCtrlResponse() {
384 FtpCtrlResponse response = ctrl_response_buffer_->PopResponse();
386 int rv = OK;
387 switch (command_sent_) {
388 case COMMAND_NONE:
389 // TODO(phajdan.jr): Check for errors in the welcome message.
390 next_state_ = STATE_CTRL_WRITE_USER;
391 break;
392 case COMMAND_USER:
393 rv = ProcessResponseUSER(response);
394 break;
395 case COMMAND_PASS:
396 rv = ProcessResponsePASS(response);
397 break;
398 case COMMAND_SYST:
399 rv = ProcessResponseSYST(response);
400 break;
401 case COMMAND_PWD:
402 rv = ProcessResponsePWD(response);
403 break;
404 case COMMAND_TYPE:
405 rv = ProcessResponseTYPE(response);
406 break;
407 case COMMAND_EPSV:
408 rv = ProcessResponseEPSV(response);
409 break;
410 case COMMAND_PASV:
411 rv = ProcessResponsePASV(response);
412 break;
413 case COMMAND_SIZE:
414 rv = ProcessResponseSIZE(response);
415 break;
416 case COMMAND_RETR:
417 rv = ProcessResponseRETR(response);
418 break;
419 case COMMAND_CWD:
420 rv = ProcessResponseCWD(response);
421 break;
422 case COMMAND_LIST:
423 rv = ProcessResponseLIST(response);
424 break;
425 case COMMAND_QUIT:
426 rv = ProcessResponseQUIT(response);
427 break;
428 default:
429 LOG(DFATAL) << "Unexpected value of command_sent_: " << command_sent_;
430 return ERR_UNEXPECTED;
433 // We may get multiple responses for some commands,
434 // see http://crbug.com/18036.
435 while (ctrl_response_buffer_->ResponseAvailable() && rv == OK) {
436 response = ctrl_response_buffer_->PopResponse();
438 switch (command_sent_) {
439 case COMMAND_RETR:
440 rv = ProcessResponseRETR(response);
441 break;
442 case COMMAND_LIST:
443 rv = ProcessResponseLIST(response);
444 break;
445 default:
446 // Multiple responses for other commands are invalid.
447 return Stop(ERR_INVALID_RESPONSE);
451 return rv;
454 // Used to prepare and send FTP command.
455 int FtpNetworkTransaction::SendFtpCommand(const std::string& command,
456 const std::string& command_for_log,
457 Command cmd) {
458 // If we send a new command when we still have unprocessed responses
459 // for previous commands, the response receiving code will have no way to know
460 // which responses are for which command.
461 DCHECK(!ctrl_response_buffer_->ResponseAvailable());
463 DCHECK(!write_command_buf_);
464 DCHECK(!write_buf_);
466 if (!IsValidFTPCommandString(command)) {
467 // Callers should validate the command themselves and return a more specific
468 // error code.
469 NOTREACHED();
470 return Stop(ERR_UNEXPECTED);
473 command_sent_ = cmd;
475 write_command_buf_ = new IOBufferWithSize(command.length() + 2);
476 write_buf_ = new DrainableIOBuffer(write_command_buf_,
477 write_command_buf_->size());
478 memcpy(write_command_buf_->data(), command.data(), command.length());
479 memcpy(write_command_buf_->data() + command.length(), kCRLF, 2);
481 net_log_.AddEvent(NetLog::TYPE_FTP_COMMAND_SENT,
482 NetLog::StringCallback("command", &command_for_log));
484 next_state_ = STATE_CTRL_WRITE;
485 return OK;
488 std::string FtpNetworkTransaction::GetRequestPathForFtpCommand(
489 bool is_directory) const {
490 std::string path(current_remote_directory_);
491 if (request_->url.has_path()) {
492 std::string gurl_path(request_->url.path());
494 // Get rid of the typecode, see RFC 1738 section 3.2.2. FTP url-path.
495 std::string::size_type pos = gurl_path.rfind(';');
496 if (pos != std::string::npos)
497 gurl_path.resize(pos);
499 path.append(gurl_path);
501 // Make sure that if the path is expected to be a file, it won't end
502 // with a trailing slash.
503 if (!is_directory && path.length() > 1 && path[path.length() - 1] == '/')
504 path.erase(path.length() - 1);
505 UnescapeRule::Type unescape_rules = UnescapeRule::SPACES |
506 UnescapeRule::URL_SPECIAL_CHARS;
507 // This may unescape to non-ASCII characters, but we allow that. See the
508 // comment for IsValidFTPCommandString.
509 path = net::UnescapeURLComponent(path, unescape_rules);
511 if (system_type_ == SYSTEM_TYPE_VMS) {
512 if (is_directory)
513 path = FtpUtil::UnixDirectoryPathToVMS(path);
514 else
515 path = FtpUtil::UnixFilePathToVMS(path);
518 DCHECK(IsValidFTPCommandString(path));
519 return path;
522 void FtpNetworkTransaction::DetectTypecode() {
523 if (!request_->url.has_path())
524 return;
525 std::string gurl_path(request_->url.path());
527 // Extract the typecode, see RFC 1738 section 3.2.2. FTP url-path.
528 std::string::size_type pos = gurl_path.rfind(';');
529 if (pos == std::string::npos)
530 return;
531 std::string typecode_string(gurl_path.substr(pos));
532 if (typecode_string == ";type=a") {
533 data_type_ = DATA_TYPE_ASCII;
534 resource_type_ = RESOURCE_TYPE_FILE;
535 } else if (typecode_string == ";type=i") {
536 data_type_ = DATA_TYPE_IMAGE;
537 resource_type_ = RESOURCE_TYPE_FILE;
538 } else if (typecode_string == ";type=d") {
539 resource_type_ = RESOURCE_TYPE_DIRECTORY;
543 int FtpNetworkTransaction::DoLoop(int result) {
544 DCHECK(next_state_ != STATE_NONE);
546 int rv = result;
547 do {
548 State state = next_state_;
549 next_state_ = STATE_NONE;
550 switch (state) {
551 case STATE_CTRL_RESOLVE_HOST:
552 DCHECK(rv == OK);
553 rv = DoCtrlResolveHost();
554 break;
555 case STATE_CTRL_RESOLVE_HOST_COMPLETE:
556 rv = DoCtrlResolveHostComplete(rv);
557 break;
558 case STATE_CTRL_CONNECT:
559 DCHECK(rv == OK);
560 rv = DoCtrlConnect();
561 break;
562 case STATE_CTRL_CONNECT_COMPLETE:
563 rv = DoCtrlConnectComplete(rv);
564 break;
565 case STATE_CTRL_READ:
566 DCHECK(rv == OK);
567 rv = DoCtrlRead();
568 break;
569 case STATE_CTRL_READ_COMPLETE:
570 rv = DoCtrlReadComplete(rv);
571 break;
572 case STATE_CTRL_WRITE:
573 DCHECK(rv == OK);
574 rv = DoCtrlWrite();
575 break;
576 case STATE_CTRL_WRITE_COMPLETE:
577 rv = DoCtrlWriteComplete(rv);
578 break;
579 case STATE_CTRL_WRITE_USER:
580 DCHECK(rv == OK);
581 rv = DoCtrlWriteUSER();
582 break;
583 case STATE_CTRL_WRITE_PASS:
584 DCHECK(rv == OK);
585 rv = DoCtrlWritePASS();
586 break;
587 case STATE_CTRL_WRITE_SYST:
588 DCHECK(rv == OK);
589 rv = DoCtrlWriteSYST();
590 break;
591 case STATE_CTRL_WRITE_PWD:
592 DCHECK(rv == OK);
593 rv = DoCtrlWritePWD();
594 break;
595 case STATE_CTRL_WRITE_TYPE:
596 DCHECK(rv == OK);
597 rv = DoCtrlWriteTYPE();
598 break;
599 case STATE_CTRL_WRITE_EPSV:
600 DCHECK(rv == OK);
601 rv = DoCtrlWriteEPSV();
602 break;
603 case STATE_CTRL_WRITE_PASV:
604 DCHECK(rv == OK);
605 rv = DoCtrlWritePASV();
606 break;
607 case STATE_CTRL_WRITE_RETR:
608 DCHECK(rv == OK);
609 rv = DoCtrlWriteRETR();
610 break;
611 case STATE_CTRL_WRITE_SIZE:
612 DCHECK(rv == OK);
613 rv = DoCtrlWriteSIZE();
614 break;
615 case STATE_CTRL_WRITE_CWD:
616 DCHECK(rv == OK);
617 rv = DoCtrlWriteCWD();
618 break;
619 case STATE_CTRL_WRITE_LIST:
620 DCHECK(rv == OK);
621 rv = DoCtrlWriteLIST();
622 break;
623 case STATE_CTRL_WRITE_QUIT:
624 DCHECK(rv == OK);
625 rv = DoCtrlWriteQUIT();
626 break;
627 case STATE_DATA_CONNECT:
628 DCHECK(rv == OK);
629 rv = DoDataConnect();
630 break;
631 case STATE_DATA_CONNECT_COMPLETE:
632 rv = DoDataConnectComplete(rv);
633 break;
634 case STATE_DATA_READ:
635 DCHECK(rv == OK);
636 rv = DoDataRead();
637 break;
638 case STATE_DATA_READ_COMPLETE:
639 rv = DoDataReadComplete(rv);
640 break;
641 default:
642 NOTREACHED() << "bad state";
643 rv = ERR_UNEXPECTED;
644 break;
646 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
647 return rv;
650 int FtpNetworkTransaction::DoCtrlResolveHost() {
651 next_state_ = STATE_CTRL_RESOLVE_HOST_COMPLETE;
653 HostResolver::RequestInfo info(HostPortPair::FromURL(request_->url));
654 // No known referrer.
655 return resolver_.Resolve(
656 info, &addresses_,
657 base::Bind(&FtpNetworkTransaction::OnIOComplete, base::Unretained(this)),
658 net_log_);
661 int FtpNetworkTransaction::DoCtrlResolveHostComplete(int result) {
662 if (result == OK)
663 next_state_ = STATE_CTRL_CONNECT;
664 return result;
667 int FtpNetworkTransaction::DoCtrlConnect() {
668 next_state_ = STATE_CTRL_CONNECT_COMPLETE;
669 ctrl_socket_.reset(socket_factory_->CreateTransportClientSocket(
670 addresses_, net_log_.net_log(), net_log_.source()));
671 net_log_.AddEvent(
672 NetLog::TYPE_FTP_CONTROL_CONNECTION,
673 ctrl_socket_->NetLog().source().ToEventParametersCallback());
674 return ctrl_socket_->Connect(io_callback_);
677 int FtpNetworkTransaction::DoCtrlConnectComplete(int result) {
678 if (result == OK) {
679 // Put the peer's IP address and port into the response.
680 IPEndPoint ip_endpoint;
681 result = ctrl_socket_->GetPeerAddress(&ip_endpoint);
682 if (result == OK) {
683 response_.socket_address = HostPortPair::FromIPEndPoint(ip_endpoint);
684 next_state_ = STATE_CTRL_READ;
686 if (ip_endpoint.GetFamily() == ADDRESS_FAMILY_IPV4) {
687 // Do not use EPSV for IPv4 connections. Some servers become confused
688 // and we time out while waiting to connect. PASV is perfectly fine for
689 // IPv4. Note that this blacklists IPv4 not to use EPSV instead of
690 // whitelisting IPv6 to use it, to make the code more future-proof:
691 // all future protocols should just use EPSV.
692 use_epsv_ = false;
696 return result;
699 int FtpNetworkTransaction::DoCtrlRead() {
700 next_state_ = STATE_CTRL_READ_COMPLETE;
701 return ctrl_socket_->Read(read_ctrl_buf_, kCtrlBufLen, io_callback_);
704 int FtpNetworkTransaction::DoCtrlReadComplete(int result) {
705 if (result == 0) {
706 // Some servers (for example Pure-FTPd) apparently close the control
707 // connection when anonymous login is not permitted. For more details
708 // see http://crbug.com/25023.
709 if (command_sent_ == COMMAND_USER &&
710 credentials_.username() == ASCIIToUTF16("anonymous")) {
711 response_.needs_auth = true;
713 return Stop(ERR_EMPTY_RESPONSE);
715 if (result < 0)
716 return Stop(result);
718 ctrl_response_buffer_->ConsumeData(read_ctrl_buf_->data(), result);
720 if (!ctrl_response_buffer_->ResponseAvailable()) {
721 // Read more data from the control socket.
722 next_state_ = STATE_CTRL_READ;
723 return OK;
726 return ProcessCtrlResponse();
729 int FtpNetworkTransaction::DoCtrlWrite() {
730 next_state_ = STATE_CTRL_WRITE_COMPLETE;
732 return ctrl_socket_->Write(write_buf_,
733 write_buf_->BytesRemaining(),
734 io_callback_);
737 int FtpNetworkTransaction::DoCtrlWriteComplete(int result) {
738 if (result < 0)
739 return result;
741 write_buf_->DidConsume(result);
742 if (write_buf_->BytesRemaining() == 0) {
743 // Clear the write buffer.
744 write_buf_ = NULL;
745 write_command_buf_ = NULL;
747 next_state_ = STATE_CTRL_READ;
748 } else {
749 next_state_ = STATE_CTRL_WRITE;
751 return OK;
754 // FTP Commands and responses
756 // USER Command.
757 int FtpNetworkTransaction::DoCtrlWriteUSER() {
758 std::string command = "USER " + UTF16ToUTF8(credentials_.username());
760 if (!IsValidFTPCommandString(command))
761 return Stop(ERR_MALFORMED_IDENTITY);
763 next_state_ = STATE_CTRL_READ;
764 return SendFtpCommand(command, "USER ***", COMMAND_USER);
767 int FtpNetworkTransaction::ProcessResponseUSER(
768 const FtpCtrlResponse& response) {
769 switch (GetErrorClass(response.status_code)) {
770 case ERROR_CLASS_OK:
771 next_state_ = STATE_CTRL_WRITE_SYST;
772 break;
773 case ERROR_CLASS_INFO_NEEDED:
774 next_state_ = STATE_CTRL_WRITE_PASS;
775 break;
776 case ERROR_CLASS_TRANSIENT_ERROR:
777 case ERROR_CLASS_PERMANENT_ERROR:
778 response_.needs_auth = true;
779 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
780 default:
781 NOTREACHED();
782 return Stop(ERR_UNEXPECTED);
784 return OK;
787 // PASS command.
788 int FtpNetworkTransaction::DoCtrlWritePASS() {
789 std::string command = "PASS " + UTF16ToUTF8(credentials_.password());
791 if (!IsValidFTPCommandString(command))
792 return Stop(ERR_MALFORMED_IDENTITY);
794 next_state_ = STATE_CTRL_READ;
795 return SendFtpCommand(command, "PASS ***", COMMAND_PASS);
798 int FtpNetworkTransaction::ProcessResponsePASS(
799 const FtpCtrlResponse& response) {
800 switch (GetErrorClass(response.status_code)) {
801 case ERROR_CLASS_OK:
802 next_state_ = STATE_CTRL_WRITE_SYST;
803 break;
804 case ERROR_CLASS_INFO_NEEDED:
805 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
806 case ERROR_CLASS_TRANSIENT_ERROR:
807 case ERROR_CLASS_PERMANENT_ERROR:
808 response_.needs_auth = true;
809 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
810 default:
811 NOTREACHED();
812 return Stop(ERR_UNEXPECTED);
814 return OK;
817 // SYST command.
818 int FtpNetworkTransaction::DoCtrlWriteSYST() {
819 std::string command = "SYST";
820 next_state_ = STATE_CTRL_READ;
821 return SendFtpCommand(command, command, COMMAND_SYST);
824 int FtpNetworkTransaction::ProcessResponseSYST(
825 const FtpCtrlResponse& response) {
826 switch (GetErrorClass(response.status_code)) {
827 case ERROR_CLASS_INITIATED:
828 return Stop(ERR_INVALID_RESPONSE);
829 case ERROR_CLASS_OK: {
830 // All important info should be on the first line.
831 std::string line = response.lines[0];
832 // The response should be ASCII, which allows us to do case-insensitive
833 // comparisons easily. If it is not ASCII, we leave the system type
834 // as unknown.
835 if (IsStringASCII(line)) {
836 line = StringToLowerASCII(line);
838 // Remove all whitespace, to correctly handle cases like fancy "V M S"
839 // response instead of "VMS".
840 RemoveChars(line, kWhitespaceASCII, &line);
842 // The "magic" strings we test for below have been gathered by an
843 // empirical study. VMS needs to come first because some VMS systems
844 // also respond with "UNIX emulation", which is not perfect. It is much
845 // more reliable to talk to these servers in their native language.
846 if (line.find("vms") != std::string::npos) {
847 system_type_ = SYSTEM_TYPE_VMS;
848 } else if (line.find("l8") != std::string::npos ||
849 line.find("unix") != std::string::npos ||
850 line.find("bsd") != std::string::npos) {
851 system_type_ = SYSTEM_TYPE_UNIX;
852 } else if (line.find("win32") != std::string::npos ||
853 line.find("windows") != std::string::npos) {
854 system_type_ = SYSTEM_TYPE_WINDOWS;
855 } else if (line.find("os/2") != std::string::npos) {
856 system_type_ = SYSTEM_TYPE_OS2;
859 next_state_ = STATE_CTRL_WRITE_PWD;
860 break;
862 case ERROR_CLASS_INFO_NEEDED:
863 return Stop(ERR_INVALID_RESPONSE);
864 case ERROR_CLASS_TRANSIENT_ERROR:
865 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
866 case ERROR_CLASS_PERMANENT_ERROR:
867 // Server does not recognize the SYST command so proceed.
868 next_state_ = STATE_CTRL_WRITE_PWD;
869 break;
870 default:
871 NOTREACHED();
872 return Stop(ERR_UNEXPECTED);
874 return OK;
877 // PWD command.
878 int FtpNetworkTransaction::DoCtrlWritePWD() {
879 std::string command = "PWD";
880 next_state_ = STATE_CTRL_READ;
881 return SendFtpCommand(command, command, COMMAND_PWD);
884 int FtpNetworkTransaction::ProcessResponsePWD(const FtpCtrlResponse& response) {
885 switch (GetErrorClass(response.status_code)) {
886 case ERROR_CLASS_INITIATED:
887 return Stop(ERR_INVALID_RESPONSE);
888 case ERROR_CLASS_OK: {
889 // The info we look for should be on the first line.
890 std::string line = response.lines[0];
891 if (line.empty())
892 return Stop(ERR_INVALID_RESPONSE);
893 std::string::size_type quote_pos = line.find('"');
894 if (quote_pos != std::string::npos) {
895 line = line.substr(quote_pos + 1);
896 quote_pos = line.find('"');
897 if (quote_pos == std::string::npos)
898 return Stop(ERR_INVALID_RESPONSE);
899 line = line.substr(0, quote_pos);
901 if (system_type_ == SYSTEM_TYPE_VMS)
902 line = FtpUtil::VMSPathToUnix(line);
903 if (line.length() && line[line.length() - 1] == '/')
904 line.erase(line.length() - 1);
905 current_remote_directory_ = line;
906 next_state_ = STATE_CTRL_WRITE_TYPE;
907 break;
909 case ERROR_CLASS_INFO_NEEDED:
910 return Stop(ERR_INVALID_RESPONSE);
911 case ERROR_CLASS_TRANSIENT_ERROR:
912 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
913 case ERROR_CLASS_PERMANENT_ERROR:
914 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
915 default:
916 NOTREACHED();
917 return Stop(ERR_UNEXPECTED);
919 return OK;
922 // TYPE command.
923 int FtpNetworkTransaction::DoCtrlWriteTYPE() {
924 std::string command = "TYPE ";
925 if (data_type_ == DATA_TYPE_ASCII) {
926 command += "A";
927 } else if (data_type_ == DATA_TYPE_IMAGE) {
928 command += "I";
929 } else {
930 NOTREACHED();
931 return Stop(ERR_UNEXPECTED);
933 next_state_ = STATE_CTRL_READ;
934 return SendFtpCommand(command, command, COMMAND_TYPE);
937 int FtpNetworkTransaction::ProcessResponseTYPE(
938 const FtpCtrlResponse& response) {
939 switch (GetErrorClass(response.status_code)) {
940 case ERROR_CLASS_INITIATED:
941 return Stop(ERR_INVALID_RESPONSE);
942 case ERROR_CLASS_OK:
943 next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV;
944 break;
945 case ERROR_CLASS_INFO_NEEDED:
946 return Stop(ERR_INVALID_RESPONSE);
947 case ERROR_CLASS_TRANSIENT_ERROR:
948 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
949 case ERROR_CLASS_PERMANENT_ERROR:
950 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
951 default:
952 NOTREACHED();
953 return Stop(ERR_UNEXPECTED);
955 return OK;
958 // EPSV command
959 int FtpNetworkTransaction::DoCtrlWriteEPSV() {
960 const std::string command = "EPSV";
961 next_state_ = STATE_CTRL_READ;
962 return SendFtpCommand(command, command, COMMAND_EPSV);
965 int FtpNetworkTransaction::ProcessResponseEPSV(
966 const FtpCtrlResponse& response) {
967 switch (GetErrorClass(response.status_code)) {
968 case ERROR_CLASS_INITIATED:
969 return Stop(ERR_INVALID_RESPONSE);
970 case ERROR_CLASS_OK:
971 if (!ExtractPortFromEPSVResponse( response, &data_connection_port_))
972 return Stop(ERR_INVALID_RESPONSE);
973 if (data_connection_port_ < 1024 ||
974 !IsPortAllowedByFtp(data_connection_port_))
975 return Stop(ERR_UNSAFE_PORT);
976 next_state_ = STATE_DATA_CONNECT;
977 break;
978 case ERROR_CLASS_INFO_NEEDED:
979 return Stop(ERR_INVALID_RESPONSE);
980 case ERROR_CLASS_TRANSIENT_ERROR:
981 case ERROR_CLASS_PERMANENT_ERROR:
982 use_epsv_ = false;
983 next_state_ = STATE_CTRL_WRITE_PASV;
984 return OK;
985 default:
986 NOTREACHED();
987 return Stop(ERR_UNEXPECTED);
989 return OK;
992 // PASV command
993 int FtpNetworkTransaction::DoCtrlWritePASV() {
994 std::string command = "PASV";
995 next_state_ = STATE_CTRL_READ;
996 return SendFtpCommand(command, command, COMMAND_PASV);
999 int FtpNetworkTransaction::ProcessResponsePASV(
1000 const FtpCtrlResponse& response) {
1001 switch (GetErrorClass(response.status_code)) {
1002 case ERROR_CLASS_INITIATED:
1003 return Stop(ERR_INVALID_RESPONSE);
1004 case ERROR_CLASS_OK:
1005 if (!ExtractPortFromPASVResponse(response, &data_connection_port_))
1006 return Stop(ERR_INVALID_RESPONSE);
1007 if (data_connection_port_ < 1024 ||
1008 !IsPortAllowedByFtp(data_connection_port_))
1009 return Stop(ERR_UNSAFE_PORT);
1010 next_state_ = STATE_DATA_CONNECT;
1011 break;
1012 case ERROR_CLASS_INFO_NEEDED:
1013 return Stop(ERR_INVALID_RESPONSE);
1014 case ERROR_CLASS_TRANSIENT_ERROR:
1015 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1016 case ERROR_CLASS_PERMANENT_ERROR:
1017 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1018 default:
1019 NOTREACHED();
1020 return Stop(ERR_UNEXPECTED);
1022 return OK;
1025 // RETR command
1026 int FtpNetworkTransaction::DoCtrlWriteRETR() {
1027 std::string command = "RETR " + GetRequestPathForFtpCommand(false);
1028 next_state_ = STATE_CTRL_READ;
1029 return SendFtpCommand(command, command, COMMAND_RETR);
1032 int FtpNetworkTransaction::ProcessResponseRETR(
1033 const FtpCtrlResponse& response) {
1034 switch (GetErrorClass(response.status_code)) {
1035 case ERROR_CLASS_INITIATED:
1036 // We want the client to start reading the response at this point.
1037 // It got here either through Start or RestartWithAuth. We want that
1038 // method to complete. Not setting next state here will make DoLoop exit
1039 // and in turn make Start/RestartWithAuth complete.
1040 resource_type_ = RESOURCE_TYPE_FILE;
1041 break;
1042 case ERROR_CLASS_OK:
1043 resource_type_ = RESOURCE_TYPE_FILE;
1044 next_state_ = STATE_CTRL_WRITE_QUIT;
1045 break;
1046 case ERROR_CLASS_INFO_NEEDED:
1047 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1048 case ERROR_CLASS_TRANSIENT_ERROR:
1049 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1050 case ERROR_CLASS_PERMANENT_ERROR:
1051 // Code 550 means "Failed to open file". Other codes are unrelated,
1052 // like "Not logged in" etc.
1053 if (response.status_code != 550 || resource_type_ == RESOURCE_TYPE_FILE)
1054 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1056 // It's possible that RETR failed because the path is a directory.
1057 resource_type_ = RESOURCE_TYPE_DIRECTORY;
1059 // We're going to try CWD next, but first send a PASV one more time,
1060 // because some FTP servers, including FileZilla, require that.
1061 // See http://crbug.com/25316.
1062 next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV;
1063 break;
1064 default:
1065 NOTREACHED();
1066 return Stop(ERR_UNEXPECTED);
1069 // We should be sure about our resource type now. Otherwise we risk
1070 // an infinite loop (RETR can later send CWD, and CWD can later send RETR).
1071 DCHECK_NE(RESOURCE_TYPE_UNKNOWN, resource_type_);
1073 return OK;
1076 // SIZE command
1077 int FtpNetworkTransaction::DoCtrlWriteSIZE() {
1078 std::string command = "SIZE " + GetRequestPathForFtpCommand(false);
1079 next_state_ = STATE_CTRL_READ;
1080 return SendFtpCommand(command, command, COMMAND_SIZE);
1083 int FtpNetworkTransaction::ProcessResponseSIZE(
1084 const FtpCtrlResponse& response) {
1085 State state_after_size;
1086 if (resource_type_ == RESOURCE_TYPE_FILE)
1087 state_after_size = STATE_CTRL_WRITE_RETR;
1088 else
1089 state_after_size = STATE_CTRL_WRITE_CWD;
1091 switch (GetErrorClass(response.status_code)) {
1092 case ERROR_CLASS_INITIATED:
1093 next_state_ = state_after_size;
1094 break;
1095 case ERROR_CLASS_OK:
1096 if (response.lines.size() != 1)
1097 return Stop(ERR_INVALID_RESPONSE);
1098 int64 size;
1099 if (!base::StringToInt64(response.lines[0], &size))
1100 return Stop(ERR_INVALID_RESPONSE);
1101 if (size < 0)
1102 return Stop(ERR_INVALID_RESPONSE);
1104 // A successful response to SIZE does not mean the resource is a file.
1105 // Some FTP servers (for example, the qnx one) send a SIZE even for
1106 // directories.
1107 response_.expected_content_size = size;
1109 next_state_ = state_after_size;
1110 break;
1111 case ERROR_CLASS_INFO_NEEDED:
1112 next_state_ = state_after_size;
1113 break;
1114 case ERROR_CLASS_TRANSIENT_ERROR:
1115 ResetDataConnectionAfterError(state_after_size);
1116 break;
1117 case ERROR_CLASS_PERMANENT_ERROR:
1118 // It's possible that SIZE failed because the path is a directory.
1119 if (resource_type_ == RESOURCE_TYPE_UNKNOWN &&
1120 response.status_code != 550) {
1121 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1124 ResetDataConnectionAfterError(state_after_size);
1125 break;
1126 default:
1127 NOTREACHED();
1128 return Stop(ERR_UNEXPECTED);
1131 return OK;
1134 // CWD command
1135 int FtpNetworkTransaction::DoCtrlWriteCWD() {
1136 std::string command = "CWD " + GetRequestPathForFtpCommand(true);
1137 next_state_ = STATE_CTRL_READ;
1138 return SendFtpCommand(command, command, COMMAND_CWD);
1141 int FtpNetworkTransaction::ProcessResponseCWD(const FtpCtrlResponse& response) {
1142 // We should never issue CWD if we know the target resource is a file.
1143 DCHECK_NE(RESOURCE_TYPE_FILE, resource_type_);
1145 switch (GetErrorClass(response.status_code)) {
1146 case ERROR_CLASS_INITIATED:
1147 return Stop(ERR_INVALID_RESPONSE);
1148 case ERROR_CLASS_OK:
1149 next_state_ = STATE_CTRL_WRITE_LIST;
1150 break;
1151 case ERROR_CLASS_INFO_NEEDED:
1152 return Stop(ERR_INVALID_RESPONSE);
1153 case ERROR_CLASS_TRANSIENT_ERROR:
1154 // Some FTP servers send response 451 (not a valid CWD response according
1155 // to RFC 959) instead of 550.
1156 if (response.status_code == 451)
1157 return ProcessResponseCWDNotADirectory();
1159 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1160 case ERROR_CLASS_PERMANENT_ERROR:
1161 if (response.status_code == 550)
1162 return ProcessResponseCWDNotADirectory();
1164 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1165 default:
1166 NOTREACHED();
1167 return Stop(ERR_UNEXPECTED);
1170 return OK;
1173 int FtpNetworkTransaction::ProcessResponseCWDNotADirectory() {
1174 if (resource_type_ == RESOURCE_TYPE_DIRECTORY) {
1175 // We're assuming that the resource is a directory, but the server
1176 // says it's not true. The most probable interpretation is that it
1177 // doesn't exist (with FTP we can't be sure).
1178 return Stop(ERR_FILE_NOT_FOUND);
1181 // We are here because SIZE failed and we are not sure what the resource
1182 // type is. It could still be file, and SIZE could fail because of
1183 // an access error (http://crbug.com/56734). Try RETR just to be sure.
1184 resource_type_ = RESOURCE_TYPE_FILE;
1186 ResetDataConnectionAfterError(STATE_CTRL_WRITE_RETR);
1187 return OK;
1190 // LIST command
1191 int FtpNetworkTransaction::DoCtrlWriteLIST() {
1192 // Use the -l option for mod_ftp configured in LISTIsNLST mode: the option
1193 // forces LIST output instead of NLST (which would be ambiguous for us
1194 // to parse).
1195 std::string command("LIST -l");
1196 if (system_type_ == SYSTEM_TYPE_VMS)
1197 command = "LIST *.*;0";
1199 next_state_ = STATE_CTRL_READ;
1200 return SendFtpCommand(command, command, COMMAND_LIST);
1203 int FtpNetworkTransaction::ProcessResponseLIST(
1204 const FtpCtrlResponse& response) {
1205 switch (GetErrorClass(response.status_code)) {
1206 case ERROR_CLASS_INITIATED:
1207 // We want the client to start reading the response at this point.
1208 // It got here either through Start or RestartWithAuth. We want that
1209 // method to complete. Not setting next state here will make DoLoop exit
1210 // and in turn make Start/RestartWithAuth complete.
1211 response_.is_directory_listing = true;
1212 break;
1213 case ERROR_CLASS_OK:
1214 response_.is_directory_listing = true;
1215 next_state_ = STATE_CTRL_WRITE_QUIT;
1216 break;
1217 case ERROR_CLASS_INFO_NEEDED:
1218 return Stop(ERR_INVALID_RESPONSE);
1219 case ERROR_CLASS_TRANSIENT_ERROR:
1220 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1221 case ERROR_CLASS_PERMANENT_ERROR:
1222 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1223 default:
1224 NOTREACHED();
1225 return Stop(ERR_UNEXPECTED);
1227 return OK;
1230 // QUIT command
1231 int FtpNetworkTransaction::DoCtrlWriteQUIT() {
1232 std::string command = "QUIT";
1233 next_state_ = STATE_CTRL_READ;
1234 return SendFtpCommand(command, command, COMMAND_QUIT);
1237 int FtpNetworkTransaction::ProcessResponseQUIT(
1238 const FtpCtrlResponse& response) {
1239 ctrl_socket_->Disconnect();
1240 return last_error_;
1243 // Data Connection
1245 int FtpNetworkTransaction::DoDataConnect() {
1246 next_state_ = STATE_DATA_CONNECT_COMPLETE;
1247 IPEndPoint ip_endpoint;
1248 AddressList data_address;
1249 // Connect to the same host as the control socket to prevent PASV port
1250 // scanning attacks.
1251 int rv = ctrl_socket_->GetPeerAddress(&ip_endpoint);
1252 if (rv != OK)
1253 return Stop(rv);
1254 data_address = AddressList::CreateFromIPAddress(
1255 ip_endpoint.address(), data_connection_port_);
1256 data_socket_.reset(socket_factory_->CreateTransportClientSocket(
1257 data_address, net_log_.net_log(), net_log_.source()));
1258 net_log_.AddEvent(
1259 NetLog::TYPE_FTP_DATA_CONNECTION,
1260 data_socket_->NetLog().source().ToEventParametersCallback());
1261 return data_socket_->Connect(io_callback_);
1264 int FtpNetworkTransaction::DoDataConnectComplete(int result) {
1265 if (result != OK && use_epsv_) {
1266 // It's possible we hit a broken server, sadly. They can break in different
1267 // ways. Some time out, some reset a connection. Fall back to PASV.
1268 // TODO(phajdan.jr): remember it for future transactions with this server.
1269 // TODO(phajdan.jr): write a test for this code path.
1270 use_epsv_ = false;
1271 next_state_ = STATE_CTRL_WRITE_PASV;
1272 return OK;
1275 // Only record the connection error after we've applied all our fallbacks.
1276 // We want to capture the final error, one we're not going to recover from.
1277 RecordDataConnectionError(result);
1279 if (result != OK)
1280 return Stop(result);
1282 next_state_ = state_after_data_connect_complete_;
1283 return OK;
1286 int FtpNetworkTransaction::DoDataRead() {
1287 DCHECK(read_data_buf_);
1288 DCHECK_GT(read_data_buf_len_, 0);
1290 if (data_socket_ == NULL || !data_socket_->IsConnected()) {
1291 // If we don't destroy the data socket completely, some servers will wait
1292 // for us (http://crbug.com/21127). The half-closed TCP connection needs
1293 // to be closed on our side too.
1294 data_socket_.reset();
1296 if (ctrl_socket_->IsConnected()) {
1297 // Wait for the server's response, we should get it before sending QUIT.
1298 next_state_ = STATE_CTRL_READ;
1299 return OK;
1302 // We are no longer connected to the server, so just finish the transaction.
1303 return Stop(OK);
1306 next_state_ = STATE_DATA_READ_COMPLETE;
1307 read_data_buf_->data()[0] = 0;
1308 return data_socket_->Read(read_data_buf_, read_data_buf_len_, io_callback_);
1311 int FtpNetworkTransaction::DoDataReadComplete(int result) {
1312 return result;
1315 // We're using a histogram as a group of counters, with one bucket for each
1316 // enumeration value. We're only interested in the values of the counters.
1317 // Ignore the shape, average, and standard deviation of the histograms because
1318 // they are meaningless.
1320 // We use two histograms. In the first histogram we tally whether the user has
1321 // seen an error of that type during the session. In the second histogram we
1322 // tally the total number of times the users sees each errer.
1323 void FtpNetworkTransaction::RecordDataConnectionError(int result) {
1324 // Gather data for http://crbug.com/3073. See how many users have trouble
1325 // establishing FTP data connection in passive FTP mode.
1326 enum {
1327 // Data connection successful.
1328 NET_ERROR_OK = 0,
1330 // Local firewall blocked the connection.
1331 NET_ERROR_ACCESS_DENIED = 1,
1333 // Connection timed out.
1334 NET_ERROR_TIMED_OUT = 2,
1336 // Connection has been estabilished, but then got broken (either reset
1337 // or aborted).
1338 NET_ERROR_CONNECTION_BROKEN = 3,
1340 // Connection has been refused.
1341 NET_ERROR_CONNECTION_REFUSED = 4,
1343 // No connection to the internet.
1344 NET_ERROR_INTERNET_DISCONNECTED = 5,
1346 // Could not reach the destination address.
1347 NET_ERROR_ADDRESS_UNREACHABLE = 6,
1349 // A programming error in our network stack.
1350 NET_ERROR_UNEXPECTED = 7,
1352 // Other kind of error.
1353 NET_ERROR_OTHER = 20,
1355 NUM_OF_NET_ERROR_TYPES
1356 } type;
1357 switch (result) {
1358 case OK:
1359 type = NET_ERROR_OK;
1360 break;
1361 case ERR_ACCESS_DENIED:
1362 case ERR_NETWORK_ACCESS_DENIED:
1363 type = NET_ERROR_ACCESS_DENIED;
1364 break;
1365 case ERR_TIMED_OUT:
1366 type = NET_ERROR_TIMED_OUT;
1367 break;
1368 case ERR_CONNECTION_ABORTED:
1369 case ERR_CONNECTION_RESET:
1370 case ERR_CONNECTION_CLOSED:
1371 type = NET_ERROR_CONNECTION_BROKEN;
1372 break;
1373 case ERR_CONNECTION_FAILED:
1374 case ERR_CONNECTION_REFUSED:
1375 type = NET_ERROR_CONNECTION_REFUSED;
1376 break;
1377 case ERR_INTERNET_DISCONNECTED:
1378 type = NET_ERROR_INTERNET_DISCONNECTED;
1379 break;
1380 case ERR_ADDRESS_INVALID:
1381 case ERR_ADDRESS_UNREACHABLE:
1382 type = NET_ERROR_ADDRESS_UNREACHABLE;
1383 break;
1384 case ERR_UNEXPECTED:
1385 type = NET_ERROR_UNEXPECTED;
1386 break;
1387 default:
1388 type = NET_ERROR_OTHER;
1389 break;
1391 static bool had_error_type[NUM_OF_NET_ERROR_TYPES];
1393 DCHECK(type >= 0 && type < NUM_OF_NET_ERROR_TYPES);
1394 if (!had_error_type[type]) {
1395 had_error_type[type] = true;
1396 UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorHappened",
1397 type, NUM_OF_NET_ERROR_TYPES);
1399 UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorCount",
1400 type, NUM_OF_NET_ERROR_TYPES);
1403 } // namespace net