• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/url_request/url_request_new_ftp_job.h"
6 
7 #include "base/compiler_specific.h"
8 #include "base/message_loop.h"
9 #include "net/base/auth.h"
10 #include "net/base/net_errors.h"
11 #include "net/base/net_util.h"
12 #include "net/ftp/ftp_response_info.h"
13 #include "net/ftp/ftp_transaction_factory.h"
14 #include "net/url_request/url_request.h"
15 #include "net/url_request/url_request_context.h"
16 #include "net/url_request/url_request_error_job.h"
17 
URLRequestNewFtpJob(URLRequest * request)18 URLRequestNewFtpJob::URLRequestNewFtpJob(URLRequest* request)
19     : URLRequestJob(request),
20       ALLOW_THIS_IN_INITIALIZER_LIST(
21           start_callback_(this, &URLRequestNewFtpJob::OnStartCompleted)),
22       ALLOW_THIS_IN_INITIALIZER_LIST(
23           read_callback_(this, &URLRequestNewFtpJob::OnReadCompleted)),
24       read_in_progress_(false),
25       context_(request->context()) {
26 }
27 
~URLRequestNewFtpJob()28 URLRequestNewFtpJob::~URLRequestNewFtpJob() {
29 }
30 
31 // static
Factory(URLRequest * request,const std::string & scheme)32 URLRequestJob* URLRequestNewFtpJob::Factory(URLRequest* request,
33                                             const std::string& scheme) {
34   DCHECK_EQ(scheme, "ftp");
35 
36   int port = request->url().IntPort();
37   if (request->url().has_port() &&
38     !net::IsPortAllowedByFtp(port) && !net::IsPortAllowedByOverride(port))
39     return new URLRequestErrorJob(request, net::ERR_UNSAFE_PORT);
40 
41   DCHECK(request->context());
42   DCHECK(request->context()->ftp_transaction_factory());
43   return new URLRequestNewFtpJob(request);
44 }
45 
GetMimeType(std::string * mime_type) const46 bool URLRequestNewFtpJob::GetMimeType(std::string* mime_type) const {
47   if (transaction_->GetResponseInfo()->is_directory_listing) {
48     *mime_type = "text/vnd.chromium.ftp-dir";
49     return true;
50   }
51   return false;
52 }
53 
Start()54 void URLRequestNewFtpJob::Start() {
55   DCHECK(!transaction_.get());
56   request_info_.url = request_->url();
57   StartTransaction();
58 }
59 
Kill()60 void URLRequestNewFtpJob::Kill() {
61   if (!transaction_.get())
62     return;
63   DestroyTransaction();
64   URLRequestJob::Kill();
65 }
66 
GetLoadState() const67 net::LoadState URLRequestNewFtpJob::GetLoadState() const {
68   return transaction_.get() ?
69       transaction_->GetLoadState() : net::LOAD_STATE_IDLE;
70 }
71 
NeedsAuth()72 bool URLRequestNewFtpJob::NeedsAuth() {
73   // Note that we only have to worry about cases where an actual FTP server
74   // requires auth (and not a proxy), because connecting to FTP via proxy
75   // effectively means the browser communicates via HTTP, and uses HTTP's
76   // Proxy-Authenticate protocol when proxy servers require auth.
77   return server_auth_ && server_auth_->state == net::AUTH_STATE_NEED_AUTH;
78 }
79 
GetAuthChallengeInfo(scoped_refptr<net::AuthChallengeInfo> * result)80 void URLRequestNewFtpJob::GetAuthChallengeInfo(
81     scoped_refptr<net::AuthChallengeInfo>* result) {
82   DCHECK((server_auth_ != NULL) &&
83          (server_auth_->state == net::AUTH_STATE_NEED_AUTH));
84   scoped_refptr<net::AuthChallengeInfo> auth_info = new net::AuthChallengeInfo;
85   auth_info->is_proxy = false;
86   auth_info->host_and_port = ASCIIToWide(
87       net::GetHostAndPort(request_->url()));
88   auth_info->scheme = L"";
89   auth_info->realm = L"";
90   result->swap(auth_info);
91 }
92 
SetAuth(const std::wstring & username,const std::wstring & password)93 void URLRequestNewFtpJob::SetAuth(const std::wstring& username,
94                                   const std::wstring& password) {
95   DCHECK(NeedsAuth());
96   server_auth_->state = net::AUTH_STATE_HAVE_AUTH;
97   server_auth_->username = username;
98   server_auth_->password = password;
99 
100   request_->context()->ftp_auth_cache()->Add(request_->url().GetOrigin(),
101                                              username, password);
102 
103   RestartTransactionWithAuth();
104 }
105 
CancelAuth()106 void URLRequestNewFtpJob::CancelAuth() {
107   DCHECK(NeedsAuth());
108   server_auth_->state = net::AUTH_STATE_CANCELED;
109 
110   // Once the auth is cancelled, we proceed with the request as though
111   // there were no auth.  Schedule this for later so that we don't cause
112   // any recursing into the caller as a result of this call.
113   MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
114       this, &URLRequestNewFtpJob::OnStartCompleted, net::OK));
115 }
116 
ReadRawData(net::IOBuffer * buf,int buf_size,int * bytes_read)117 bool URLRequestNewFtpJob::ReadRawData(net::IOBuffer* buf,
118                                       int buf_size,
119                                       int *bytes_read) {
120   DCHECK_NE(buf_size, 0);
121   DCHECK(bytes_read);
122   DCHECK(!read_in_progress_);
123 
124   int rv = transaction_->Read(buf, buf_size, &read_callback_);
125   if (rv >= 0) {
126     *bytes_read = rv;
127     return true;
128   }
129 
130   if (rv == net::ERR_IO_PENDING) {
131     read_in_progress_ = true;
132     SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
133   } else {
134     NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv));
135   }
136   return false;
137 }
138 
OnStartCompleted(int result)139 void URLRequestNewFtpJob::OnStartCompleted(int result) {
140   // If the request was destroyed, then there is no more work to do.
141   if (!request_ || !request_->delegate())
142     return;
143   // If the transaction was destroyed, then the job was cancelled, and
144   // we can just ignore this notification.
145   if (!transaction_.get())
146     return;
147   // Clear the IO_PENDING status
148   SetStatus(URLRequestStatus());
149   if (result == net::OK) {
150     NotifyHeadersComplete();
151   } else if (transaction_->GetResponseInfo()->needs_auth) {
152     GURL origin = request_->url().GetOrigin();
153     if (server_auth_ && server_auth_->state == net::AUTH_STATE_HAVE_AUTH) {
154       request_->context()->ftp_auth_cache()->Remove(origin,
155                                                     server_auth_->username,
156                                                     server_auth_->password);
157     } else if (!server_auth_) {
158       server_auth_ = new net::AuthData();
159     }
160     server_auth_->state = net::AUTH_STATE_NEED_AUTH;
161 
162     net::FtpAuthCache::Entry* cached_auth =
163         request_->context()->ftp_auth_cache()->Lookup(origin);
164 
165     if (cached_auth) {
166       // Retry using cached auth data.
167       SetAuth(cached_auth->username, cached_auth->password);
168     } else {
169       // Prompt for a username/password.
170       NotifyHeadersComplete();
171     }
172   } else {
173     NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result));
174   }
175 }
176 
OnReadCompleted(int result)177 void URLRequestNewFtpJob::OnReadCompleted(int result) {
178   read_in_progress_ = false;
179   if (result == 0) {
180     NotifyDone(URLRequestStatus());
181   } else if (result < 0) {
182     NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result));
183   } else {
184     // Clear the IO_PENDING status
185     SetStatus(URLRequestStatus());
186   }
187   NotifyReadComplete(result);
188 }
189 
RestartTransactionWithAuth()190 void URLRequestNewFtpJob::RestartTransactionWithAuth() {
191   DCHECK(server_auth_ && server_auth_->state == net::AUTH_STATE_HAVE_AUTH);
192 
193   // No matter what, we want to report our status as IO pending since we will
194   // be notifying our consumer asynchronously via OnStartCompleted.
195   SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
196 
197   int rv = transaction_->RestartWithAuth(server_auth_->username,
198                                          server_auth_->password,
199                                          &start_callback_);
200   if (rv == net::ERR_IO_PENDING)
201     return;
202 
203   MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
204       this, &URLRequestNewFtpJob::OnStartCompleted, rv));
205 }
206 
StartTransaction()207 void URLRequestNewFtpJob::StartTransaction() {
208   // Create a transaction.
209   DCHECK(!transaction_.get());
210   DCHECK(request_->context());
211   DCHECK(request_->context()->ftp_transaction_factory());
212 
213   transaction_.reset(
214   request_->context()->ftp_transaction_factory()->CreateTransaction());
215 
216   // No matter what, we want to report our status as IO pending since we will
217   // be notifying our consumer asynchronously via OnStartCompleted.
218   SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
219   int rv;
220   if (transaction_.get()) {
221     rv = transaction_->Start(
222         &request_info_, &start_callback_, request_->load_log());
223     if (rv == net::ERR_IO_PENDING)
224       return;
225   } else {
226     rv = net::ERR_FAILED;
227   }
228   // The transaction started synchronously, but we need to notify the
229   // URLRequest delegate via the message loop.
230   MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
231       this, &URLRequestNewFtpJob::OnStartCompleted, rv));
232 }
233 
DestroyTransaction()234 void URLRequestNewFtpJob::DestroyTransaction() {
235   DCHECK(transaction_.get());
236 
237   transaction_.reset();
238 }
239