Plugin Power Saver: Force SW rendering for peripheral plugins.
[chromium-blink-merge.git] / net / ftp / ftp_network_transaction.cc
blob4cbde2725b0b8c1f9bd2601f40b58dd0321a60f8
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/strings/string_number_conversions.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/string_split.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/values.h"
16 #include "net/base/address_list.h"
17 #include "net/base/connection_type_histograms.h"
18 #include "net/base/escape.h"
19 #include "net/base/net_errors.h"
20 #include "net/base/net_log.h"
21 #include "net/base/net_util.h"
22 #include "net/ftp/ftp_network_session.h"
23 #include "net/ftp/ftp_request_info.h"
24 #include "net/ftp/ftp_util.h"
25 #include "net/socket/client_socket_factory.h"
26 #include "net/socket/stream_socket.h"
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 (!base::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 io_callback_(base::Bind(&FtpNetworkTransaction::OnIOComplete,
210 base::Unretained(this))),
211 session_(session),
212 request_(NULL),
213 resolver_(session->host_resolver()),
214 read_ctrl_buf_(new IOBuffer(kCtrlBufLen)),
215 read_data_buf_len_(0),
216 last_error_(OK),
217 system_type_(SYSTEM_TYPE_UNKNOWN),
218 // Use image (binary) transfer by default. It should always work,
219 // whereas the ascii transfer may damage binary data.
220 data_type_(DATA_TYPE_IMAGE),
221 resource_type_(RESOURCE_TYPE_UNKNOWN),
222 use_epsv_(true),
223 data_connection_port_(0),
224 socket_factory_(socket_factory),
225 next_state_(STATE_NONE),
226 state_after_data_connect_complete_(STATE_CTRL_WRITE_SIZE) {}
228 FtpNetworkTransaction::~FtpNetworkTransaction() {
231 int FtpNetworkTransaction::Stop(int error) {
232 if (command_sent_ == COMMAND_QUIT)
233 return error;
235 next_state_ = STATE_CTRL_WRITE_QUIT;
236 last_error_ = error;
237 return OK;
240 int FtpNetworkTransaction::RestartIgnoringLastError(
241 const CompletionCallback& callback) {
242 return ERR_NOT_IMPLEMENTED;
245 int FtpNetworkTransaction::Start(const FtpRequestInfo* request_info,
246 const CompletionCallback& callback,
247 const BoundNetLog& net_log) {
248 net_log_ = net_log;
249 request_ = request_info;
251 ctrl_response_buffer_.reset(new FtpCtrlResponseBuffer(net_log_));
253 if (request_->url.has_username()) {
254 base::string16 username;
255 base::string16 password;
256 GetIdentityFromURL(request_->url, &username, &password);
257 credentials_.Set(username, password);
258 } else {
259 credentials_.Set(base::ASCIIToUTF16("anonymous"),
260 base::ASCIIToUTF16("chrome@example.com"));
263 DetectTypecode();
265 next_state_ = STATE_CTRL_RESOLVE_HOST;
266 int rv = DoLoop(OK);
267 if (rv == ERR_IO_PENDING)
268 user_callback_ = callback;
269 return rv;
272 int FtpNetworkTransaction::RestartWithAuth(const AuthCredentials& credentials,
273 const CompletionCallback& callback) {
274 ResetStateForRestart();
276 credentials_ = credentials;
278 next_state_ = STATE_CTRL_RESOLVE_HOST;
279 int rv = DoLoop(OK);
280 if (rv == ERR_IO_PENDING)
281 user_callback_ = callback;
282 return rv;
285 int FtpNetworkTransaction::Read(IOBuffer* buf,
286 int buf_len,
287 const CompletionCallback& callback) {
288 DCHECK(buf);
289 DCHECK_GT(buf_len, 0);
291 read_data_buf_ = buf;
292 read_data_buf_len_ = buf_len;
294 next_state_ = STATE_DATA_READ;
295 int rv = DoLoop(OK);
296 if (rv == ERR_IO_PENDING)
297 user_callback_ = callback;
298 return rv;
301 const FtpResponseInfo* FtpNetworkTransaction::GetResponseInfo() const {
302 return &response_;
305 LoadState FtpNetworkTransaction::GetLoadState() const {
306 if (next_state_ == STATE_CTRL_RESOLVE_HOST_COMPLETE)
307 return LOAD_STATE_RESOLVING_HOST;
309 if (next_state_ == STATE_CTRL_CONNECT_COMPLETE ||
310 next_state_ == STATE_DATA_CONNECT_COMPLETE)
311 return LOAD_STATE_CONNECTING;
313 if (next_state_ == STATE_DATA_READ_COMPLETE)
314 return LOAD_STATE_READING_RESPONSE;
316 if (command_sent_ == COMMAND_RETR && read_data_buf_.get())
317 return LOAD_STATE_READING_RESPONSE;
319 if (command_sent_ == COMMAND_QUIT)
320 return LOAD_STATE_IDLE;
322 if (command_sent_ != COMMAND_NONE)
323 return LOAD_STATE_SENDING_REQUEST;
325 return LOAD_STATE_IDLE;
328 uint64 FtpNetworkTransaction::GetUploadProgress() const {
329 return 0;
332 void FtpNetworkTransaction::ResetStateForRestart() {
333 command_sent_ = COMMAND_NONE;
334 user_callback_.Reset();
335 response_ = FtpResponseInfo();
336 read_ctrl_buf_ = new IOBuffer(kCtrlBufLen);
337 ctrl_response_buffer_.reset(new FtpCtrlResponseBuffer(net_log_));
338 read_data_buf_ = NULL;
339 read_data_buf_len_ = 0;
340 if (write_buf_.get())
341 write_buf_->SetOffset(0);
342 last_error_ = OK;
343 data_connection_port_ = 0;
344 ctrl_socket_.reset();
345 data_socket_.reset();
346 next_state_ = STATE_NONE;
347 state_after_data_connect_complete_ = STATE_CTRL_WRITE_SIZE;
350 void FtpNetworkTransaction::ResetDataConnectionAfterError(State next_state) {
351 // The server _might_ have reset the data connection
352 // (see RFC 959 3.2. ESTABLISHING DATA CONNECTIONS:
353 // "The server MUST close the data connection under the following
354 // conditions:
355 // ...
356 // 5. An irrecoverable error condition occurs.")
358 // It is ambiguous what an irrecoverable error condition is,
359 // so we take no chances.
360 state_after_data_connect_complete_ = next_state;
361 next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV;
364 void FtpNetworkTransaction::DoCallback(int rv) {
365 DCHECK(rv != ERR_IO_PENDING);
366 DCHECK(!user_callback_.is_null());
368 // Since Run may result in Read being called, clear callback_ up front.
369 CompletionCallback c = user_callback_;
370 user_callback_.Reset();
371 c.Run(rv);
374 void FtpNetworkTransaction::OnIOComplete(int result) {
375 int rv = DoLoop(result);
376 if (rv != ERR_IO_PENDING)
377 DoCallback(rv);
380 int FtpNetworkTransaction::ProcessCtrlResponse() {
381 FtpCtrlResponse response = ctrl_response_buffer_->PopResponse();
383 int rv = OK;
384 switch (command_sent_) {
385 case COMMAND_NONE:
386 // TODO(phajdan.jr): Check for errors in the welcome message.
387 next_state_ = STATE_CTRL_WRITE_USER;
388 break;
389 case COMMAND_USER:
390 rv = ProcessResponseUSER(response);
391 break;
392 case COMMAND_PASS:
393 rv = ProcessResponsePASS(response);
394 break;
395 case COMMAND_SYST:
396 rv = ProcessResponseSYST(response);
397 break;
398 case COMMAND_PWD:
399 rv = ProcessResponsePWD(response);
400 break;
401 case COMMAND_TYPE:
402 rv = ProcessResponseTYPE(response);
403 break;
404 case COMMAND_EPSV:
405 rv = ProcessResponseEPSV(response);
406 break;
407 case COMMAND_PASV:
408 rv = ProcessResponsePASV(response);
409 break;
410 case COMMAND_SIZE:
411 rv = ProcessResponseSIZE(response);
412 break;
413 case COMMAND_RETR:
414 rv = ProcessResponseRETR(response);
415 break;
416 case COMMAND_CWD:
417 rv = ProcessResponseCWD(response);
418 break;
419 case COMMAND_LIST:
420 rv = ProcessResponseLIST(response);
421 break;
422 case COMMAND_QUIT:
423 rv = ProcessResponseQUIT(response);
424 break;
425 default:
426 LOG(DFATAL) << "Unexpected value of command_sent_: " << command_sent_;
427 return ERR_UNEXPECTED;
430 // We may get multiple responses for some commands,
431 // see http://crbug.com/18036.
432 while (ctrl_response_buffer_->ResponseAvailable() && rv == OK) {
433 response = ctrl_response_buffer_->PopResponse();
435 switch (command_sent_) {
436 case COMMAND_RETR:
437 rv = ProcessResponseRETR(response);
438 break;
439 case COMMAND_LIST:
440 rv = ProcessResponseLIST(response);
441 break;
442 default:
443 // Multiple responses for other commands are invalid.
444 return Stop(ERR_INVALID_RESPONSE);
448 return rv;
451 // Used to prepare and send FTP command.
452 int FtpNetworkTransaction::SendFtpCommand(const std::string& command,
453 const std::string& command_for_log,
454 Command cmd) {
455 // If we send a new command when we still have unprocessed responses
456 // for previous commands, the response receiving code will have no way to know
457 // which responses are for which command.
458 DCHECK(!ctrl_response_buffer_->ResponseAvailable());
460 DCHECK(!write_command_buf_.get());
461 DCHECK(!write_buf_.get());
463 if (!IsValidFTPCommandString(command)) {
464 // Callers should validate the command themselves and return a more specific
465 // error code.
466 NOTREACHED();
467 return Stop(ERR_UNEXPECTED);
470 command_sent_ = cmd;
472 write_command_buf_ = new IOBufferWithSize(command.length() + 2);
473 write_buf_ = new DrainableIOBuffer(write_command_buf_.get(),
474 write_command_buf_->size());
475 memcpy(write_command_buf_->data(), command.data(), command.length());
476 memcpy(write_command_buf_->data() + command.length(), kCRLF, 2);
478 net_log_.AddEvent(NetLog::TYPE_FTP_COMMAND_SENT,
479 NetLog::StringCallback("command", &command_for_log));
481 next_state_ = STATE_CTRL_WRITE;
482 return OK;
485 std::string FtpNetworkTransaction::GetRequestPathForFtpCommand(
486 bool is_directory) const {
487 std::string path(current_remote_directory_);
488 if (request_->url.has_path()) {
489 std::string gurl_path(request_->url.path());
491 // Get rid of the typecode, see RFC 1738 section 3.2.2. FTP url-path.
492 std::string::size_type pos = gurl_path.rfind(';');
493 if (pos != std::string::npos)
494 gurl_path.resize(pos);
496 path.append(gurl_path);
498 // Make sure that if the path is expected to be a file, it won't end
499 // with a trailing slash.
500 if (!is_directory && path.length() > 1 && path[path.length() - 1] == '/')
501 path.erase(path.length() - 1);
502 UnescapeRule::Type unescape_rules = UnescapeRule::SPACES |
503 UnescapeRule::URL_SPECIAL_CHARS;
504 // This may unescape to non-ASCII characters, but we allow that. See the
505 // comment for IsValidFTPCommandString.
506 path = net::UnescapeURLComponent(path, unescape_rules);
508 if (system_type_ == SYSTEM_TYPE_VMS) {
509 if (is_directory)
510 path = FtpUtil::UnixDirectoryPathToVMS(path);
511 else
512 path = FtpUtil::UnixFilePathToVMS(path);
515 DCHECK(IsValidFTPCommandString(path));
516 return path;
519 void FtpNetworkTransaction::DetectTypecode() {
520 if (!request_->url.has_path())
521 return;
522 std::string gurl_path(request_->url.path());
524 // Extract the typecode, see RFC 1738 section 3.2.2. FTP url-path.
525 std::string::size_type pos = gurl_path.rfind(';');
526 if (pos == std::string::npos)
527 return;
528 std::string typecode_string(gurl_path.substr(pos));
529 if (typecode_string == ";type=a") {
530 data_type_ = DATA_TYPE_ASCII;
531 resource_type_ = RESOURCE_TYPE_FILE;
532 } else if (typecode_string == ";type=i") {
533 data_type_ = DATA_TYPE_IMAGE;
534 resource_type_ = RESOURCE_TYPE_FILE;
535 } else if (typecode_string == ";type=d") {
536 resource_type_ = RESOURCE_TYPE_DIRECTORY;
540 int FtpNetworkTransaction::DoLoop(int result) {
541 DCHECK(next_state_ != STATE_NONE);
543 int rv = result;
544 do {
545 State state = next_state_;
546 next_state_ = STATE_NONE;
547 switch (state) {
548 case STATE_CTRL_RESOLVE_HOST:
549 DCHECK(rv == OK);
550 rv = DoCtrlResolveHost();
551 break;
552 case STATE_CTRL_RESOLVE_HOST_COMPLETE:
553 rv = DoCtrlResolveHostComplete(rv);
554 break;
555 case STATE_CTRL_CONNECT:
556 DCHECK(rv == OK);
557 rv = DoCtrlConnect();
558 break;
559 case STATE_CTRL_CONNECT_COMPLETE:
560 rv = DoCtrlConnectComplete(rv);
561 break;
562 case STATE_CTRL_READ:
563 DCHECK(rv == OK);
564 rv = DoCtrlRead();
565 break;
566 case STATE_CTRL_READ_COMPLETE:
567 rv = DoCtrlReadComplete(rv);
568 break;
569 case STATE_CTRL_WRITE:
570 DCHECK(rv == OK);
571 rv = DoCtrlWrite();
572 break;
573 case STATE_CTRL_WRITE_COMPLETE:
574 rv = DoCtrlWriteComplete(rv);
575 break;
576 case STATE_CTRL_WRITE_USER:
577 DCHECK(rv == OK);
578 rv = DoCtrlWriteUSER();
579 break;
580 case STATE_CTRL_WRITE_PASS:
581 DCHECK(rv == OK);
582 rv = DoCtrlWritePASS();
583 break;
584 case STATE_CTRL_WRITE_SYST:
585 DCHECK(rv == OK);
586 rv = DoCtrlWriteSYST();
587 break;
588 case STATE_CTRL_WRITE_PWD:
589 DCHECK(rv == OK);
590 rv = DoCtrlWritePWD();
591 break;
592 case STATE_CTRL_WRITE_TYPE:
593 DCHECK(rv == OK);
594 rv = DoCtrlWriteTYPE();
595 break;
596 case STATE_CTRL_WRITE_EPSV:
597 DCHECK(rv == OK);
598 rv = DoCtrlWriteEPSV();
599 break;
600 case STATE_CTRL_WRITE_PASV:
601 DCHECK(rv == OK);
602 rv = DoCtrlWritePASV();
603 break;
604 case STATE_CTRL_WRITE_RETR:
605 DCHECK(rv == OK);
606 rv = DoCtrlWriteRETR();
607 break;
608 case STATE_CTRL_WRITE_SIZE:
609 DCHECK(rv == OK);
610 rv = DoCtrlWriteSIZE();
611 break;
612 case STATE_CTRL_WRITE_CWD:
613 DCHECK(rv == OK);
614 rv = DoCtrlWriteCWD();
615 break;
616 case STATE_CTRL_WRITE_LIST:
617 DCHECK(rv == OK);
618 rv = DoCtrlWriteLIST();
619 break;
620 case STATE_CTRL_WRITE_QUIT:
621 DCHECK(rv == OK);
622 rv = DoCtrlWriteQUIT();
623 break;
624 case STATE_DATA_CONNECT:
625 DCHECK(rv == OK);
626 rv = DoDataConnect();
627 break;
628 case STATE_DATA_CONNECT_COMPLETE:
629 rv = DoDataConnectComplete(rv);
630 break;
631 case STATE_DATA_READ:
632 DCHECK(rv == OK);
633 rv = DoDataRead();
634 break;
635 case STATE_DATA_READ_COMPLETE:
636 rv = DoDataReadComplete(rv);
637 break;
638 default:
639 NOTREACHED() << "bad state";
640 rv = ERR_UNEXPECTED;
641 break;
643 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
644 return rv;
647 int FtpNetworkTransaction::DoCtrlResolveHost() {
648 next_state_ = STATE_CTRL_RESOLVE_HOST_COMPLETE;
650 HostResolver::RequestInfo info(HostPortPair::FromURL(request_->url));
651 // No known referrer.
652 return resolver_.Resolve(
653 info,
654 DEFAULT_PRIORITY,
655 &addresses_,
656 base::Bind(&FtpNetworkTransaction::OnIOComplete, base::Unretained(this)),
657 net_log_);
660 int FtpNetworkTransaction::DoCtrlResolveHostComplete(int result) {
661 if (result == OK)
662 next_state_ = STATE_CTRL_CONNECT;
663 return result;
666 int FtpNetworkTransaction::DoCtrlConnect() {
667 next_state_ = STATE_CTRL_CONNECT_COMPLETE;
668 ctrl_socket_ = socket_factory_->CreateTransportClientSocket(
669 addresses_, net_log_.net_log(), net_log_.source());
670 net_log_.AddEvent(
671 NetLog::TYPE_FTP_CONTROL_CONNECTION,
672 ctrl_socket_->NetLog().source().ToEventParametersCallback());
673 return ctrl_socket_->Connect(io_callback_);
676 int FtpNetworkTransaction::DoCtrlConnectComplete(int result) {
677 if (result == OK) {
678 // Put the peer's IP address and port into the response.
679 IPEndPoint ip_endpoint;
680 result = ctrl_socket_->GetPeerAddress(&ip_endpoint);
681 if (result == OK) {
682 response_.socket_address = HostPortPair::FromIPEndPoint(ip_endpoint);
683 next_state_ = STATE_CTRL_READ;
685 if (ip_endpoint.GetFamily() == ADDRESS_FAMILY_IPV4) {
686 // Do not use EPSV for IPv4 connections. Some servers become confused
687 // and we time out while waiting to connect. PASV is perfectly fine for
688 // IPv4. Note that this blacklists IPv4 not to use EPSV instead of
689 // whitelisting IPv6 to use it, to make the code more future-proof:
690 // all future protocols should just use EPSV.
691 use_epsv_ = false;
695 return result;
698 int FtpNetworkTransaction::DoCtrlRead() {
699 next_state_ = STATE_CTRL_READ_COMPLETE;
700 return ctrl_socket_->Read(read_ctrl_buf_.get(), kCtrlBufLen, io_callback_);
703 int FtpNetworkTransaction::DoCtrlReadComplete(int result) {
704 if (result == 0) {
705 // Some servers (for example Pure-FTPd) apparently close the control
706 // connection when anonymous login is not permitted. For more details
707 // see http://crbug.com/25023.
708 if (command_sent_ == COMMAND_USER &&
709 credentials_.username() == base::ASCIIToUTF16("anonymous")) {
710 response_.needs_auth = true;
712 return Stop(ERR_EMPTY_RESPONSE);
714 if (result < 0)
715 return Stop(result);
717 ctrl_response_buffer_->ConsumeData(read_ctrl_buf_->data(), result);
719 if (!ctrl_response_buffer_->ResponseAvailable()) {
720 // Read more data from the control socket.
721 next_state_ = STATE_CTRL_READ;
722 return OK;
725 return ProcessCtrlResponse();
728 int FtpNetworkTransaction::DoCtrlWrite() {
729 next_state_ = STATE_CTRL_WRITE_COMPLETE;
731 return ctrl_socket_->Write(
732 write_buf_.get(), write_buf_->BytesRemaining(), io_callback_);
735 int FtpNetworkTransaction::DoCtrlWriteComplete(int result) {
736 if (result < 0)
737 return result;
739 write_buf_->DidConsume(result);
740 if (write_buf_->BytesRemaining() == 0) {
741 // Clear the write buffer.
742 write_buf_ = NULL;
743 write_command_buf_ = NULL;
745 next_state_ = STATE_CTRL_READ;
746 } else {
747 next_state_ = STATE_CTRL_WRITE;
749 return OK;
752 // FTP Commands and responses
754 // USER Command.
755 int FtpNetworkTransaction::DoCtrlWriteUSER() {
756 std::string command = "USER " + base::UTF16ToUTF8(credentials_.username());
758 if (!IsValidFTPCommandString(command))
759 return Stop(ERR_MALFORMED_IDENTITY);
761 next_state_ = STATE_CTRL_READ;
762 return SendFtpCommand(command, "USER ***", COMMAND_USER);
765 int FtpNetworkTransaction::ProcessResponseUSER(
766 const FtpCtrlResponse& response) {
767 switch (GetErrorClass(response.status_code)) {
768 case ERROR_CLASS_OK:
769 next_state_ = STATE_CTRL_WRITE_SYST;
770 break;
771 case ERROR_CLASS_INFO_NEEDED:
772 next_state_ = STATE_CTRL_WRITE_PASS;
773 break;
774 case ERROR_CLASS_TRANSIENT_ERROR:
775 case ERROR_CLASS_PERMANENT_ERROR:
776 response_.needs_auth = true;
777 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
778 default:
779 NOTREACHED();
780 return Stop(ERR_UNEXPECTED);
782 return OK;
785 // PASS command.
786 int FtpNetworkTransaction::DoCtrlWritePASS() {
787 std::string command = "PASS " + base::UTF16ToUTF8(credentials_.password());
789 if (!IsValidFTPCommandString(command))
790 return Stop(ERR_MALFORMED_IDENTITY);
792 next_state_ = STATE_CTRL_READ;
793 return SendFtpCommand(command, "PASS ***", COMMAND_PASS);
796 int FtpNetworkTransaction::ProcessResponsePASS(
797 const FtpCtrlResponse& response) {
798 switch (GetErrorClass(response.status_code)) {
799 case ERROR_CLASS_OK:
800 next_state_ = STATE_CTRL_WRITE_SYST;
801 break;
802 case ERROR_CLASS_INFO_NEEDED:
803 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
804 case ERROR_CLASS_TRANSIENT_ERROR:
805 case ERROR_CLASS_PERMANENT_ERROR:
806 response_.needs_auth = true;
807 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
808 default:
809 NOTREACHED();
810 return Stop(ERR_UNEXPECTED);
812 return OK;
815 // SYST command.
816 int FtpNetworkTransaction::DoCtrlWriteSYST() {
817 std::string command = "SYST";
818 next_state_ = STATE_CTRL_READ;
819 return SendFtpCommand(command, command, COMMAND_SYST);
822 int FtpNetworkTransaction::ProcessResponseSYST(
823 const FtpCtrlResponse& response) {
824 switch (GetErrorClass(response.status_code)) {
825 case ERROR_CLASS_INITIATED:
826 return Stop(ERR_INVALID_RESPONSE);
827 case ERROR_CLASS_OK: {
828 // All important info should be on the first line.
829 std::string line = response.lines[0];
830 // The response should be ASCII, which allows us to do case-insensitive
831 // comparisons easily. If it is not ASCII, we leave the system type
832 // as unknown.
833 if (base::IsStringASCII(line)) {
834 line = base::StringToLowerASCII(line);
836 // Remove all whitespace, to correctly handle cases like fancy "V M S"
837 // response instead of "VMS".
838 base::RemoveChars(line, base::kWhitespaceASCII, &line);
840 // The "magic" strings we test for below have been gathered by an
841 // empirical study. VMS needs to come first because some VMS systems
842 // also respond with "UNIX emulation", which is not perfect. It is much
843 // more reliable to talk to these servers in their native language.
844 if (line.find("vms") != std::string::npos) {
845 system_type_ = SYSTEM_TYPE_VMS;
846 } else if (line.find("l8") != std::string::npos ||
847 line.find("unix") != std::string::npos ||
848 line.find("bsd") != std::string::npos) {
849 system_type_ = SYSTEM_TYPE_UNIX;
850 } else if (line.find("win32") != std::string::npos ||
851 line.find("windows") != std::string::npos) {
852 system_type_ = SYSTEM_TYPE_WINDOWS;
853 } else if (line.find("os/2") != std::string::npos) {
854 system_type_ = SYSTEM_TYPE_OS2;
857 next_state_ = STATE_CTRL_WRITE_PWD;
858 break;
860 case ERROR_CLASS_INFO_NEEDED:
861 return Stop(ERR_INVALID_RESPONSE);
862 case ERROR_CLASS_TRANSIENT_ERROR:
863 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
864 case ERROR_CLASS_PERMANENT_ERROR:
865 // Server does not recognize the SYST command so proceed.
866 next_state_ = STATE_CTRL_WRITE_PWD;
867 break;
868 default:
869 NOTREACHED();
870 return Stop(ERR_UNEXPECTED);
872 return OK;
875 // PWD command.
876 int FtpNetworkTransaction::DoCtrlWritePWD() {
877 std::string command = "PWD";
878 next_state_ = STATE_CTRL_READ;
879 return SendFtpCommand(command, command, COMMAND_PWD);
882 int FtpNetworkTransaction::ProcessResponsePWD(const FtpCtrlResponse& response) {
883 switch (GetErrorClass(response.status_code)) {
884 case ERROR_CLASS_INITIATED:
885 return Stop(ERR_INVALID_RESPONSE);
886 case ERROR_CLASS_OK: {
887 // The info we look for should be on the first line.
888 std::string line = response.lines[0];
889 if (line.empty())
890 return Stop(ERR_INVALID_RESPONSE);
891 std::string::size_type quote_pos = line.find('"');
892 if (quote_pos != std::string::npos) {
893 line = line.substr(quote_pos + 1);
894 quote_pos = line.find('"');
895 if (quote_pos == std::string::npos)
896 return Stop(ERR_INVALID_RESPONSE);
897 line = line.substr(0, quote_pos);
899 if (system_type_ == SYSTEM_TYPE_VMS)
900 line = FtpUtil::VMSPathToUnix(line);
901 if (line.length() && line[line.length() - 1] == '/')
902 line.erase(line.length() - 1);
903 current_remote_directory_ = line;
904 next_state_ = STATE_CTRL_WRITE_TYPE;
905 break;
907 case ERROR_CLASS_INFO_NEEDED:
908 return Stop(ERR_INVALID_RESPONSE);
909 case ERROR_CLASS_TRANSIENT_ERROR:
910 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
911 case ERROR_CLASS_PERMANENT_ERROR:
912 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
913 default:
914 NOTREACHED();
915 return Stop(ERR_UNEXPECTED);
917 return OK;
920 // TYPE command.
921 int FtpNetworkTransaction::DoCtrlWriteTYPE() {
922 std::string command = "TYPE ";
923 if (data_type_ == DATA_TYPE_ASCII) {
924 command += "A";
925 } else if (data_type_ == DATA_TYPE_IMAGE) {
926 command += "I";
927 } else {
928 NOTREACHED();
929 return Stop(ERR_UNEXPECTED);
931 next_state_ = STATE_CTRL_READ;
932 return SendFtpCommand(command, command, COMMAND_TYPE);
935 int FtpNetworkTransaction::ProcessResponseTYPE(
936 const FtpCtrlResponse& response) {
937 switch (GetErrorClass(response.status_code)) {
938 case ERROR_CLASS_INITIATED:
939 return Stop(ERR_INVALID_RESPONSE);
940 case ERROR_CLASS_OK:
941 next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV;
942 break;
943 case ERROR_CLASS_INFO_NEEDED:
944 return Stop(ERR_INVALID_RESPONSE);
945 case ERROR_CLASS_TRANSIENT_ERROR:
946 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
947 case ERROR_CLASS_PERMANENT_ERROR:
948 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
949 default:
950 NOTREACHED();
951 return Stop(ERR_UNEXPECTED);
953 return OK;
956 // EPSV command
957 int FtpNetworkTransaction::DoCtrlWriteEPSV() {
958 const std::string command = "EPSV";
959 next_state_ = STATE_CTRL_READ;
960 return SendFtpCommand(command, command, COMMAND_EPSV);
963 int FtpNetworkTransaction::ProcessResponseEPSV(
964 const FtpCtrlResponse& response) {
965 switch (GetErrorClass(response.status_code)) {
966 case ERROR_CLASS_INITIATED:
967 return Stop(ERR_INVALID_RESPONSE);
968 case ERROR_CLASS_OK:
969 if (!ExtractPortFromEPSVResponse( response, &data_connection_port_))
970 return Stop(ERR_INVALID_RESPONSE);
971 if (data_connection_port_ < 1024 ||
972 !IsPortAllowedByFtp(data_connection_port_))
973 return Stop(ERR_UNSAFE_PORT);
974 next_state_ = STATE_DATA_CONNECT;
975 break;
976 case ERROR_CLASS_INFO_NEEDED:
977 return Stop(ERR_INVALID_RESPONSE);
978 case ERROR_CLASS_TRANSIENT_ERROR:
979 case ERROR_CLASS_PERMANENT_ERROR:
980 use_epsv_ = false;
981 next_state_ = STATE_CTRL_WRITE_PASV;
982 return OK;
983 default:
984 NOTREACHED();
985 return Stop(ERR_UNEXPECTED);
987 return OK;
990 // PASV command
991 int FtpNetworkTransaction::DoCtrlWritePASV() {
992 std::string command = "PASV";
993 next_state_ = STATE_CTRL_READ;
994 return SendFtpCommand(command, command, COMMAND_PASV);
997 int FtpNetworkTransaction::ProcessResponsePASV(
998 const FtpCtrlResponse& response) {
999 switch (GetErrorClass(response.status_code)) {
1000 case ERROR_CLASS_INITIATED:
1001 return Stop(ERR_INVALID_RESPONSE);
1002 case ERROR_CLASS_OK:
1003 if (!ExtractPortFromPASVResponse(response, &data_connection_port_))
1004 return Stop(ERR_INVALID_RESPONSE);
1005 if (data_connection_port_ < 1024 ||
1006 !IsPortAllowedByFtp(data_connection_port_))
1007 return Stop(ERR_UNSAFE_PORT);
1008 next_state_ = STATE_DATA_CONNECT;
1009 break;
1010 case ERROR_CLASS_INFO_NEEDED:
1011 return Stop(ERR_INVALID_RESPONSE);
1012 case ERROR_CLASS_TRANSIENT_ERROR:
1013 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1014 case ERROR_CLASS_PERMANENT_ERROR:
1015 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1016 default:
1017 NOTREACHED();
1018 return Stop(ERR_UNEXPECTED);
1020 return OK;
1023 // RETR command
1024 int FtpNetworkTransaction::DoCtrlWriteRETR() {
1025 std::string command = "RETR " + GetRequestPathForFtpCommand(false);
1026 next_state_ = STATE_CTRL_READ;
1027 return SendFtpCommand(command, command, COMMAND_RETR);
1030 int FtpNetworkTransaction::ProcessResponseRETR(
1031 const FtpCtrlResponse& response) {
1032 switch (GetErrorClass(response.status_code)) {
1033 case ERROR_CLASS_INITIATED:
1034 // We want the client to start reading the response at this point.
1035 // It got here either through Start or RestartWithAuth. We want that
1036 // method to complete. Not setting next state here will make DoLoop exit
1037 // and in turn make Start/RestartWithAuth complete.
1038 resource_type_ = RESOURCE_TYPE_FILE;
1039 break;
1040 case ERROR_CLASS_OK:
1041 resource_type_ = RESOURCE_TYPE_FILE;
1042 next_state_ = STATE_CTRL_WRITE_QUIT;
1043 break;
1044 case ERROR_CLASS_INFO_NEEDED:
1045 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1046 case ERROR_CLASS_TRANSIENT_ERROR:
1047 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1048 case ERROR_CLASS_PERMANENT_ERROR:
1049 // Code 550 means "Failed to open file". Other codes are unrelated,
1050 // like "Not logged in" etc.
1051 if (response.status_code != 550 || resource_type_ == RESOURCE_TYPE_FILE)
1052 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1054 // It's possible that RETR failed because the path is a directory.
1055 resource_type_ = RESOURCE_TYPE_DIRECTORY;
1057 // We're going to try CWD next, but first send a PASV one more time,
1058 // because some FTP servers, including FileZilla, require that.
1059 // See http://crbug.com/25316.
1060 next_state_ = use_epsv_ ? STATE_CTRL_WRITE_EPSV : STATE_CTRL_WRITE_PASV;
1061 break;
1062 default:
1063 NOTREACHED();
1064 return Stop(ERR_UNEXPECTED);
1067 // We should be sure about our resource type now. Otherwise we risk
1068 // an infinite loop (RETR can later send CWD, and CWD can later send RETR).
1069 DCHECK_NE(RESOURCE_TYPE_UNKNOWN, resource_type_);
1071 return OK;
1074 // SIZE command
1075 int FtpNetworkTransaction::DoCtrlWriteSIZE() {
1076 std::string command = "SIZE " + GetRequestPathForFtpCommand(false);
1077 next_state_ = STATE_CTRL_READ;
1078 return SendFtpCommand(command, command, COMMAND_SIZE);
1081 int FtpNetworkTransaction::ProcessResponseSIZE(
1082 const FtpCtrlResponse& response) {
1083 State state_after_size;
1084 if (resource_type_ == RESOURCE_TYPE_FILE)
1085 state_after_size = STATE_CTRL_WRITE_RETR;
1086 else
1087 state_after_size = STATE_CTRL_WRITE_CWD;
1089 switch (GetErrorClass(response.status_code)) {
1090 case ERROR_CLASS_INITIATED:
1091 next_state_ = state_after_size;
1092 break;
1093 case ERROR_CLASS_OK:
1094 if (response.lines.size() != 1)
1095 return Stop(ERR_INVALID_RESPONSE);
1096 int64 size;
1097 if (!base::StringToInt64(response.lines[0], &size))
1098 return Stop(ERR_INVALID_RESPONSE);
1099 if (size < 0)
1100 return Stop(ERR_INVALID_RESPONSE);
1102 // A successful response to SIZE does not mean the resource is a file.
1103 // Some FTP servers (for example, the qnx one) send a SIZE even for
1104 // directories.
1105 response_.expected_content_size = size;
1107 next_state_ = state_after_size;
1108 break;
1109 case ERROR_CLASS_INFO_NEEDED:
1110 next_state_ = state_after_size;
1111 break;
1112 case ERROR_CLASS_TRANSIENT_ERROR:
1113 ResetDataConnectionAfterError(state_after_size);
1114 break;
1115 case ERROR_CLASS_PERMANENT_ERROR:
1116 // It's possible that SIZE failed because the path is a directory.
1117 if (resource_type_ == RESOURCE_TYPE_UNKNOWN &&
1118 response.status_code != 550) {
1119 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1122 ResetDataConnectionAfterError(state_after_size);
1123 break;
1124 default:
1125 NOTREACHED();
1126 return Stop(ERR_UNEXPECTED);
1129 return OK;
1132 // CWD command
1133 int FtpNetworkTransaction::DoCtrlWriteCWD() {
1134 std::string command = "CWD " + GetRequestPathForFtpCommand(true);
1135 next_state_ = STATE_CTRL_READ;
1136 return SendFtpCommand(command, command, COMMAND_CWD);
1139 int FtpNetworkTransaction::ProcessResponseCWD(const FtpCtrlResponse& response) {
1140 // We should never issue CWD if we know the target resource is a file.
1141 DCHECK_NE(RESOURCE_TYPE_FILE, resource_type_);
1143 switch (GetErrorClass(response.status_code)) {
1144 case ERROR_CLASS_INITIATED:
1145 return Stop(ERR_INVALID_RESPONSE);
1146 case ERROR_CLASS_OK:
1147 next_state_ = STATE_CTRL_WRITE_LIST;
1148 break;
1149 case ERROR_CLASS_INFO_NEEDED:
1150 return Stop(ERR_INVALID_RESPONSE);
1151 case ERROR_CLASS_TRANSIENT_ERROR:
1152 // Some FTP servers send response 451 (not a valid CWD response according
1153 // to RFC 959) instead of 550.
1154 if (response.status_code == 451)
1155 return ProcessResponseCWDNotADirectory();
1157 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1158 case ERROR_CLASS_PERMANENT_ERROR:
1159 if (response.status_code == 550)
1160 return ProcessResponseCWDNotADirectory();
1162 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1163 default:
1164 NOTREACHED();
1165 return Stop(ERR_UNEXPECTED);
1168 return OK;
1171 int FtpNetworkTransaction::ProcessResponseCWDNotADirectory() {
1172 if (resource_type_ == RESOURCE_TYPE_DIRECTORY) {
1173 // We're assuming that the resource is a directory, but the server
1174 // says it's not true. The most probable interpretation is that it
1175 // doesn't exist (with FTP we can't be sure).
1176 return Stop(ERR_FILE_NOT_FOUND);
1179 // We are here because SIZE failed and we are not sure what the resource
1180 // type is. It could still be file, and SIZE could fail because of
1181 // an access error (http://crbug.com/56734). Try RETR just to be sure.
1182 resource_type_ = RESOURCE_TYPE_FILE;
1184 ResetDataConnectionAfterError(STATE_CTRL_WRITE_RETR);
1185 return OK;
1188 // LIST command
1189 int FtpNetworkTransaction::DoCtrlWriteLIST() {
1190 // Use the -l option for mod_ftp configured in LISTIsNLST mode: the option
1191 // forces LIST output instead of NLST (which would be ambiguous for us
1192 // to parse).
1193 std::string command("LIST -l");
1194 if (system_type_ == SYSTEM_TYPE_VMS)
1195 command = "LIST *.*;0";
1197 next_state_ = STATE_CTRL_READ;
1198 return SendFtpCommand(command, command, COMMAND_LIST);
1201 int FtpNetworkTransaction::ProcessResponseLIST(
1202 const FtpCtrlResponse& response) {
1203 switch (GetErrorClass(response.status_code)) {
1204 case ERROR_CLASS_INITIATED:
1205 // We want the client to start reading the response at this point.
1206 // It got here either through Start or RestartWithAuth. We want that
1207 // method to complete. Not setting next state here will make DoLoop exit
1208 // and in turn make Start/RestartWithAuth complete.
1209 response_.is_directory_listing = true;
1210 break;
1211 case ERROR_CLASS_OK:
1212 response_.is_directory_listing = true;
1213 next_state_ = STATE_CTRL_WRITE_QUIT;
1214 break;
1215 case ERROR_CLASS_INFO_NEEDED:
1216 return Stop(ERR_INVALID_RESPONSE);
1217 case ERROR_CLASS_TRANSIENT_ERROR:
1218 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1219 case ERROR_CLASS_PERMANENT_ERROR:
1220 return Stop(GetNetErrorCodeForFtpResponseCode(response.status_code));
1221 default:
1222 NOTREACHED();
1223 return Stop(ERR_UNEXPECTED);
1225 return OK;
1228 // QUIT command
1229 int FtpNetworkTransaction::DoCtrlWriteQUIT() {
1230 std::string command = "QUIT";
1231 next_state_ = STATE_CTRL_READ;
1232 return SendFtpCommand(command, command, COMMAND_QUIT);
1235 int FtpNetworkTransaction::ProcessResponseQUIT(
1236 const FtpCtrlResponse& response) {
1237 ctrl_socket_->Disconnect();
1238 return last_error_;
1241 // Data Connection
1243 int FtpNetworkTransaction::DoDataConnect() {
1244 next_state_ = STATE_DATA_CONNECT_COMPLETE;
1245 IPEndPoint ip_endpoint;
1246 AddressList data_address;
1247 // Connect to the same host as the control socket to prevent PASV port
1248 // scanning attacks.
1249 int rv = ctrl_socket_->GetPeerAddress(&ip_endpoint);
1250 if (rv != OK)
1251 return Stop(rv);
1252 data_address = AddressList::CreateFromIPAddress(
1253 ip_endpoint.address(), data_connection_port_);
1254 data_socket_ = socket_factory_->CreateTransportClientSocket(
1255 data_address, net_log_.net_log(), net_log_.source());
1256 net_log_.AddEvent(
1257 NetLog::TYPE_FTP_DATA_CONNECTION,
1258 data_socket_->NetLog().source().ToEventParametersCallback());
1259 return data_socket_->Connect(io_callback_);
1262 int FtpNetworkTransaction::DoDataConnectComplete(int result) {
1263 if (result != OK && use_epsv_) {
1264 // It's possible we hit a broken server, sadly. They can break in different
1265 // ways. Some time out, some reset a connection. Fall back to PASV.
1266 // TODO(phajdan.jr): remember it for future transactions with this server.
1267 // TODO(phajdan.jr): write a test for this code path.
1268 use_epsv_ = false;
1269 next_state_ = STATE_CTRL_WRITE_PASV;
1270 return OK;
1273 // Only record the connection error after we've applied all our fallbacks.
1274 // We want to capture the final error, one we're not going to recover from.
1275 RecordDataConnectionError(result);
1277 if (result != OK)
1278 return Stop(result);
1280 next_state_ = state_after_data_connect_complete_;
1281 return OK;
1284 int FtpNetworkTransaction::DoDataRead() {
1285 DCHECK(read_data_buf_.get());
1286 DCHECK_GT(read_data_buf_len_, 0);
1288 if (data_socket_ == NULL || !data_socket_->IsConnected()) {
1289 // If we don't destroy the data socket completely, some servers will wait
1290 // for us (http://crbug.com/21127). The half-closed TCP connection needs
1291 // to be closed on our side too.
1292 data_socket_.reset();
1294 if (ctrl_socket_->IsConnected()) {
1295 // Wait for the server's response, we should get it before sending QUIT.
1296 next_state_ = STATE_CTRL_READ;
1297 return OK;
1300 // We are no longer connected to the server, so just finish the transaction.
1301 return Stop(OK);
1304 next_state_ = STATE_DATA_READ_COMPLETE;
1305 read_data_buf_->data()[0] = 0;
1306 return data_socket_->Read(
1307 read_data_buf_.get(), read_data_buf_len_, io_callback_);
1310 int FtpNetworkTransaction::DoDataReadComplete(int result) {
1311 return result;
1314 // We're using a histogram as a group of counters, with one bucket for each
1315 // enumeration value. We're only interested in the values of the counters.
1316 // Ignore the shape, average, and standard deviation of the histograms because
1317 // they are meaningless.
1319 // We use two histograms. In the first histogram we tally whether the user has
1320 // seen an error of that type during the session. In the second histogram we
1321 // tally the total number of times the users sees each errer.
1322 void FtpNetworkTransaction::RecordDataConnectionError(int result) {
1323 // Gather data for http://crbug.com/3073. See how many users have trouble
1324 // establishing FTP data connection in passive FTP mode.
1325 enum {
1326 // Data connection successful.
1327 NET_ERROR_OK = 0,
1329 // Local firewall blocked the connection.
1330 NET_ERROR_ACCESS_DENIED = 1,
1332 // Connection timed out.
1333 NET_ERROR_TIMED_OUT = 2,
1335 // Connection has been estabilished, but then got broken (either reset
1336 // or aborted).
1337 NET_ERROR_CONNECTION_BROKEN = 3,
1339 // Connection has been refused.
1340 NET_ERROR_CONNECTION_REFUSED = 4,
1342 // No connection to the internet.
1343 NET_ERROR_INTERNET_DISCONNECTED = 5,
1345 // Could not reach the destination address.
1346 NET_ERROR_ADDRESS_UNREACHABLE = 6,
1348 // A programming error in our network stack.
1349 NET_ERROR_UNEXPECTED = 7,
1351 // Other kind of error.
1352 NET_ERROR_OTHER = 20,
1354 NUM_OF_NET_ERROR_TYPES
1355 } type;
1356 switch (result) {
1357 case OK:
1358 type = NET_ERROR_OK;
1359 break;
1360 case ERR_ACCESS_DENIED:
1361 case ERR_NETWORK_ACCESS_DENIED:
1362 type = NET_ERROR_ACCESS_DENIED;
1363 break;
1364 case ERR_TIMED_OUT:
1365 type = NET_ERROR_TIMED_OUT;
1366 break;
1367 case ERR_CONNECTION_ABORTED:
1368 case ERR_CONNECTION_RESET:
1369 case ERR_CONNECTION_CLOSED:
1370 type = NET_ERROR_CONNECTION_BROKEN;
1371 break;
1372 case ERR_CONNECTION_FAILED:
1373 case ERR_CONNECTION_REFUSED:
1374 type = NET_ERROR_CONNECTION_REFUSED;
1375 break;
1376 case ERR_INTERNET_DISCONNECTED:
1377 type = NET_ERROR_INTERNET_DISCONNECTED;
1378 break;
1379 case ERR_ADDRESS_INVALID:
1380 case ERR_ADDRESS_UNREACHABLE:
1381 type = NET_ERROR_ADDRESS_UNREACHABLE;
1382 break;
1383 case ERR_UNEXPECTED:
1384 type = NET_ERROR_UNEXPECTED;
1385 break;
1386 default:
1387 type = NET_ERROR_OTHER;
1388 break;
1390 static bool had_error_type[NUM_OF_NET_ERROR_TYPES];
1392 DCHECK(type >= 0 && type < NUM_OF_NET_ERROR_TYPES);
1393 if (!had_error_type[type]) {
1394 had_error_type[type] = true;
1395 UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorHappened",
1396 type, NUM_OF_NET_ERROR_TYPES);
1398 UMA_HISTOGRAM_ENUMERATION("Net.FtpDataConnectionErrorCount",
1399 type, NUM_OF_NET_ERROR_TYPES);
1402 } // namespace net