1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 2001
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Akkana Peck <akkana@netscape.com> (original author)
24 : * Darin Fisher <darin@meer.net>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either the GNU General Public License Version 2 or later (the "GPL"), or
28 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "nsURIChecker.h"
41 : #include "nsIServiceManager.h"
42 : #include "nsIAuthPrompt.h"
43 : #include "nsIHttpChannel.h"
44 : #include "nsNetUtil.h"
45 : #include "nsString.h"
46 : #include "nsIAsyncVerifyRedirectCallback.h"
47 :
48 : //-----------------------------------------------------------------------------
49 :
50 : static bool
51 0 : ServerIsNES3x(nsIHttpChannel *httpChannel)
52 : {
53 0 : nsCAutoString server;
54 0 : httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Server"), server);
55 : // case sensitive string comparison is OK here. the server string
56 : // is a well-known value, so we should not have to worry about it
57 : // being case-smashed or otherwise case-mutated.
58 : return StringBeginsWith(server,
59 0 : NS_LITERAL_CSTRING("Netscape-Enterprise/3."));
60 : }
61 :
62 : //-----------------------------------------------------------------------------
63 :
64 0 : NS_IMPL_ISUPPORTS6(nsURIChecker,
65 : nsIURIChecker,
66 : nsIRequest,
67 : nsIRequestObserver,
68 : nsIStreamListener,
69 : nsIChannelEventSink,
70 : nsIInterfaceRequestor)
71 :
72 0 : nsURIChecker::nsURIChecker()
73 : : mStatus(NS_OK)
74 : , mIsPending(false)
75 0 : , mAllowHead(true)
76 : {
77 0 : }
78 :
79 : void
80 0 : nsURIChecker::SetStatusAndCallBack(nsresult aStatus)
81 : {
82 0 : mStatus = aStatus;
83 0 : mIsPending = false;
84 :
85 0 : if (mObserver) {
86 0 : mObserver->OnStartRequest(this, mObserverContext);
87 0 : mObserver->OnStopRequest(this, mObserverContext, mStatus);
88 0 : mObserver = nsnull;
89 0 : mObserverContext = nsnull;
90 : }
91 0 : }
92 :
93 : nsresult
94 0 : nsURIChecker::CheckStatus()
95 : {
96 0 : NS_ASSERTION(mChannel, "no channel");
97 :
98 : nsresult status;
99 0 : nsresult rv = mChannel->GetStatus(&status);
100 : // DNS errors and other obvious problems will return failure status
101 0 : if (NS_FAILED(rv) || NS_FAILED(status))
102 0 : return NS_BINDING_FAILED;
103 :
104 : // If status is zero, it might still be an error if it's http:
105 : // http has data even when there's an error like a 404.
106 0 : nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
107 0 : if (!httpChannel)
108 0 : return NS_BINDING_SUCCEEDED;
109 :
110 : PRUint32 responseStatus;
111 0 : rv = httpChannel->GetResponseStatus(&responseStatus);
112 0 : if (NS_FAILED(rv))
113 0 : return NS_BINDING_FAILED;
114 :
115 : // If it's between 200-299, it's valid:
116 0 : if (responseStatus / 100 == 2)
117 0 : return NS_BINDING_SUCCEEDED;
118 :
119 : // If we got a 404 (not found), we need some extra checking:
120 : // toplevel urls from Netscape Enterprise Server 3.6, like the old AOL-
121 : // hosted http://www.mozilla.org, generate a 404 and will have to be
122 : // retried without the head.
123 0 : if (responseStatus == 404) {
124 0 : if (mAllowHead && ServerIsNES3x(httpChannel)) {
125 0 : mAllowHead = false;
126 :
127 : // save the current value of mChannel in case we can't issue
128 : // the new request for some reason.
129 0 : nsCOMPtr<nsIChannel> lastChannel = mChannel;
130 :
131 0 : nsCOMPtr<nsIURI> uri;
132 : PRUint32 loadFlags;
133 :
134 0 : rv = lastChannel->GetOriginalURI(getter_AddRefs(uri));
135 0 : rv |= lastChannel->GetLoadFlags(&loadFlags);
136 :
137 : // XXX we are carrying over the load flags, but what about other
138 : // parameters that may have been set on lastChannel??
139 :
140 0 : if (NS_SUCCEEDED(rv)) {
141 0 : rv = Init(uri);
142 0 : if (NS_SUCCEEDED(rv)) {
143 0 : rv = mChannel->SetLoadFlags(loadFlags);
144 0 : if (NS_SUCCEEDED(rv)) {
145 0 : rv = AsyncCheck(mObserver, mObserverContext);
146 : // if we succeeded in loading the new channel, then we
147 : // want to return without notifying our observer.
148 0 : if (NS_SUCCEEDED(rv))
149 0 : return NS_BASE_STREAM_WOULD_BLOCK;
150 : }
151 : }
152 : }
153 : // it is important to update this so our observer will be able
154 : // to access our baseChannel attribute if they want.
155 0 : mChannel = lastChannel;
156 : }
157 : }
158 :
159 : // If we get here, assume the resource does not exist.
160 0 : return NS_BINDING_FAILED;
161 : }
162 :
163 : //-----------------------------------------------------------------------------
164 : // nsIURIChecker methods:
165 : //-----------------------------------------------------------------------------
166 :
167 : NS_IMETHODIMP
168 0 : nsURIChecker::Init(nsIURI *aURI)
169 : {
170 : nsresult rv;
171 0 : nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
172 0 : if (NS_FAILED(rv)) return rv;
173 :
174 0 : rv = ios->NewChannelFromURI(aURI, getter_AddRefs(mChannel));
175 0 : if (NS_FAILED(rv)) return rv;
176 :
177 0 : if (mAllowHead) {
178 0 : mAllowHead = false;
179 : // See if it's an http channel, which needs special treatment:
180 0 : nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
181 0 : if (httpChannel) {
182 : // We can have an HTTP channel that has a non-HTTP URL if
183 : // we're doing FTP via an HTTP proxy, for example. See for
184 : // example bug 148813
185 0 : bool isReallyHTTP = false;
186 0 : aURI->SchemeIs("http", &isReallyHTTP);
187 0 : if (!isReallyHTTP)
188 0 : aURI->SchemeIs("https", &isReallyHTTP);
189 0 : if (isReallyHTTP) {
190 0 : httpChannel->SetRequestMethod(NS_LITERAL_CSTRING("HEAD"));
191 : // set back to true so we'll know that this request is issuing
192 : // a HEAD request. this is used down in OnStartRequest to
193 : // handle cases where we need to repeat the request as a normal
194 : // GET to deal with server borkage.
195 0 : mAllowHead = true;
196 : }
197 : }
198 : }
199 0 : return NS_OK;
200 : }
201 :
202 : NS_IMETHODIMP
203 0 : nsURIChecker::AsyncCheck(nsIRequestObserver *aObserver,
204 : nsISupports *aObserverContext)
205 : {
206 0 : NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
207 :
208 : // Hook us up to listen to redirects and the like (this creates a reference
209 : // cycle!)
210 0 : mChannel->SetNotificationCallbacks(this);
211 :
212 : // and start the request:
213 0 : nsresult rv = mChannel->AsyncOpen(this, nsnull);
214 0 : if (NS_FAILED(rv))
215 0 : mChannel = nsnull;
216 : else {
217 : // ok, wait for OnStartRequest to fire.
218 0 : mIsPending = true;
219 0 : mObserver = aObserver;
220 0 : mObserverContext = aObserverContext;
221 : }
222 0 : return rv;
223 : }
224 :
225 : NS_IMETHODIMP
226 0 : nsURIChecker::GetBaseChannel(nsIChannel **aChannel)
227 : {
228 0 : NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
229 0 : NS_ADDREF(*aChannel = mChannel);
230 0 : return NS_OK;
231 : }
232 :
233 : //-----------------------------------------------------------------------------
234 : // nsIRequest methods:
235 : //-----------------------------------------------------------------------------
236 :
237 : NS_IMETHODIMP
238 0 : nsURIChecker::GetName(nsACString &aName)
239 : {
240 0 : NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
241 0 : return mChannel->GetName(aName);
242 : }
243 :
244 : NS_IMETHODIMP
245 0 : nsURIChecker::IsPending(bool *aPendingRet)
246 : {
247 0 : *aPendingRet = mIsPending;
248 0 : return NS_OK;
249 : }
250 :
251 : NS_IMETHODIMP
252 0 : nsURIChecker::GetStatus(nsresult* aStatusRet)
253 : {
254 0 : *aStatusRet = mStatus;
255 0 : return NS_OK;
256 : }
257 :
258 : NS_IMETHODIMP
259 0 : nsURIChecker::Cancel(nsresult status)
260 : {
261 0 : NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
262 0 : return mChannel->Cancel(status);
263 : }
264 :
265 : NS_IMETHODIMP
266 0 : nsURIChecker::Suspend()
267 : {
268 0 : NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
269 0 : return mChannel->Suspend();
270 : }
271 :
272 : NS_IMETHODIMP
273 0 : nsURIChecker::Resume()
274 : {
275 0 : NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
276 0 : return mChannel->Resume();
277 : }
278 :
279 : NS_IMETHODIMP
280 0 : nsURIChecker::GetLoadGroup(nsILoadGroup **aLoadGroup)
281 : {
282 0 : NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
283 0 : return mChannel->GetLoadGroup(aLoadGroup);
284 : }
285 :
286 : NS_IMETHODIMP
287 0 : nsURIChecker::SetLoadGroup(nsILoadGroup *aLoadGroup)
288 : {
289 0 : NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
290 0 : return mChannel->SetLoadGroup(aLoadGroup);
291 : }
292 :
293 : NS_IMETHODIMP
294 0 : nsURIChecker::GetLoadFlags(nsLoadFlags *aLoadFlags)
295 : {
296 0 : NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
297 0 : return mChannel->GetLoadFlags(aLoadFlags);
298 : }
299 :
300 : NS_IMETHODIMP
301 0 : nsURIChecker::SetLoadFlags(nsLoadFlags aLoadFlags)
302 : {
303 0 : NS_ENSURE_TRUE(mChannel, NS_ERROR_NOT_INITIALIZED);
304 0 : return mChannel->SetLoadFlags(aLoadFlags);
305 : }
306 :
307 : //-----------------------------------------------------------------------------
308 : // nsIRequestObserver methods:
309 : //-----------------------------------------------------------------------------
310 :
311 : NS_IMETHODIMP
312 0 : nsURIChecker::OnStartRequest(nsIRequest *aRequest, nsISupports *aCtxt)
313 : {
314 0 : NS_ASSERTION(aRequest == mChannel, "unexpected request");
315 :
316 0 : nsresult rv = CheckStatus();
317 0 : if (rv != NS_BASE_STREAM_WOULD_BLOCK)
318 0 : SetStatusAndCallBack(rv);
319 :
320 : // cancel the request (we don't care to look at the data).
321 0 : return NS_BINDING_ABORTED;
322 : }
323 :
324 : NS_IMETHODIMP
325 0 : nsURIChecker::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
326 : nsresult statusCode)
327 : {
328 : // NOTE: we may have kicked off a subsequent request, so we should not do
329 : // any cleanup unless this request matches the one we are currently using.
330 0 : if (mChannel == request) {
331 : // break reference cycle between us and the channel (see comment in
332 : // AsyncCheckURI)
333 0 : mChannel = nsnull;
334 : }
335 0 : return NS_OK;
336 : }
337 :
338 : //-----------------------------------------------------------------------------
339 : // nsIStreamListener methods:
340 : //-----------------------------------------------------------------------------
341 :
342 : NS_IMETHODIMP
343 0 : nsURIChecker::OnDataAvailable(nsIRequest *aRequest, nsISupports *aCtxt,
344 : nsIInputStream *aInput, PRUint32 aOffset,
345 : PRUint32 aCount)
346 : {
347 0 : NS_NOTREACHED("nsURIChecker::OnDataAvailable");
348 0 : return NS_BINDING_ABORTED;
349 : }
350 :
351 : //-----------------------------------------------------------------------------
352 : // nsIInterfaceRequestor methods:
353 : //-----------------------------------------------------------------------------
354 :
355 : NS_IMETHODIMP
356 0 : nsURIChecker::GetInterface(const nsIID & aIID, void **aResult)
357 : {
358 0 : if (mObserver && aIID.Equals(NS_GET_IID(nsIAuthPrompt))) {
359 0 : nsCOMPtr<nsIInterfaceRequestor> req = do_QueryInterface(mObserver);
360 0 : if (req)
361 0 : return req->GetInterface(aIID, aResult);
362 : }
363 0 : return QueryInterface(aIID, aResult);
364 : }
365 :
366 : //-----------------------------------------------------------------------------
367 : // nsIChannelEventSink methods:
368 : //-----------------------------------------------------------------------------
369 :
370 : NS_IMETHODIMP
371 0 : nsURIChecker::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
372 : nsIChannel *aNewChannel,
373 : PRUint32 aFlags,
374 : nsIAsyncVerifyRedirectCallback *callback)
375 : {
376 : // We have a new channel
377 0 : mChannel = aNewChannel;
378 0 : callback->OnRedirectVerifyCallback(NS_OK);
379 0 : return NS_OK;
380 : }
|