b=450088 backing out (new reftest failed)
[wine-gecko.git] / modules / libjar / nsJARChannel.cpp
blob205872707165d07f56378c1ecc7f7b353949f845
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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.
16 * The Original Code is mozilla.org Code.
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.
23 * Contributor(s):
24 * Jeff Walden <jwalden+code@mit.edu>
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.
38 * ***** END LICENSE BLOCK ***** */
40 #include "nsJAR.h"
41 #include "nsJARChannel.h"
42 #include "nsJARProtocolHandler.h"
43 #include "nsMimeTypes.h"
44 #include "nsNetUtil.h"
45 #include "nsInt64.h"
46 #include "nsEscape.h"
47 #include "nsIPrefService.h"
48 #include "nsIPrefBranch.h"
50 #include "nsIScriptSecurityManager.h"
51 #include "nsIPrincipal.h"
52 #include "nsIFileURL.h"
53 #include "nsIJAR.h"
55 static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID);
57 // the entry for a directory will either be empty (in the case of the
58 // top-level directory) or will end with a slash
59 #define ENTRY_IS_DIRECTORY(_entry) \
60 ((_entry).IsEmpty() || '/' == (_entry).Last())
62 //-----------------------------------------------------------------------------
64 #if defined(PR_LOGGING)
66 // set NSPR_LOG_MODULES=nsJarProtocol:5
68 static PRLogModuleInfo *gJarProtocolLog = nsnull;
69 #endif
71 #define LOG(args) PR_LOG(gJarProtocolLog, PR_LOG_DEBUG, args)
72 #define LOG_ENABLED() PR_LOG_TEST(gJarProtocolLog, 4)
74 //-----------------------------------------------------------------------------
75 // nsJARInputThunk
77 // this class allows us to do some extra work on the stream transport thread.
78 //-----------------------------------------------------------------------------
80 class nsJARInputThunk : public nsIInputStream
82 public:
83 NS_DECL_ISUPPORTS
84 NS_DECL_NSIINPUTSTREAM
86 nsJARInputThunk(nsIFile *jarFile,
87 nsIURI* fullJarURI,
88 const nsACString &jarEntry,
89 nsIZipReaderCache *jarCache)
90 : mJarCache(jarCache)
91 , mJarFile(jarFile)
92 , mFullJarURI(fullJarURI)
93 , mJarEntry(jarEntry)
94 , mContentLength(-1)
96 NS_ASSERTION(mJarFile, "no jar file");
99 virtual ~nsJARInputThunk()
101 if (!mJarCache && mJarReader)
102 mJarReader->Close();
105 void GetJarReader(nsIZipReader **result)
107 NS_IF_ADDREF(*result = mJarReader);
110 PRInt32 GetContentLength()
112 return mContentLength;
115 nsresult EnsureJarStream();
117 private:
119 nsCOMPtr<nsIZipReaderCache> mJarCache;
120 nsCOMPtr<nsIZipReader> mJarReader;
121 nsCOMPtr<nsIFile> mJarFile;
122 nsCOMPtr<nsIURI> mFullJarURI;
123 nsCOMPtr<nsIInputStream> mJarStream;
124 nsCString mJarEntry;
125 PRInt32 mContentLength;
128 NS_IMPL_THREADSAFE_ISUPPORTS1(nsJARInputThunk, nsIInputStream)
130 nsresult
131 nsJARInputThunk::EnsureJarStream()
133 if (mJarStream)
134 return NS_OK;
136 nsresult rv;
137 if (mJarCache)
138 rv = mJarCache->GetZip(mJarFile, getter_AddRefs(mJarReader));
139 else {
140 // create an uncached jar reader
141 mJarReader = do_CreateInstance(kZipReaderCID, &rv);
142 if (NS_FAILED(rv)) return rv;
144 rv = mJarReader->Open(mJarFile);
146 if (NS_FAILED(rv)) return rv;
148 if (ENTRY_IS_DIRECTORY(mJarEntry)) {
149 // A directory stream also needs the Spec of the FullJarURI
150 // because is included in the stream data itself.
152 nsCAutoString jarDirSpec;
153 rv = mFullJarURI->GetAsciiSpec(jarDirSpec);
154 if (NS_FAILED(rv)) return rv;
156 rv = mJarReader->GetInputStreamWithSpec(jarDirSpec,
157 mJarEntry.get(),
158 getter_AddRefs(mJarStream));
160 else {
161 rv = mJarReader->GetInputStream(mJarEntry.get(),
162 getter_AddRefs(mJarStream));
164 if (NS_FAILED(rv)) {
165 // convert to the proper result if the entry wasn't found
166 // so that error pages work
167 if (rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST)
168 rv = NS_ERROR_FILE_NOT_FOUND;
169 return rv;
172 // ask the JarStream for the content length
173 mJarStream->Available((PRUint32 *) &mContentLength);
175 return NS_OK;
178 NS_IMETHODIMP
179 nsJARInputThunk::Close()
181 if (mJarStream)
182 return mJarStream->Close();
184 return NS_OK;
187 NS_IMETHODIMP
188 nsJARInputThunk::Available(PRUint32 *avail)
190 nsresult rv = EnsureJarStream();
191 if (NS_FAILED(rv)) return rv;
193 return mJarStream->Available(avail);
196 NS_IMETHODIMP
197 nsJARInputThunk::Read(char *buf, PRUint32 count, PRUint32 *countRead)
199 nsresult rv = EnsureJarStream();
200 if (NS_FAILED(rv)) return rv;
202 return mJarStream->Read(buf, count, countRead);
205 NS_IMETHODIMP
206 nsJARInputThunk::ReadSegments(nsWriteSegmentFun writer, void *closure,
207 PRUint32 count, PRUint32 *countRead)
209 // stream transport does only calls Read()
210 return NS_ERROR_NOT_IMPLEMENTED;
213 NS_IMETHODIMP
214 nsJARInputThunk::IsNonBlocking(PRBool *nonBlocking)
216 *nonBlocking = PR_FALSE;
217 return NS_OK;
220 //-----------------------------------------------------------------------------
222 nsJARChannel::nsJARChannel()
223 : mContentLength(-1)
224 , mLoadFlags(LOAD_NORMAL)
225 , mStatus(NS_OK)
226 , mIsPending(PR_FALSE)
227 , mIsUnsafe(PR_TRUE)
228 , mJarInput(nsnull)
230 #if defined(PR_LOGGING)
231 if (!gJarProtocolLog)
232 gJarProtocolLog = PR_NewLogModule("nsJarProtocol");
233 #endif
235 // hold an owning reference to the jar handler
236 NS_ADDREF(gJarHandler);
239 nsJARChannel::~nsJARChannel()
241 // with the exception of certain error cases mJarInput will already be null.
242 NS_IF_RELEASE(mJarInput);
244 // release owning reference to the jar handler
245 nsJARProtocolHandler *handler = gJarHandler;
246 NS_RELEASE(handler); // NULL parameter
249 NS_IMPL_ISUPPORTS6(nsJARChannel,
250 nsIRequest,
251 nsIChannel,
252 nsIStreamListener,
253 nsIRequestObserver,
254 nsIDownloadObserver,
255 nsIJARChannel)
257 nsresult
258 nsJARChannel::Init(nsIURI *uri)
260 nsresult rv;
261 mJarURI = do_QueryInterface(uri, &rv);
262 if (NS_FAILED(rv))
263 return rv;
265 mOriginalURI = mJarURI;
267 // Prevent loading jar:javascript URIs (see bug 290982).
268 nsCOMPtr<nsIURI> innerURI;
269 rv = mJarURI->GetJARFile(getter_AddRefs(innerURI));
270 if (NS_FAILED(rv))
271 return rv;
272 PRBool isJS;
273 rv = innerURI->SchemeIs("javascript", &isJS);
274 if (NS_FAILED(rv))
275 return rv;
276 if (isJS) {
277 NS_WARNING("blocking jar:javascript:");
278 return NS_ERROR_INVALID_ARG;
281 #if defined(PR_LOGGING)
282 mJarURI->GetSpec(mSpec);
283 #endif
284 return rv;
287 nsresult
288 nsJARChannel::CreateJarInput(nsIZipReaderCache *jarCache)
290 // important to pass a clone of the file since the nsIFile impl is not
291 // necessarily MT-safe
292 nsCOMPtr<nsIFile> clonedFile;
293 nsresult rv = mJarFile->Clone(getter_AddRefs(clonedFile));
294 if (NS_FAILED(rv)) return rv;
296 mJarInput = new nsJARInputThunk(clonedFile, mJarURI, mJarEntry, jarCache);
297 if (!mJarInput)
298 return NS_ERROR_OUT_OF_MEMORY;
299 NS_ADDREF(mJarInput);
300 return NS_OK;
303 nsresult
304 nsJARChannel::EnsureJarInput(PRBool blocking)
306 LOG(("nsJARChannel::EnsureJarInput [this=%x %s]\n", this, mSpec.get()));
308 nsresult rv;
309 nsCOMPtr<nsIURI> uri;
311 rv = mJarURI->GetJARFile(getter_AddRefs(mJarBaseURI));
312 if (NS_FAILED(rv)) return rv;
314 rv = mJarURI->GetJAREntry(mJarEntry);
315 if (NS_FAILED(rv)) return rv;
317 // The name of the JAR entry must not contain URL-escaped characters:
318 // we're moving from URL domain to a filename domain here. nsStandardURL
319 // does basic escaping by default, which breaks reading zipped files which
320 // have e.g. spaces in their filenames.
321 NS_UnescapeURL(mJarEntry);
323 // try to get a nsIFile directly from the url, which will often succeed.
325 nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mJarBaseURI);
326 if (fileURL)
327 fileURL->GetFile(getter_AddRefs(mJarFile));
330 if (mJarFile) {
331 mIsUnsafe = PR_FALSE;
333 // NOTE: we do not need to deal with mSecurityInfo here,
334 // because we're loading from a local file
335 rv = CreateJarInput(gJarHandler->JarCache());
337 else if (blocking) {
338 NS_NOTREACHED("need sync downloader");
339 rv = NS_ERROR_NOT_IMPLEMENTED;
341 else {
342 // kick off an async download of the base URI...
343 rv = NS_NewDownloader(getter_AddRefs(mDownloader), this);
344 if (NS_SUCCEEDED(rv))
345 rv = NS_OpenURI(mDownloader, nsnull, mJarBaseURI, nsnull,
346 mLoadGroup, mCallbacks,
347 mLoadFlags & ~(LOAD_DOCUMENT_URI | LOAD_CALL_CONTENT_SNIFFERS));
349 return rv;
353 //-----------------------------------------------------------------------------
354 // nsIRequest
355 //-----------------------------------------------------------------------------
357 NS_IMETHODIMP
358 nsJARChannel::GetName(nsACString &result)
360 return mJarURI->GetSpec(result);
363 NS_IMETHODIMP
364 nsJARChannel::IsPending(PRBool *result)
366 *result = mIsPending;
367 return NS_OK;
370 NS_IMETHODIMP
371 nsJARChannel::GetStatus(nsresult *status)
373 if (mPump && NS_SUCCEEDED(mStatus))
374 mPump->GetStatus(status);
375 else
376 *status = mStatus;
377 return NS_OK;
380 NS_IMETHODIMP
381 nsJARChannel::Cancel(nsresult status)
383 mStatus = status;
384 if (mPump)
385 return mPump->Cancel(status);
387 NS_ASSERTION(!mIsPending, "need to implement cancel when downloading");
388 return NS_OK;
391 NS_IMETHODIMP
392 nsJARChannel::Suspend()
394 if (mPump)
395 return mPump->Suspend();
397 NS_ASSERTION(!mIsPending, "need to implement suspend when downloading");
398 return NS_OK;
401 NS_IMETHODIMP
402 nsJARChannel::Resume()
404 if (mPump)
405 return mPump->Resume();
407 NS_ASSERTION(!mIsPending, "need to implement resume when downloading");
408 return NS_OK;
411 NS_IMETHODIMP
412 nsJARChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
414 *aLoadFlags = mLoadFlags;
415 return NS_OK;
418 NS_IMETHODIMP
419 nsJARChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
421 mLoadFlags = aLoadFlags;
422 return NS_OK;
425 NS_IMETHODIMP
426 nsJARChannel::GetLoadGroup(nsILoadGroup **aLoadGroup)
428 NS_IF_ADDREF(*aLoadGroup = mLoadGroup);
429 return NS_OK;
432 NS_IMETHODIMP
433 nsJARChannel::SetLoadGroup(nsILoadGroup *aLoadGroup)
435 mLoadGroup = aLoadGroup;
436 return NS_OK;
439 //-----------------------------------------------------------------------------
440 // nsIChannel
441 //-----------------------------------------------------------------------------
443 NS_IMETHODIMP
444 nsJARChannel::GetOriginalURI(nsIURI **aURI)
446 *aURI = mOriginalURI;
447 NS_ADDREF(*aURI);
448 return NS_OK;
451 NS_IMETHODIMP
452 nsJARChannel::SetOriginalURI(nsIURI *aURI)
454 NS_ENSURE_ARG_POINTER(aURI);
455 mOriginalURI = aURI;
456 return NS_OK;
459 NS_IMETHODIMP
460 nsJARChannel::GetURI(nsIURI **aURI)
462 NS_IF_ADDREF(*aURI = mJarURI);
463 return NS_OK;
466 NS_IMETHODIMP
467 nsJARChannel::GetOwner(nsISupports **result)
469 nsresult rv;
471 if (mOwner) {
472 NS_ADDREF(*result = mOwner);
473 return NS_OK;
476 if (!mJarInput) {
477 *result = nsnull;
478 return NS_OK;
481 //-- Verify signature, if one is present, and set owner accordingly
482 nsCOMPtr<nsIZipReader> jarReader;
483 mJarInput->GetJarReader(getter_AddRefs(jarReader));
484 if (!jarReader)
485 return NS_ERROR_NOT_INITIALIZED;
487 nsCOMPtr<nsIJAR> jar = do_QueryInterface(jarReader, &rv);
488 if (NS_FAILED(rv)) {
489 NS_ERROR("nsIJAR not supported");
490 return rv;
493 nsCOMPtr<nsIPrincipal> cert;
494 rv = jar->GetCertificatePrincipal(mJarEntry.get(), getter_AddRefs(cert));
495 if (NS_FAILED(rv)) return rv;
497 if (cert) {
498 nsCAutoString certFingerprint;
499 rv = cert->GetFingerprint(certFingerprint);
500 if (NS_FAILED(rv)) return rv;
502 nsCAutoString subjectName;
503 rv = cert->GetSubjectName(subjectName);
504 if (NS_FAILED(rv)) return rv;
506 nsCAutoString prettyName;
507 rv = cert->GetPrettyName(prettyName);
508 if (NS_FAILED(rv)) return rv;
510 nsCOMPtr<nsISupports> certificate;
511 rv = cert->GetCertificate(getter_AddRefs(certificate));
512 if (NS_FAILED(rv)) return rv;
514 nsCOMPtr<nsIScriptSecurityManager> secMan =
515 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
516 if (NS_FAILED(rv)) return rv;
518 rv = secMan->GetCertificatePrincipal(certFingerprint, subjectName,
519 prettyName, certificate,
520 mJarBaseURI,
521 getter_AddRefs(cert));
522 if (NS_FAILED(rv)) return rv;
524 mOwner = do_QueryInterface(cert, &rv);
525 if (NS_FAILED(rv)) return rv;
527 NS_ADDREF(*result = mOwner);
529 return NS_OK;
532 NS_IMETHODIMP
533 nsJARChannel::SetOwner(nsISupports *aOwner)
535 mOwner = aOwner;
536 return NS_OK;
539 NS_IMETHODIMP
540 nsJARChannel::GetNotificationCallbacks(nsIInterfaceRequestor **aCallbacks)
542 NS_IF_ADDREF(*aCallbacks = mCallbacks);
543 return NS_OK;
546 NS_IMETHODIMP
547 nsJARChannel::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks)
549 mCallbacks = aCallbacks;
550 return NS_OK;
553 NS_IMETHODIMP
554 nsJARChannel::GetSecurityInfo(nsISupports **aSecurityInfo)
556 NS_PRECONDITION(aSecurityInfo, "Null out param");
557 NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo);
558 return NS_OK;
561 NS_IMETHODIMP
562 nsJARChannel::GetContentType(nsACString &result)
564 if (mContentType.IsEmpty()) {
566 // generate content type and set it
568 const char *ext = nsnull, *fileName = mJarEntry.get();
569 PRInt32 len = mJarEntry.Length();
571 // check if we're displaying a directory
572 // mJarEntry will be empty if we're trying to display
573 // the topmost directory in a zip, e.g. jar:foo.zip!/
574 if (ENTRY_IS_DIRECTORY(mJarEntry)) {
575 mContentType.AssignLiteral(APPLICATION_HTTP_INDEX_FORMAT);
577 else {
578 // not a directory, take a guess by its extension
579 for (PRInt32 i = len-1; i >= 0; i--) {
580 if (fileName[i] == '.') {
581 ext = &fileName[i + 1];
582 break;
585 if (ext) {
586 nsIMIMEService *mimeServ = gJarHandler->MimeService();
587 if (mimeServ)
588 mimeServ->GetTypeFromExtension(nsDependentCString(ext), mContentType);
590 if (mContentType.IsEmpty())
591 mContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE);
594 result = mContentType;
595 return NS_OK;
598 NS_IMETHODIMP
599 nsJARChannel::SetContentType(const nsACString &aContentType)
601 // If someone gives us a type hint we should just use that type instead of
602 // doing our guessing. So we don't care when this is being called.
604 // mContentCharset is unchanged if not parsed
605 NS_ParseContentType(aContentType, mContentType, mContentCharset);
606 return NS_OK;
609 NS_IMETHODIMP
610 nsJARChannel::GetContentCharset(nsACString &aContentCharset)
612 // If someone gives us a charset hint we should just use that charset.
613 // So we don't care when this is being called.
614 aContentCharset = mContentCharset;
615 return NS_OK;
618 NS_IMETHODIMP
619 nsJARChannel::SetContentCharset(const nsACString &aContentCharset)
621 mContentCharset = aContentCharset;
622 return NS_OK;
625 NS_IMETHODIMP
626 nsJARChannel::GetContentLength(PRInt32 *result)
628 // if content length is unknown, query mJarInput...
629 if (mContentLength < 0 && mJarInput)
630 mContentLength = mJarInput->GetContentLength();
632 *result = mContentLength;
633 return NS_OK;
636 NS_IMETHODIMP
637 nsJARChannel::SetContentLength(PRInt32 aContentLength)
639 // XXX does this really make any sense at all?
640 mContentLength = aContentLength;
641 return NS_OK;
644 NS_IMETHODIMP
645 nsJARChannel::Open(nsIInputStream **stream)
647 LOG(("nsJARChannel::Open [this=%x]\n", this));
649 NS_ENSURE_TRUE(!mJarInput, NS_ERROR_IN_PROGRESS);
650 NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
652 mJarFile = nsnull;
653 mIsUnsafe = PR_TRUE;
655 nsresult rv = EnsureJarInput(PR_TRUE);
656 if (NS_FAILED(rv)) return rv;
658 if (!mJarInput)
659 return NS_ERROR_UNEXPECTED;
661 // force load the jar file now so GetContentLength will return a
662 // meaningful value once we return.
663 mJarInput->EnsureJarStream();
665 NS_ADDREF(*stream = mJarInput);
666 return NS_OK;
669 NS_IMETHODIMP
670 nsJARChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctx)
672 LOG(("nsJARChannel::AsyncOpen [this=%x]\n", this));
674 NS_ENSURE_ARG_POINTER(listener);
675 NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
677 mJarFile = nsnull;
678 mIsUnsafe = PR_TRUE;
680 // Initialize mProgressSink
681 NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, mProgressSink);
683 nsresult rv = EnsureJarInput(PR_FALSE);
684 if (NS_FAILED(rv)) return rv;
686 if (mJarInput) {
687 // create input stream pump
688 rv = NS_NewInputStreamPump(getter_AddRefs(mPump), mJarInput);
689 if (NS_FAILED(rv)) return rv;
691 rv = mPump->AsyncRead(this, nsnull);
692 if (NS_FAILED(rv)) return rv;
695 if (mLoadGroup)
696 mLoadGroup->AddRequest(this, nsnull);
698 mListener = listener;
699 mListenerContext = ctx;
700 mIsPending = PR_TRUE;
701 return NS_OK;
704 //-----------------------------------------------------------------------------
705 // nsIJARChannel
706 //-----------------------------------------------------------------------------
707 NS_IMETHODIMP
708 nsJARChannel::GetIsUnsafe(PRBool *isUnsafe)
710 *isUnsafe = mIsUnsafe;
711 return NS_OK;
714 //-----------------------------------------------------------------------------
715 // nsIDownloadObserver
716 //-----------------------------------------------------------------------------
718 NS_IMETHODIMP
719 nsJARChannel::OnDownloadComplete(nsIDownloader *downloader,
720 nsIRequest *request,
721 nsISupports *context,
722 nsresult status,
723 nsIFile *file)
725 nsresult rv;
727 nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
728 if (channel) {
729 PRUint32 loadFlags;
730 channel->GetLoadFlags(&loadFlags);
731 if (loadFlags & LOAD_REPLACE) {
732 mLoadFlags |= LOAD_REPLACE;
734 if (!mOriginalURI) {
735 SetOriginalURI(mJarURI);
738 nsCOMPtr<nsIURI> innerURI;
739 rv = channel->GetURI(getter_AddRefs(innerURI));
740 if (NS_SUCCEEDED(rv)) {
741 nsCOMPtr<nsIJARURI> newURI;
742 rv = mJarURI->CloneWithJARFile(innerURI,
743 getter_AddRefs(newURI));
744 if (NS_SUCCEEDED(rv)) {
745 mJarURI = newURI;
748 if (NS_SUCCEEDED(status)) {
749 status = rv;
754 if (NS_SUCCEEDED(status) && channel) {
755 // Grab the security info from our base channel
756 channel->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
758 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
759 if (httpChannel) {
760 // We only want to run scripts if the server really intended to
761 // send us a JAR file. Check the server-supplied content type for
762 // a JAR type.
763 nsCAutoString header;
764 httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Content-Type"),
765 header);
767 nsCAutoString contentType;
768 nsCAutoString charset;
769 NS_ParseContentType(header, contentType, charset);
771 mIsUnsafe = !contentType.EqualsLiteral("application/java-archive") &&
772 !contentType.EqualsLiteral("application/x-jar");
773 } else {
774 nsCOMPtr<nsIJARChannel> innerJARChannel(do_QueryInterface(channel));
775 if (innerJARChannel) {
776 PRBool unsafe;
777 innerJARChannel->GetIsUnsafe(&unsafe);
778 mIsUnsafe = unsafe;
783 if (NS_SUCCEEDED(status) && mIsUnsafe) {
784 PRBool allowUnpack = PR_FALSE;
786 nsCOMPtr<nsIPrefBranch> prefs =
787 do_GetService(NS_PREFSERVICE_CONTRACTID);
788 if (prefs) {
789 prefs->GetBoolPref("network.jar.open-unsafe-types", &allowUnpack);
792 if (!allowUnpack) {
793 status = NS_ERROR_UNSAFE_CONTENT_TYPE;
797 if (NS_SUCCEEDED(status)) {
798 mJarFile = file;
800 rv = CreateJarInput(nsnull);
801 if (NS_SUCCEEDED(rv)) {
802 // create input stream pump
803 rv = NS_NewInputStreamPump(getter_AddRefs(mPump), mJarInput);
804 if (NS_SUCCEEDED(rv))
805 rv = mPump->AsyncRead(this, nsnull);
807 status = rv;
810 if (NS_FAILED(status)) {
811 mStatus = status;
812 OnStartRequest(nsnull, nsnull);
813 OnStopRequest(nsnull, nsnull, status);
816 return NS_OK;
819 //-----------------------------------------------------------------------------
820 // nsIStreamListener
821 //-----------------------------------------------------------------------------
823 NS_IMETHODIMP
824 nsJARChannel::OnStartRequest(nsIRequest *req, nsISupports *ctx)
826 LOG(("nsJARChannel::OnStartRequest [this=%x %s]\n", this, mSpec.get()));
828 return mListener->OnStartRequest(this, mListenerContext);
831 NS_IMETHODIMP
832 nsJARChannel::OnStopRequest(nsIRequest *req, nsISupports *ctx, nsresult status)
834 LOG(("nsJARChannel::OnStopRequest [this=%x %s status=%x]\n",
835 this, mSpec.get(), status));
837 if (NS_SUCCEEDED(mStatus))
838 mStatus = status;
840 if (mListener) {
841 mListener->OnStopRequest(this, mListenerContext, status);
842 mListener = 0;
843 mListenerContext = 0;
846 if (mLoadGroup)
847 mLoadGroup->RemoveRequest(this, nsnull, status);
849 mPump = 0;
850 NS_IF_RELEASE(mJarInput);
851 mIsPending = PR_FALSE;
852 mDownloader = 0; // this may delete the underlying jar file
854 // Drop notification callbacks to prevent cycles.
855 mCallbacks = 0;
856 mProgressSink = 0;
858 return NS_OK;
861 NS_IMETHODIMP
862 nsJARChannel::OnDataAvailable(nsIRequest *req, nsISupports *ctx,
863 nsIInputStream *stream,
864 PRUint32 offset, PRUint32 count)
866 #if defined(PR_LOGGING)
867 LOG(("nsJARChannel::OnDataAvailable [this=%x %s]\n", this, mSpec.get()));
868 #endif
870 nsresult rv;
872 rv = mListener->OnDataAvailable(this, mListenerContext, stream, offset, count);
874 // simply report progress here instead of hooking ourselves up as a
875 // nsITransportEventSink implementation.
876 // XXX do the 64-bit stuff for real
877 if (mProgressSink && NS_SUCCEEDED(rv) && !(mLoadFlags & LOAD_BACKGROUND))
878 mProgressSink->OnProgress(this, nsnull, nsUint64(offset + count),
879 nsUint64(mContentLength));
881 return rv; // let the pump cancel on failure