1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : *
3 : * ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is mozilla.org Code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 1998
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Jeff Walden <jwalden+code@mit.edu>
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 "nsJAR.h"
41 : #include "nsJARChannel.h"
42 : #include "nsJARProtocolHandler.h"
43 : #include "nsMimeTypes.h"
44 : #include "nsNetUtil.h"
45 : #include "nsEscape.h"
46 : #include "nsIPrefService.h"
47 : #include "nsIPrefBranch.h"
48 : #include "nsIViewSourceChannel.h"
49 : #include "nsChannelProperties.h"
50 :
51 : #include "nsIScriptSecurityManager.h"
52 : #include "nsIPrincipal.h"
53 : #include "nsIFileURL.h"
54 :
55 : #include "mozilla/Preferences.h"
56 :
57 : using namespace mozilla;
58 :
59 : static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID);
60 :
61 : // the entry for a directory will either be empty (in the case of the
62 : // top-level directory) or will end with a slash
63 : #define ENTRY_IS_DIRECTORY(_entry) \
64 : ((_entry).IsEmpty() || '/' == (_entry).Last())
65 :
66 : //-----------------------------------------------------------------------------
67 :
68 : #if defined(PR_LOGGING)
69 : //
70 : // set NSPR_LOG_MODULES=nsJarProtocol:5
71 : //
72 : static PRLogModuleInfo *gJarProtocolLog = nsnull;
73 : #endif
74 :
75 : #define LOG(args) PR_LOG(gJarProtocolLog, PR_LOG_DEBUG, args)
76 : #define LOG_ENABLED() PR_LOG_TEST(gJarProtocolLog, 4)
77 :
78 : //-----------------------------------------------------------------------------
79 : // nsJARInputThunk
80 : //
81 : // this class allows us to do some extra work on the stream transport thread.
82 : //-----------------------------------------------------------------------------
83 :
84 : class nsJARInputThunk : public nsIInputStream
85 : {
86 : public:
87 : NS_DECL_ISUPPORTS
88 : NS_DECL_NSIINPUTSTREAM
89 :
90 193 : nsJARInputThunk(nsIZipReader *zipReader,
91 : nsIURI* fullJarURI,
92 : const nsACString &jarEntry,
93 : nsIZipReaderCache *jarCache)
94 : : mJarCache(jarCache)
95 : , mJarReader(zipReader)
96 : , mJarEntry(jarEntry)
97 193 : , mContentLength(-1)
98 : {
99 193 : if (fullJarURI) {
100 : #ifdef DEBUG
101 : nsresult rv =
102 : #endif
103 193 : fullJarURI->GetAsciiSpec(mJarDirSpec);
104 193 : NS_ASSERTION(NS_SUCCEEDED(rv), "this shouldn't fail");
105 : }
106 193 : }
107 :
108 386 : virtual ~nsJARInputThunk()
109 386 : {
110 193 : if (!mJarCache && mJarReader)
111 0 : mJarReader->Close();
112 772 : }
113 :
114 0 : void GetJarReader(nsIZipReader **result)
115 : {
116 0 : NS_IF_ADDREF(*result = mJarReader);
117 0 : }
118 :
119 57 : PRInt32 GetContentLength()
120 : {
121 57 : return mContentLength;
122 : }
123 :
124 : nsresult EnsureJarStream();
125 :
126 : private:
127 :
128 : nsCOMPtr<nsIZipReaderCache> mJarCache;
129 : nsCOMPtr<nsIZipReader> mJarReader;
130 : nsCString mJarDirSpec;
131 : nsCOMPtr<nsIInputStream> mJarStream;
132 : nsCString mJarEntry;
133 : PRInt32 mContentLength;
134 : };
135 :
136 1555 : NS_IMPL_THREADSAFE_ISUPPORTS1(nsJARInputThunk, nsIInputStream)
137 :
138 : nsresult
139 357 : nsJARInputThunk::EnsureJarStream()
140 : {
141 357 : if (mJarStream)
142 164 : return NS_OK;
143 :
144 : nsresult rv;
145 193 : if (ENTRY_IS_DIRECTORY(mJarEntry)) {
146 : // A directory stream also needs the Spec of the FullJarURI
147 : // because is included in the stream data itself.
148 :
149 0 : NS_ENSURE_STATE(!mJarDirSpec.IsEmpty());
150 :
151 0 : rv = mJarReader->GetInputStreamWithSpec(mJarDirSpec,
152 : mJarEntry,
153 0 : getter_AddRefs(mJarStream));
154 : }
155 : else {
156 193 : rv = mJarReader->GetInputStream(mJarEntry,
157 193 : getter_AddRefs(mJarStream));
158 : }
159 193 : if (NS_FAILED(rv)) {
160 : // convert to the proper result if the entry wasn't found
161 : // so that error pages work
162 41 : if (rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST)
163 41 : rv = NS_ERROR_FILE_NOT_FOUND;
164 41 : return rv;
165 : }
166 :
167 : // ask the JarStream for the content length
168 152 : rv = mJarStream->Available((PRUint32 *) &mContentLength);
169 152 : if (NS_FAILED(rv)) return rv;
170 :
171 152 : return NS_OK;
172 : }
173 :
174 : NS_IMETHODIMP
175 0 : nsJARInputThunk::Close()
176 : {
177 0 : if (mJarStream)
178 0 : return mJarStream->Close();
179 :
180 0 : return NS_OK;
181 : }
182 :
183 : NS_IMETHODIMP
184 54 : nsJARInputThunk::Available(PRUint32 *avail)
185 : {
186 54 : nsresult rv = EnsureJarStream();
187 54 : if (NS_FAILED(rv)) return rv;
188 :
189 54 : return mJarStream->Available(avail);
190 : }
191 :
192 : NS_IMETHODIMP
193 110 : nsJARInputThunk::Read(char *buf, PRUint32 count, PRUint32 *countRead)
194 : {
195 110 : nsresult rv = EnsureJarStream();
196 110 : if (NS_FAILED(rv)) return rv;
197 :
198 110 : return mJarStream->Read(buf, count, countRead);
199 : }
200 :
201 : NS_IMETHODIMP
202 1 : nsJARInputThunk::ReadSegments(nsWriteSegmentFun writer, void *closure,
203 : PRUint32 count, PRUint32 *countRead)
204 : {
205 : // stream transport does only calls Read()
206 1 : return NS_ERROR_NOT_IMPLEMENTED;
207 : }
208 :
209 : NS_IMETHODIMP
210 0 : nsJARInputThunk::IsNonBlocking(bool *nonBlocking)
211 : {
212 0 : *nonBlocking = false;
213 0 : return NS_OK;
214 : }
215 :
216 : //-----------------------------------------------------------------------------
217 : // nsJARChannel
218 : //-----------------------------------------------------------------------------
219 :
220 :
221 399 : nsJARChannel::nsJARChannel()
222 : : mContentLength(-1)
223 : , mLoadFlags(LOAD_NORMAL)
224 : , mStatus(NS_OK)
225 : , mIsPending(false)
226 : , mIsUnsafe(true)
227 399 : , mJarInput(nsnull)
228 : {
229 : #if defined(PR_LOGGING)
230 399 : if (!gJarProtocolLog)
231 25 : gJarProtocolLog = PR_NewLogModule("nsJarProtocol");
232 : #endif
233 :
234 : // hold an owning reference to the jar handler
235 399 : NS_ADDREF(gJarHandler);
236 399 : }
237 :
238 1197 : nsJARChannel::~nsJARChannel()
239 : {
240 : // with the exception of certain error cases mJarInput will already be null.
241 399 : NS_IF_RELEASE(mJarInput);
242 :
243 : // release owning reference to the jar handler
244 399 : nsJARProtocolHandler *handler = gJarHandler;
245 399 : NS_RELEASE(handler); // NULL parameter
246 1596 : }
247 :
248 2762 : NS_IMPL_ISUPPORTS_INHERITED6(nsJARChannel,
249 : nsHashPropertyBag,
250 : nsIRequest,
251 : nsIChannel,
252 : nsIStreamListener,
253 : nsIRequestObserver,
254 : nsIDownloadObserver,
255 : nsIJARChannel)
256 :
257 : nsresult
258 399 : nsJARChannel::Init(nsIURI *uri)
259 : {
260 : nsresult rv;
261 399 : rv = nsHashPropertyBag::Init();
262 399 : if (NS_FAILED(rv))
263 0 : return rv;
264 :
265 399 : mJarURI = do_QueryInterface(uri, &rv);
266 399 : if (NS_FAILED(rv))
267 0 : return rv;
268 :
269 399 : mOriginalURI = mJarURI;
270 :
271 : // Prevent loading jar:javascript URIs (see bug 290982).
272 798 : nsCOMPtr<nsIURI> innerURI;
273 399 : rv = mJarURI->GetJARFile(getter_AddRefs(innerURI));
274 399 : if (NS_FAILED(rv))
275 0 : return rv;
276 : bool isJS;
277 399 : rv = innerURI->SchemeIs("javascript", &isJS);
278 399 : if (NS_FAILED(rv))
279 0 : return rv;
280 399 : if (isJS) {
281 0 : NS_WARNING("blocking jar:javascript:");
282 0 : return NS_ERROR_INVALID_ARG;
283 : }
284 :
285 : #if defined(PR_LOGGING)
286 399 : mJarURI->GetSpec(mSpec);
287 : #endif
288 399 : return rv;
289 : }
290 :
291 : nsresult
292 195 : nsJARChannel::CreateJarInput(nsIZipReaderCache *jarCache)
293 : {
294 : // important to pass a clone of the file since the nsIFile impl is not
295 : // necessarily MT-safe
296 390 : nsCOMPtr<nsIFile> clonedFile;
297 195 : nsresult rv = mJarFile->Clone(getter_AddRefs(clonedFile));
298 195 : if (NS_FAILED(rv))
299 0 : return rv;
300 :
301 390 : nsCOMPtr<nsIZipReader> reader;
302 195 : if (jarCache) {
303 195 : if (mInnerJarEntry.IsEmpty())
304 151 : rv = jarCache->GetZip(mJarFile, getter_AddRefs(reader));
305 : else
306 : rv = jarCache->GetInnerZip(mJarFile, mInnerJarEntry,
307 44 : getter_AddRefs(reader));
308 : } else {
309 : // create an uncached jar reader
310 0 : nsCOMPtr<nsIZipReader> outerReader = do_CreateInstance(kZipReaderCID, &rv);
311 0 : if (NS_FAILED(rv))
312 0 : return rv;
313 :
314 0 : rv = outerReader->Open(mJarFile);
315 0 : if (NS_FAILED(rv))
316 0 : return rv;
317 :
318 0 : if (mInnerJarEntry.IsEmpty())
319 0 : reader = outerReader;
320 : else {
321 0 : reader = do_CreateInstance(kZipReaderCID, &rv);
322 0 : if (NS_FAILED(rv))
323 0 : return rv;
324 :
325 0 : rv = reader->OpenInner(outerReader, mInnerJarEntry);
326 : }
327 : }
328 195 : if (NS_FAILED(rv))
329 2 : return rv;
330 :
331 386 : mJarInput = new nsJARInputThunk(reader, mJarURI, mJarEntry, jarCache);
332 193 : if (!mJarInput)
333 0 : return NS_ERROR_OUT_OF_MEMORY;
334 193 : NS_ADDREF(mJarInput);
335 193 : return NS_OK;
336 : }
337 :
338 : nsresult
339 196 : nsJARChannel::EnsureJarInput(bool blocking)
340 : {
341 196 : LOG(("nsJARChannel::EnsureJarInput [this=%x %s]\n", this, mSpec.get()));
342 :
343 : nsresult rv;
344 392 : nsCOMPtr<nsIURI> uri;
345 :
346 196 : rv = mJarURI->GetJARFile(getter_AddRefs(mJarBaseURI));
347 196 : if (NS_FAILED(rv)) return rv;
348 :
349 196 : rv = mJarURI->GetJAREntry(mJarEntry);
350 196 : if (NS_FAILED(rv)) return rv;
351 :
352 : // The name of the JAR entry must not contain URL-escaped characters:
353 : // we're moving from URL domain to a filename domain here. nsStandardURL
354 : // does basic escaping by default, which breaks reading zipped files which
355 : // have e.g. spaces in their filenames.
356 196 : NS_UnescapeURL(mJarEntry);
357 :
358 : // try to get a nsIFile directly from the url, which will often succeed.
359 : {
360 392 : nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mJarBaseURI);
361 196 : if (fileURL)
362 151 : fileURL->GetFile(getter_AddRefs(mJarFile));
363 : }
364 : // try to handle a nested jar
365 196 : if (!mJarFile) {
366 90 : nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(mJarBaseURI);
367 45 : if (jarURI) {
368 88 : nsCOMPtr<nsIFileURL> fileURL;
369 88 : nsCOMPtr<nsIURI> innerJarURI;
370 44 : rv = jarURI->GetJARFile(getter_AddRefs(innerJarURI));
371 44 : if (NS_SUCCEEDED(rv))
372 44 : fileURL = do_QueryInterface(innerJarURI);
373 44 : if (fileURL) {
374 44 : fileURL->GetFile(getter_AddRefs(mJarFile));
375 44 : jarURI->GetJAREntry(mInnerJarEntry);
376 : }
377 : }
378 : }
379 :
380 196 : if (mJarFile) {
381 195 : mIsUnsafe = false;
382 :
383 : // NOTE: we do not need to deal with mSecurityInfo here,
384 : // because we're loading from a local file
385 195 : rv = CreateJarInput(gJarHandler->JarCache());
386 : }
387 1 : else if (blocking) {
388 0 : NS_NOTREACHED("need sync downloader");
389 0 : rv = NS_ERROR_NOT_IMPLEMENTED;
390 : }
391 : else {
392 : // kick off an async download of the base URI...
393 1 : rv = NS_NewDownloader(getter_AddRefs(mDownloader), this);
394 1 : if (NS_SUCCEEDED(rv))
395 : rv = NS_OpenURI(mDownloader, nsnull, mJarBaseURI, nsnull,
396 : mLoadGroup, mCallbacks,
397 1 : mLoadFlags & ~(LOAD_DOCUMENT_URI | LOAD_CALL_CONTENT_SNIFFERS));
398 : }
399 196 : return rv;
400 :
401 : }
402 :
403 : //-----------------------------------------------------------------------------
404 : // nsIRequest
405 : //-----------------------------------------------------------------------------
406 :
407 : NS_IMETHODIMP
408 0 : nsJARChannel::GetName(nsACString &result)
409 : {
410 0 : return mJarURI->GetSpec(result);
411 : }
412 :
413 : NS_IMETHODIMP
414 0 : nsJARChannel::IsPending(bool *result)
415 : {
416 0 : *result = mIsPending;
417 0 : return NS_OK;
418 : }
419 :
420 : NS_IMETHODIMP
421 0 : nsJARChannel::GetStatus(nsresult *status)
422 : {
423 0 : if (mPump && NS_SUCCEEDED(mStatus))
424 0 : mPump->GetStatus(status);
425 : else
426 0 : *status = mStatus;
427 0 : return NS_OK;
428 : }
429 :
430 : NS_IMETHODIMP
431 0 : nsJARChannel::Cancel(nsresult status)
432 : {
433 0 : mStatus = status;
434 0 : if (mPump)
435 0 : return mPump->Cancel(status);
436 :
437 0 : NS_ASSERTION(!mIsPending, "need to implement cancel when downloading");
438 0 : return NS_OK;
439 : }
440 :
441 : NS_IMETHODIMP
442 0 : nsJARChannel::Suspend()
443 : {
444 0 : if (mPump)
445 0 : return mPump->Suspend();
446 :
447 0 : NS_ASSERTION(!mIsPending, "need to implement suspend when downloading");
448 0 : return NS_OK;
449 : }
450 :
451 : NS_IMETHODIMP
452 0 : nsJARChannel::Resume()
453 : {
454 0 : if (mPump)
455 0 : return mPump->Resume();
456 :
457 0 : NS_ASSERTION(!mIsPending, "need to implement resume when downloading");
458 0 : return NS_OK;
459 : }
460 :
461 : NS_IMETHODIMP
462 3 : nsJARChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
463 : {
464 3 : *aLoadFlags = mLoadFlags;
465 3 : return NS_OK;
466 : }
467 :
468 : NS_IMETHODIMP
469 3 : nsJARChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
470 : {
471 3 : mLoadFlags = aLoadFlags;
472 3 : return NS_OK;
473 : }
474 :
475 : NS_IMETHODIMP
476 0 : nsJARChannel::GetLoadGroup(nsILoadGroup **aLoadGroup)
477 : {
478 0 : NS_IF_ADDREF(*aLoadGroup = mLoadGroup);
479 0 : return NS_OK;
480 : }
481 :
482 : NS_IMETHODIMP
483 0 : nsJARChannel::SetLoadGroup(nsILoadGroup *aLoadGroup)
484 : {
485 0 : mLoadGroup = aLoadGroup;
486 0 : return NS_OK;
487 : }
488 :
489 : //-----------------------------------------------------------------------------
490 : // nsIChannel
491 : //-----------------------------------------------------------------------------
492 :
493 : NS_IMETHODIMP
494 0 : nsJARChannel::GetOriginalURI(nsIURI **aURI)
495 : {
496 0 : *aURI = mOriginalURI;
497 0 : NS_ADDREF(*aURI);
498 0 : return NS_OK;
499 : }
500 :
501 : NS_IMETHODIMP
502 3 : nsJARChannel::SetOriginalURI(nsIURI *aURI)
503 : {
504 3 : NS_ENSURE_ARG_POINTER(aURI);
505 3 : mOriginalURI = aURI;
506 3 : return NS_OK;
507 : }
508 :
509 : NS_IMETHODIMP
510 202 : nsJARChannel::GetURI(nsIURI **aURI)
511 : {
512 202 : NS_IF_ADDREF(*aURI = mJarURI);
513 202 : return NS_OK;
514 : }
515 :
516 : NS_IMETHODIMP
517 0 : nsJARChannel::GetOwner(nsISupports **result)
518 : {
519 : nsresult rv;
520 :
521 0 : if (mOwner) {
522 0 : NS_ADDREF(*result = mOwner);
523 0 : return NS_OK;
524 : }
525 :
526 0 : if (!mJarInput) {
527 0 : *result = nsnull;
528 0 : return NS_OK;
529 : }
530 :
531 : //-- Verify signature, if one is present, and set owner accordingly
532 0 : nsCOMPtr<nsIZipReader> jarReader;
533 0 : mJarInput->GetJarReader(getter_AddRefs(jarReader));
534 0 : if (!jarReader)
535 0 : return NS_ERROR_NOT_INITIALIZED;
536 :
537 0 : nsCOMPtr<nsIPrincipal> cert;
538 0 : rv = jarReader->GetCertificatePrincipal(mJarEntry, getter_AddRefs(cert));
539 0 : if (NS_FAILED(rv)) return rv;
540 :
541 0 : if (cert) {
542 0 : nsCAutoString certFingerprint;
543 0 : rv = cert->GetFingerprint(certFingerprint);
544 0 : if (NS_FAILED(rv)) return rv;
545 :
546 0 : nsCAutoString subjectName;
547 0 : rv = cert->GetSubjectName(subjectName);
548 0 : if (NS_FAILED(rv)) return rv;
549 :
550 0 : nsCAutoString prettyName;
551 0 : rv = cert->GetPrettyName(prettyName);
552 0 : if (NS_FAILED(rv)) return rv;
553 :
554 0 : nsCOMPtr<nsISupports> certificate;
555 0 : rv = cert->GetCertificate(getter_AddRefs(certificate));
556 0 : if (NS_FAILED(rv)) return rv;
557 :
558 : nsCOMPtr<nsIScriptSecurityManager> secMan =
559 0 : do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
560 0 : if (NS_FAILED(rv)) return rv;
561 :
562 0 : rv = secMan->GetCertificatePrincipal(certFingerprint, subjectName,
563 : prettyName, certificate,
564 : mJarBaseURI,
565 0 : getter_AddRefs(cert));
566 0 : if (NS_FAILED(rv)) return rv;
567 :
568 0 : mOwner = do_QueryInterface(cert, &rv);
569 0 : if (NS_FAILED(rv)) return rv;
570 :
571 0 : NS_ADDREF(*result = mOwner);
572 : }
573 0 : return NS_OK;
574 : }
575 :
576 : NS_IMETHODIMP
577 1 : nsJARChannel::SetOwner(nsISupports *aOwner)
578 : {
579 1 : mOwner = aOwner;
580 1 : return NS_OK;
581 : }
582 :
583 : NS_IMETHODIMP
584 0 : nsJARChannel::GetNotificationCallbacks(nsIInterfaceRequestor **aCallbacks)
585 : {
586 0 : NS_IF_ADDREF(*aCallbacks = mCallbacks);
587 0 : return NS_OK;
588 : }
589 :
590 : NS_IMETHODIMP
591 0 : nsJARChannel::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks)
592 : {
593 0 : mCallbacks = aCallbacks;
594 0 : return NS_OK;
595 : }
596 :
597 : NS_IMETHODIMP
598 0 : nsJARChannel::GetSecurityInfo(nsISupports **aSecurityInfo)
599 : {
600 0 : NS_PRECONDITION(aSecurityInfo, "Null out param");
601 0 : NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo);
602 0 : return NS_OK;
603 : }
604 :
605 : NS_IMETHODIMP
606 0 : nsJARChannel::GetContentType(nsACString &result)
607 : {
608 0 : if (mContentType.IsEmpty()) {
609 : //
610 : // generate content type and set it
611 : //
612 0 : const char *ext = nsnull, *fileName = mJarEntry.get();
613 0 : PRInt32 len = mJarEntry.Length();
614 :
615 : // check if we're displaying a directory
616 : // mJarEntry will be empty if we're trying to display
617 : // the topmost directory in a zip, e.g. jar:foo.zip!/
618 0 : if (ENTRY_IS_DIRECTORY(mJarEntry)) {
619 0 : mContentType.AssignLiteral(APPLICATION_HTTP_INDEX_FORMAT);
620 : }
621 : else {
622 : // not a directory, take a guess by its extension
623 0 : for (PRInt32 i = len-1; i >= 0; i--) {
624 0 : if (fileName[i] == '.') {
625 0 : ext = &fileName[i + 1];
626 0 : break;
627 : }
628 : }
629 0 : if (ext) {
630 0 : nsIMIMEService *mimeServ = gJarHandler->MimeService();
631 0 : if (mimeServ)
632 0 : mimeServ->GetTypeFromExtension(nsDependentCString(ext), mContentType);
633 : }
634 0 : if (mContentType.IsEmpty())
635 0 : mContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE);
636 : }
637 : }
638 0 : result = mContentType;
639 0 : return NS_OK;
640 : }
641 :
642 : NS_IMETHODIMP
643 97 : nsJARChannel::SetContentType(const nsACString &aContentType)
644 : {
645 : // If someone gives us a type hint we should just use that type instead of
646 : // doing our guessing. So we don't care when this is being called.
647 :
648 : // mContentCharset is unchanged if not parsed
649 97 : NS_ParseContentType(aContentType, mContentType, mContentCharset);
650 97 : return NS_OK;
651 : }
652 :
653 : NS_IMETHODIMP
654 0 : nsJARChannel::GetContentCharset(nsACString &aContentCharset)
655 : {
656 : // If someone gives us a charset hint we should just use that charset.
657 : // So we don't care when this is being called.
658 0 : aContentCharset = mContentCharset;
659 0 : return NS_OK;
660 : }
661 :
662 : NS_IMETHODIMP
663 0 : nsJARChannel::SetContentCharset(const nsACString &aContentCharset)
664 : {
665 0 : mContentCharset = aContentCharset;
666 0 : return NS_OK;
667 : }
668 :
669 : NS_IMETHODIMP
670 1 : nsJARChannel::GetContentDisposition(PRUint32 *aContentDisposition)
671 : {
672 1 : if (mContentDispositionHeader.IsEmpty())
673 1 : return NS_ERROR_NOT_AVAILABLE;
674 :
675 0 : *aContentDisposition = mContentDisposition;
676 0 : return NS_OK;
677 : }
678 :
679 : NS_IMETHODIMP
680 0 : nsJARChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename)
681 : {
682 0 : return NS_ERROR_NOT_AVAILABLE;
683 : }
684 :
685 : NS_IMETHODIMP
686 0 : nsJARChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader)
687 : {
688 0 : if (mContentDispositionHeader.IsEmpty())
689 0 : return NS_ERROR_NOT_AVAILABLE;
690 :
691 0 : aContentDispositionHeader = mContentDispositionHeader;
692 0 : return NS_OK;
693 : }
694 :
695 : NS_IMETHODIMP
696 57 : nsJARChannel::GetContentLength(PRInt32 *result)
697 : {
698 : // if content length is unknown, query mJarInput...
699 57 : if (mContentLength < 0 && mJarInput)
700 57 : mContentLength = mJarInput->GetContentLength();
701 :
702 57 : *result = mContentLength;
703 57 : return NS_OK;
704 : }
705 :
706 : NS_IMETHODIMP
707 0 : nsJARChannel::SetContentLength(PRInt32 aContentLength)
708 : {
709 : // XXX does this really make any sense at all?
710 0 : mContentLength = aContentLength;
711 0 : return NS_OK;
712 : }
713 :
714 : NS_IMETHODIMP
715 195 : nsJARChannel::Open(nsIInputStream **stream)
716 : {
717 195 : LOG(("nsJARChannel::Open [this=%x]\n", this));
718 :
719 195 : NS_ENSURE_TRUE(!mJarInput, NS_ERROR_IN_PROGRESS);
720 195 : NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
721 :
722 195 : mJarFile = nsnull;
723 195 : mIsUnsafe = true;
724 :
725 195 : nsresult rv = EnsureJarInput(true);
726 195 : if (NS_FAILED(rv)) return rv;
727 :
728 193 : if (!mJarInput)
729 0 : return NS_ERROR_UNEXPECTED;
730 :
731 : // force load the jar file now so GetContentLength will return a
732 : // meaningful value once we return.
733 193 : rv = mJarInput->EnsureJarStream();
734 193 : if (NS_FAILED(rv)) return rv;
735 :
736 152 : NS_ADDREF(*stream = mJarInput);
737 152 : return NS_OK;
738 : }
739 :
740 : NS_IMETHODIMP
741 2 : nsJARChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctx)
742 : {
743 2 : LOG(("nsJARChannel::AsyncOpen [this=%x]\n", this));
744 :
745 2 : NS_ENSURE_ARG_POINTER(listener);
746 1 : NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
747 :
748 1 : mJarFile = nsnull;
749 1 : mIsUnsafe = true;
750 :
751 : // Initialize mProgressSink
752 1 : NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, mProgressSink);
753 :
754 1 : nsresult rv = EnsureJarInput(false);
755 1 : if (NS_FAILED(rv)) return rv;
756 :
757 : // These variables must only be set if we're going to trigger an
758 : // OnStartRequest, either from AsyncRead or OnDownloadComplete.
759 1 : mListener = listener;
760 1 : mListenerContext = ctx;
761 1 : mIsPending = true;
762 1 : if (mJarInput) {
763 : // create input stream pump and call AsyncRead as a block
764 0 : rv = NS_NewInputStreamPump(getter_AddRefs(mPump), mJarInput);
765 0 : if (NS_SUCCEEDED(rv))
766 0 : rv = mPump->AsyncRead(this, nsnull);
767 :
768 : // If we failed to create the pump or initiate the AsyncRead,
769 : // then we need to clear these variables.
770 0 : if (NS_FAILED(rv)) {
771 0 : mIsPending = false;
772 0 : mListenerContext = nsnull;
773 0 : mListener = nsnull;
774 0 : return rv;
775 : }
776 : }
777 :
778 1 : if (mLoadGroup)
779 0 : mLoadGroup->AddRequest(this, nsnull);
780 :
781 1 : return NS_OK;
782 : }
783 :
784 : //-----------------------------------------------------------------------------
785 : // nsIJARChannel
786 : //-----------------------------------------------------------------------------
787 : NS_IMETHODIMP
788 0 : nsJARChannel::GetIsUnsafe(bool *isUnsafe)
789 : {
790 0 : *isUnsafe = mIsUnsafe;
791 0 : return NS_OK;
792 : }
793 :
794 : //-----------------------------------------------------------------------------
795 : // nsIDownloadObserver
796 : //-----------------------------------------------------------------------------
797 :
798 : NS_IMETHODIMP
799 1 : nsJARChannel::OnDownloadComplete(nsIDownloader *downloader,
800 : nsIRequest *request,
801 : nsISupports *context,
802 : nsresult status,
803 : nsIFile *file)
804 : {
805 : nsresult rv;
806 :
807 2 : nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
808 1 : if (channel) {
809 : PRUint32 loadFlags;
810 1 : channel->GetLoadFlags(&loadFlags);
811 1 : if (loadFlags & LOAD_REPLACE) {
812 0 : mLoadFlags |= LOAD_REPLACE;
813 :
814 0 : if (!mOriginalURI) {
815 0 : SetOriginalURI(mJarURI);
816 : }
817 :
818 0 : nsCOMPtr<nsIURI> innerURI;
819 0 : rv = channel->GetURI(getter_AddRefs(innerURI));
820 0 : if (NS_SUCCEEDED(rv)) {
821 0 : nsCOMPtr<nsIJARURI> newURI;
822 0 : rv = mJarURI->CloneWithJARFile(innerURI,
823 0 : getter_AddRefs(newURI));
824 0 : if (NS_SUCCEEDED(rv)) {
825 0 : mJarURI = newURI;
826 : }
827 : }
828 0 : if (NS_SUCCEEDED(status)) {
829 0 : status = rv;
830 : }
831 : }
832 : }
833 :
834 1 : if (NS_SUCCEEDED(status) && channel) {
835 : // Grab the security info from our base channel
836 0 : channel->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
837 :
838 0 : nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
839 0 : if (httpChannel) {
840 : // We only want to run scripts if the server really intended to
841 : // send us a JAR file. Check the server-supplied content type for
842 : // a JAR type.
843 0 : nsCAutoString header;
844 0 : httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Content-Type"),
845 0 : header);
846 0 : nsCAutoString contentType;
847 0 : nsCAutoString charset;
848 0 : NS_ParseContentType(header, contentType, charset);
849 0 : nsCAutoString channelContentType;
850 0 : channel->GetContentType(channelContentType);
851 0 : mIsUnsafe = !(contentType.Equals(channelContentType) &&
852 0 : (contentType.EqualsLiteral("application/java-archive") ||
853 0 : contentType.EqualsLiteral("application/x-jar")));
854 : } else {
855 0 : nsCOMPtr<nsIJARChannel> innerJARChannel(do_QueryInterface(channel));
856 0 : if (innerJARChannel) {
857 : bool unsafe;
858 0 : innerJARChannel->GetIsUnsafe(&unsafe);
859 0 : mIsUnsafe = unsafe;
860 : }
861 : }
862 :
863 0 : channel->GetContentDispositionHeader(mContentDispositionHeader);
864 0 : mContentDisposition = NS_GetContentDispositionFromHeader(mContentDispositionHeader, this);
865 : }
866 :
867 1 : if (NS_SUCCEEDED(status) && mIsUnsafe &&
868 0 : !Preferences::GetBool("network.jar.open-unsafe-types", false)) {
869 0 : status = NS_ERROR_UNSAFE_CONTENT_TYPE;
870 : }
871 :
872 1 : if (NS_SUCCEEDED(status)) {
873 : // Refuse to unpack view-source: jars even if open-unsafe-types is set.
874 0 : nsCOMPtr<nsIViewSourceChannel> viewSource = do_QueryInterface(channel);
875 0 : if (viewSource) {
876 0 : status = NS_ERROR_UNSAFE_CONTENT_TYPE;
877 : }
878 : }
879 :
880 1 : if (NS_SUCCEEDED(status)) {
881 0 : mJarFile = file;
882 :
883 0 : rv = CreateJarInput(nsnull);
884 0 : if (NS_SUCCEEDED(rv)) {
885 : // create input stream pump
886 0 : rv = NS_NewInputStreamPump(getter_AddRefs(mPump), mJarInput);
887 0 : if (NS_SUCCEEDED(rv))
888 0 : rv = mPump->AsyncRead(this, nsnull);
889 : }
890 0 : status = rv;
891 : }
892 :
893 1 : if (NS_FAILED(status)) {
894 1 : mStatus = status;
895 1 : OnStartRequest(nsnull, nsnull);
896 1 : OnStopRequest(nsnull, nsnull, status);
897 : }
898 :
899 1 : return NS_OK;
900 : }
901 :
902 : //-----------------------------------------------------------------------------
903 : // nsIStreamListener
904 : //-----------------------------------------------------------------------------
905 :
906 : NS_IMETHODIMP
907 1 : nsJARChannel::OnStartRequest(nsIRequest *req, nsISupports *ctx)
908 : {
909 1 : LOG(("nsJARChannel::OnStartRequest [this=%x %s]\n", this, mSpec.get()));
910 :
911 1 : return mListener->OnStartRequest(this, mListenerContext);
912 : }
913 :
914 : NS_IMETHODIMP
915 1 : nsJARChannel::OnStopRequest(nsIRequest *req, nsISupports *ctx, nsresult status)
916 : {
917 1 : LOG(("nsJARChannel::OnStopRequest [this=%x %s status=%x]\n",
918 : this, mSpec.get(), status));
919 :
920 1 : if (NS_SUCCEEDED(mStatus))
921 0 : mStatus = status;
922 :
923 1 : if (mListener) {
924 1 : mListener->OnStopRequest(this, mListenerContext, status);
925 1 : mListener = 0;
926 1 : mListenerContext = 0;
927 : }
928 :
929 1 : if (mLoadGroup)
930 0 : mLoadGroup->RemoveRequest(this, nsnull, status);
931 :
932 1 : mPump = 0;
933 1 : NS_IF_RELEASE(mJarInput);
934 1 : mIsPending = false;
935 1 : mDownloader = 0; // this may delete the underlying jar file
936 :
937 : // Drop notification callbacks to prevent cycles.
938 1 : mCallbacks = 0;
939 1 : mProgressSink = 0;
940 :
941 1 : return NS_OK;
942 : }
943 :
944 : NS_IMETHODIMP
945 0 : nsJARChannel::OnDataAvailable(nsIRequest *req, nsISupports *ctx,
946 : nsIInputStream *stream,
947 : PRUint32 offset, PRUint32 count)
948 : {
949 : #if defined(PR_LOGGING)
950 0 : LOG(("nsJARChannel::OnDataAvailable [this=%x %s]\n", this, mSpec.get()));
951 : #endif
952 :
953 : nsresult rv;
954 :
955 0 : rv = mListener->OnDataAvailable(this, mListenerContext, stream, offset, count);
956 :
957 : // simply report progress here instead of hooking ourselves up as a
958 : // nsITransportEventSink implementation.
959 : // XXX do the 64-bit stuff for real
960 0 : if (mProgressSink && NS_SUCCEEDED(rv) && !(mLoadFlags & LOAD_BACKGROUND))
961 0 : mProgressSink->OnProgress(this, nsnull, PRUint64(offset + count),
962 0 : PRUint64(mContentLength));
963 :
964 0 : return rv; // let the pump cancel on failure
965 : }
|