Back to index

lightning-sunbird  0.9+nobinonly
nsJARChannel.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org Code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1998
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsJARChannel.h"
00040 #include "nsJARProtocolHandler.h"
00041 #include "nsMimeTypes.h"
00042 #include "nsNetUtil.h"
00043 #include "nsInt64.h"
00044 #include "nsIPrefService.h"
00045 #include "nsIPrefBranch.h"
00046 
00047 #include "nsIScriptSecurityManager.h"
00048 #include "nsIPrincipal.h"
00049 #include "nsIFileURL.h"
00050 #include "nsIJAR.h"
00051 
00052 static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID);
00053 
00054 //-----------------------------------------------------------------------------
00055 
00056 #if defined(PR_LOGGING)
00057 //
00058 // set NSPR_LOG_MODULES=nsJarProtocol:5
00059 //
00060 static PRLogModuleInfo *gJarProtocolLog = nsnull;
00061 #endif
00062 
00063 #define LOG(args)     PR_LOG(gJarProtocolLog, PR_LOG_DEBUG, args)
00064 #define LOG_ENABLED() PR_LOG_TEST(gJarProtocolLog, 4)
00065 
00066 //-----------------------------------------------------------------------------
00067 // nsJARInputThunk
00068 //
00069 // this class allows us to do some extra work on the stream transport thread.
00070 //-----------------------------------------------------------------------------
00071 
00072 class nsJARInputThunk : public nsIInputStream
00073 {
00074 public:
00075     NS_DECL_ISUPPORTS
00076     NS_DECL_NSIINPUTSTREAM
00077 
00078     nsJARInputThunk(nsIFile *jarFile, const nsACString &jarEntry,
00079                     nsIZipReaderCache *jarCache)
00080         : mJarCache(jarCache)
00081         , mJarFile(jarFile)
00082         , mJarEntry(jarEntry)
00083         , mContentLength(-1)
00084     {
00085         NS_ASSERTION(mJarFile, "no jar file");
00086     }
00087 
00088     virtual ~nsJARInputThunk()
00089     {
00090         if (!mJarCache && mJarReader)
00091             mJarReader->Close();
00092     }
00093 
00094     void GetJarReader(nsIZipReader **result)
00095     {
00096         NS_IF_ADDREF(*result = mJarReader);
00097     }
00098 
00099     PRInt32 GetContentLength()
00100     {
00101         return mContentLength;
00102     }
00103 
00104     nsresult EnsureJarStream();
00105 
00106 private:
00107 
00108     nsCOMPtr<nsIZipReaderCache> mJarCache;
00109     nsCOMPtr<nsIZipReader>      mJarReader;
00110     nsCOMPtr<nsIFile>           mJarFile;
00111     nsCOMPtr<nsIInputStream>    mJarStream;
00112     nsCString                   mJarEntry;
00113     PRInt32                     mContentLength;
00114 };
00115 
00116 NS_IMPL_THREADSAFE_ISUPPORTS1(nsJARInputThunk, nsIInputStream)
00117 
00118 nsresult
00119 nsJARInputThunk::EnsureJarStream()
00120 {
00121     if (mJarStream)
00122         return NS_OK;
00123 
00124     nsresult rv;
00125     if (mJarCache)
00126         rv = mJarCache->GetZip(mJarFile, getter_AddRefs(mJarReader));
00127     else {
00128         // create an uncached jar reader
00129         mJarReader = do_CreateInstance(kZipReaderCID, &rv);
00130         if (NS_FAILED(rv)) return rv;
00131 
00132         rv = mJarReader->Init(mJarFile);
00133         if (NS_FAILED(rv)) return rv;
00134 
00135         rv = mJarReader->Open();
00136     }
00137     if (NS_FAILED(rv)) return rv;
00138 
00139     rv = mJarReader->GetInputStream(mJarEntry.get(),
00140                                     getter_AddRefs(mJarStream));
00141     if (NS_FAILED(rv)) return rv;
00142 
00143     // ask the JarStream for the content length
00144     mJarStream->Available((PRUint32 *) &mContentLength);
00145 
00146     return NS_OK;
00147 }
00148 
00149 NS_IMETHODIMP
00150 nsJARInputThunk::Close()
00151 {
00152     if (mJarStream)
00153         return mJarStream->Close();
00154 
00155     return NS_OK;
00156 }
00157 
00158 NS_IMETHODIMP
00159 nsJARInputThunk::Available(PRUint32 *avail)
00160 {
00161     nsresult rv = EnsureJarStream();
00162     if (NS_FAILED(rv)) return rv;
00163 
00164     return mJarStream->Available(avail);
00165 }
00166 
00167 NS_IMETHODIMP
00168 nsJARInputThunk::Read(char *buf, PRUint32 count, PRUint32 *countRead)
00169 {
00170     nsresult rv = EnsureJarStream();
00171     if (NS_FAILED(rv)) return rv;
00172 
00173     return mJarStream->Read(buf, count, countRead);
00174 }
00175 
00176 NS_IMETHODIMP
00177 nsJARInputThunk::ReadSegments(nsWriteSegmentFun writer, void *closure,
00178                               PRUint32 count, PRUint32 *countRead)
00179 {
00180     // stream transport does only calls Read()
00181     return NS_ERROR_NOT_IMPLEMENTED;
00182 }
00183 
00184 NS_IMETHODIMP
00185 nsJARInputThunk::IsNonBlocking(PRBool *nonBlocking)
00186 {
00187     *nonBlocking = PR_FALSE;
00188     return NS_OK;
00189 }
00190 
00191 //-----------------------------------------------------------------------------
00192 
00193 nsJARChannel::nsJARChannel()
00194     : mContentLength(-1)
00195     , mLoadFlags(LOAD_NORMAL)
00196     , mStatus(NS_OK)
00197     , mIsPending(PR_FALSE)
00198     , mIsUnsafe(PR_TRUE)
00199     , mJarInput(nsnull)
00200 {
00201 #if defined(PR_LOGGING)
00202     if (!gJarProtocolLog)
00203         gJarProtocolLog = PR_NewLogModule("nsJarProtocol");
00204 #endif
00205 
00206     // hold an owning reference to the jar handler
00207     NS_ADDREF(gJarHandler);
00208 }
00209 
00210 nsJARChannel::~nsJARChannel()
00211 {
00212     // with the exception of certain error cases mJarInput will already be null.
00213     NS_IF_RELEASE(mJarInput);
00214 
00215     // release owning reference to the jar handler
00216     nsJARProtocolHandler *handler = gJarHandler;
00217     NS_RELEASE(handler); // NULL parameter
00218 }
00219 
00220 NS_IMPL_ISUPPORTS7(nsJARChannel,
00221                    nsIRequest,
00222                    nsIChannel,
00223                    nsIStreamListener,
00224                    nsIRequestObserver,
00225                    nsIDownloadObserver,
00226                    nsIJARChannel,
00227                    nsIJARChannel_MOZILLA_1_8_BRANCH)
00228 
00229 nsresult 
00230 nsJARChannel::Init(nsIURI *uri)
00231 {
00232     nsresult rv;
00233     mJarURI = do_QueryInterface(uri, &rv);
00234     if (NS_FAILED(rv))
00235         return rv;
00236 
00237     // Prevent loading jar:javascript URIs (see bug 290982).
00238     nsCOMPtr<nsIURI> innerURI;
00239     rv = mJarURI->GetJARFile(getter_AddRefs(innerURI));
00240     if (NS_FAILED(rv))
00241         return rv;
00242     PRBool isJS;
00243     rv = innerURI->SchemeIs("javascript", &isJS);
00244     if (NS_FAILED(rv))
00245         return rv;
00246     if (isJS) {
00247         NS_WARNING("blocking jar:javascript:");
00248         return NS_ERROR_INVALID_ARG;
00249     }
00250 
00251 #if defined(PR_LOGGING)
00252     mJarURI->GetSpec(mSpec);
00253 #endif
00254     return rv;
00255 }
00256 
00257 nsresult
00258 nsJARChannel::CreateJarInput(nsIZipReaderCache *jarCache)
00259 {
00260     // important to pass a clone of the file since the nsIFile impl is not
00261     // necessarily MT-safe
00262     nsCOMPtr<nsIFile> clonedFile;
00263     nsresult rv = mJarFile->Clone(getter_AddRefs(clonedFile));
00264     if (NS_FAILED(rv)) return rv;
00265 
00266     mJarInput = new nsJARInputThunk(clonedFile, mJarEntry, jarCache);
00267     if (!mJarInput)
00268         return NS_ERROR_OUT_OF_MEMORY;
00269     NS_ADDREF(mJarInput);
00270     return NS_OK;
00271 }
00272 
00273 nsresult
00274 nsJARChannel::EnsureJarInput(PRBool blocking)
00275 {
00276     LOG(("nsJARChannel::EnsureJarInput [this=%x %s]\n", this, mSpec.get()));
00277 
00278     nsresult rv;
00279     nsCOMPtr<nsIURI> uri;
00280 
00281     rv = mJarURI->GetJARFile(getter_AddRefs(mJarBaseURI));
00282     if (NS_FAILED(rv)) return rv;
00283 
00284     rv = mJarURI->GetJAREntry(mJarEntry);
00285     if (NS_FAILED(rv)) return rv;
00286 
00287     // try to get a nsIFile directly from the url, which will often succeed.
00288     {
00289         nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mJarBaseURI);
00290         if (fileURL)
00291             fileURL->GetFile(getter_AddRefs(mJarFile));
00292     }
00293 
00294     if (mJarFile) {
00295         mIsUnsafe = PR_FALSE;
00296 
00297         // NOTE: we do not need to deal with mSecurityInfo here,
00298         // because we're loading from a local file
00299         rv = CreateJarInput(gJarHandler->JarCache());
00300     }
00301     else if (blocking) {
00302         NS_NOTREACHED("need sync downloader");
00303         rv = NS_ERROR_NOT_IMPLEMENTED;
00304     }
00305     else {
00306         // kick off an async download of the base URI...
00307         rv = NS_NewDownloader(getter_AddRefs(mDownloader), this);
00308         if (NS_SUCCEEDED(rv))
00309             rv = NS_OpenURI(mDownloader, nsnull, mJarBaseURI, nsnull,
00310                             mLoadGroup, mCallbacks,
00311                             mLoadFlags & ~(LOAD_DOCUMENT_URI | LOAD_CALL_CONTENT_SNIFFERS));
00312     }
00313     return rv;
00314 
00315 }
00316 
00317 //-----------------------------------------------------------------------------
00318 // nsIRequest
00319 //-----------------------------------------------------------------------------
00320 
00321 NS_IMETHODIMP
00322 nsJARChannel::GetName(nsACString &result)
00323 {
00324     return mJarURI->GetSpec(result);
00325 }
00326 
00327 NS_IMETHODIMP
00328 nsJARChannel::IsPending(PRBool *result)
00329 {
00330     *result = mIsPending;
00331     return NS_OK;
00332 }
00333 
00334 NS_IMETHODIMP
00335 nsJARChannel::GetStatus(nsresult *status)
00336 {
00337     if (mPump && NS_SUCCEEDED(mStatus))
00338         mPump->GetStatus(status);
00339     else
00340         *status = mStatus;
00341     return NS_OK;
00342 }
00343 
00344 NS_IMETHODIMP
00345 nsJARChannel::Cancel(nsresult status)
00346 {
00347     mStatus = status;
00348     if (mPump)
00349         return mPump->Cancel(status);
00350 
00351     NS_ASSERTION(!mIsPending, "need to implement cancel when downloading");
00352     return NS_OK;
00353 }
00354 
00355 NS_IMETHODIMP
00356 nsJARChannel::Suspend()
00357 {
00358     if (mPump)
00359         return mPump->Suspend();
00360 
00361     NS_ASSERTION(!mIsPending, "need to implement suspend when downloading");
00362     return NS_OK;
00363 }
00364 
00365 NS_IMETHODIMP
00366 nsJARChannel::Resume()
00367 {
00368     if (mPump)
00369         return mPump->Resume();
00370 
00371     NS_ASSERTION(!mIsPending, "need to implement resume when downloading");
00372     return NS_OK;
00373 }
00374 
00375 NS_IMETHODIMP
00376 nsJARChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
00377 {
00378     *aLoadFlags = mLoadFlags;
00379     return NS_OK;
00380 }
00381 
00382 NS_IMETHODIMP
00383 nsJARChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
00384 {
00385     mLoadFlags = aLoadFlags;
00386     return NS_OK;
00387 }
00388 
00389 NS_IMETHODIMP
00390 nsJARChannel::GetLoadGroup(nsILoadGroup **aLoadGroup)
00391 {
00392     NS_IF_ADDREF(*aLoadGroup = mLoadGroup);
00393     return NS_OK;
00394 }
00395 
00396 NS_IMETHODIMP
00397 nsJARChannel::SetLoadGroup(nsILoadGroup *aLoadGroup)
00398 {
00399     mLoadGroup = aLoadGroup;
00400     return NS_OK;
00401 }
00402 
00403 //-----------------------------------------------------------------------------
00404 // nsIChannel
00405 //-----------------------------------------------------------------------------
00406 
00407 NS_IMETHODIMP
00408 nsJARChannel::GetOriginalURI(nsIURI **aURI)
00409 {
00410     if (mOriginalURI)
00411         *aURI = mOriginalURI;
00412     else
00413         *aURI = mJarURI;
00414     NS_IF_ADDREF(*aURI);
00415     return NS_OK;
00416 }
00417 
00418 NS_IMETHODIMP
00419 nsJARChannel::SetOriginalURI(nsIURI *aURI)
00420 {
00421     mOriginalURI = aURI;
00422     return NS_OK;
00423 }
00424 
00425 NS_IMETHODIMP
00426 nsJARChannel::GetURI(nsIURI **aURI)
00427 {
00428     NS_IF_ADDREF(*aURI = mJarURI);
00429     return NS_OK;
00430 }
00431 
00432 NS_IMETHODIMP
00433 nsJARChannel::GetOwner(nsISupports **result)
00434 {
00435     nsresult rv;
00436 
00437     if (mOwner) {
00438         NS_ADDREF(*result = mOwner);
00439         return NS_OK;
00440     }
00441 
00442     if (!mJarInput) {
00443         *result = nsnull;
00444         return NS_OK;
00445     }
00446 
00447     //-- Verify signature, if one is present, and set owner accordingly
00448     nsCOMPtr<nsIZipReader> jarReader;
00449     mJarInput->GetJarReader(getter_AddRefs(jarReader));
00450     if (!jarReader)
00451         return NS_ERROR_NOT_INITIALIZED;
00452 
00453     nsCOMPtr<nsIJAR> jar = do_QueryInterface(jarReader, &rv);
00454     if (NS_FAILED(rv)) {
00455         NS_ERROR("nsIJAR not supported");
00456         return rv;
00457     }
00458 
00459     nsCOMPtr<nsIPrincipal> cert;
00460     rv = jar->GetCertificatePrincipal(mJarEntry.get(), getter_AddRefs(cert));
00461     if (NS_FAILED(rv)) return rv;
00462 
00463     if (cert) {
00464         nsCAutoString certFingerprint;
00465         rv = cert->GetFingerprint(certFingerprint);
00466         if (NS_FAILED(rv)) return rv;
00467 
00468         nsCAutoString subjectName;
00469         rv = cert->GetSubjectName(subjectName);
00470         if (NS_FAILED(rv)) return rv;
00471 
00472         nsCAutoString prettyName;
00473         rv = cert->GetPrettyName(prettyName);
00474         if (NS_FAILED(rv)) return rv;
00475 
00476         nsCOMPtr<nsISupports> certificate;
00477         rv = cert->GetCertificate(getter_AddRefs(certificate));
00478         if (NS_FAILED(rv)) return rv;
00479         
00480         nsCOMPtr<nsIScriptSecurityManager> secMan = 
00481                  do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
00482         if (NS_FAILED(rv)) return rv;
00483 
00484         rv = secMan->GetCertificatePrincipal(certFingerprint, subjectName,
00485                                              prettyName, certificate,
00486                                              mJarBaseURI,
00487                                              getter_AddRefs(cert));
00488         if (NS_FAILED(rv)) return rv;
00489 
00490         mOwner = do_QueryInterface(cert, &rv);
00491         if (NS_FAILED(rv)) return rv;
00492 
00493         NS_ADDREF(*result = mOwner);
00494     }
00495     return NS_OK;
00496 }
00497 
00498 NS_IMETHODIMP
00499 nsJARChannel::SetOwner(nsISupports *aOwner)
00500 {
00501     mOwner = aOwner;
00502     return NS_OK;
00503 }
00504 
00505 NS_IMETHODIMP
00506 nsJARChannel::GetNotificationCallbacks(nsIInterfaceRequestor **aCallbacks)
00507 {
00508     NS_IF_ADDREF(*aCallbacks = mCallbacks);
00509     return NS_OK;
00510 }
00511 
00512 NS_IMETHODIMP
00513 nsJARChannel::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks)
00514 {
00515     mCallbacks = aCallbacks;
00516     return NS_OK;
00517 }
00518 
00519 NS_IMETHODIMP 
00520 nsJARChannel::GetSecurityInfo(nsISupports **aSecurityInfo)
00521 {
00522     NS_PRECONDITION(aSecurityInfo, "Null out param");
00523     NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo);
00524     return NS_OK;
00525 }
00526 
00527 NS_IMETHODIMP
00528 nsJARChannel::GetContentType(nsACString &result)
00529 {
00530     if (mContentType.IsEmpty()) {
00531         //
00532         // generate content type and set it
00533         //
00534         if (mJarEntry.IsEmpty()) {
00535             LOG(("mJarEntry is empty!\n"));
00536             return NS_ERROR_NOT_AVAILABLE;
00537         }
00538     
00539         const char *ext = nsnull, *fileName = mJarEntry.get();
00540         PRInt32 len = mJarEntry.Length();
00541         for (PRInt32 i = len-1; i >= 0; i--) {
00542             if (fileName[i] == '.') {
00543                 ext = &fileName[i + 1];
00544                 break;
00545             }
00546         }
00547         if (ext) {
00548             nsIMIMEService *mimeServ = gJarHandler->MimeService();
00549             if (mimeServ)
00550                 mimeServ->GetTypeFromExtension(nsDependentCString(ext), mContentType);
00551         }
00552         if (mContentType.IsEmpty())
00553             mContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE);
00554     }
00555     result = mContentType;
00556     return NS_OK;
00557 }
00558 
00559 NS_IMETHODIMP
00560 nsJARChannel::SetContentType(const nsACString &aContentType)
00561 {
00562     // If someone gives us a type hint we should just use that type instead of
00563     // doing our guessing.  So we don't care when this is being called.
00564 
00565     // mContentCharset is unchanged if not parsed
00566     NS_ParseContentType(aContentType, mContentType, mContentCharset);
00567     return NS_OK;
00568 }
00569 
00570 NS_IMETHODIMP
00571 nsJARChannel::GetContentCharset(nsACString &aContentCharset)
00572 {
00573     // If someone gives us a charset hint we should just use that charset.
00574     // So we don't care when this is being called.
00575     aContentCharset = mContentCharset;
00576     return NS_OK;
00577 }
00578 
00579 NS_IMETHODIMP
00580 nsJARChannel::SetContentCharset(const nsACString &aContentCharset)
00581 {
00582     mContentCharset = aContentCharset;
00583     return NS_OK;
00584 }
00585 
00586 NS_IMETHODIMP
00587 nsJARChannel::GetContentLength(PRInt32 *result)
00588 {
00589     // if content length is unknown, query mJarInput...
00590     if (mContentLength < 0 && mJarInput)
00591         mContentLength = mJarInput->GetContentLength();
00592 
00593     *result = mContentLength;
00594     return NS_OK;
00595 }
00596 
00597 NS_IMETHODIMP
00598 nsJARChannel::SetContentLength(PRInt32 aContentLength)
00599 {
00600     // XXX does this really make any sense at all?
00601     mContentLength = aContentLength;
00602     return NS_OK;
00603 }
00604 
00605 NS_IMETHODIMP
00606 nsJARChannel::Open(nsIInputStream **stream)
00607 {
00608     LOG(("nsJARChannel::Open [this=%x]\n", this));
00609 
00610     NS_ENSURE_TRUE(!mJarInput, NS_ERROR_IN_PROGRESS);
00611     NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
00612 
00613     mJarFile = nsnull;
00614     mIsUnsafe = PR_TRUE;
00615 
00616     nsresult rv = EnsureJarInput(PR_TRUE);
00617     if (NS_FAILED(rv)) return rv;
00618 
00619     if (!mJarInput)
00620         return NS_ERROR_UNEXPECTED;
00621 
00622     // force load the jar file now so GetContentLength will return a
00623     // meaningful value once we return.
00624     mJarInput->EnsureJarStream();
00625 
00626     NS_ADDREF(*stream = mJarInput);
00627     return NS_OK;
00628 }
00629 
00630 NS_IMETHODIMP
00631 nsJARChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctx)
00632 {
00633     LOG(("nsJARChannel::AsyncOpen [this=%x]\n", this));
00634 
00635     NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
00636 
00637     mJarFile = nsnull;
00638     mIsUnsafe = PR_TRUE;
00639 
00640     // Initialize mProgressSink
00641     NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, mProgressSink);
00642 
00643     nsresult rv = EnsureJarInput(PR_FALSE);
00644     if (NS_FAILED(rv)) return rv;
00645 
00646     if (mJarInput) {
00647         // create input stream pump
00648         rv = NS_NewInputStreamPump(getter_AddRefs(mPump), mJarInput);
00649         if (NS_FAILED(rv)) return rv;
00650 
00651         rv = mPump->AsyncRead(this, nsnull);
00652         if (NS_FAILED(rv)) return rv;
00653     }
00654 
00655     if (mLoadGroup)
00656         mLoadGroup->AddRequest(this, nsnull);
00657 
00658     mListener = listener;
00659     mListenerContext = ctx;
00660     mIsPending = PR_TRUE;
00661     return NS_OK;
00662 }
00663 
00664 //-----------------------------------------------------------------------------
00665 // nsIJARChannel
00666 //-----------------------------------------------------------------------------
00667 NS_IMETHODIMP
00668 nsJARChannel::GetIsUnsafe(PRBool *isUnsafe)
00669 {
00670     *isUnsafe = mIsUnsafe;
00671     return NS_OK;
00672 }
00673 
00674 //-----------------------------------------------------------------------------
00675 // nsIDownloadObserver
00676 //-----------------------------------------------------------------------------
00677 
00678 NS_IMETHODIMP
00679 nsJARChannel::OnDownloadComplete(nsIDownloader *downloader,
00680                                  nsIRequest    *request,
00681                                  nsISupports   *context,
00682                                  nsresult       status,
00683                                  nsIFile       *file)
00684 {
00685     nsresult rv;
00686 
00687     nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
00688     if (channel) {
00689         PRUint32 loadFlags;
00690         channel->GetLoadFlags(&loadFlags);
00691         if (loadFlags & LOAD_REPLACE) {
00692             mLoadFlags |= LOAD_REPLACE;
00693 
00694             if (!mOriginalURI) {
00695                 SetOriginalURI(mJarURI);
00696             }
00697 
00698             nsCOMPtr<nsIURI> innerURI;
00699             rv = channel->GetURI(getter_AddRefs(innerURI));
00700             if (NS_SUCCEEDED(rv)) {
00701                 nsCOMPtr<nsIJARURI> newURI;
00702                 nsCOMPtr<nsIJARURI_MOZILLA_1_8_BRANCH> jarURIBranch =
00703                     do_QueryInterface(mJarURI);
00704                 rv = jarURIBranch->CloneWithJARFile(innerURI,
00705                                                     getter_AddRefs(newURI));
00706                 if (NS_SUCCEEDED(rv)) {
00707                     mJarURI = newURI;
00708                 }
00709             }
00710             if (NS_SUCCEEDED(status)) {
00711                 status = rv;
00712             }
00713         }
00714     }
00715 
00716     if (NS_SUCCEEDED(status) && channel) {
00717         // Grab the security info from our base channel
00718         channel->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
00719 
00720         nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
00721         if (httpChannel) {
00722             // We only want to run scripts if the server really intended to
00723             // send us a JAR file.  Check the server-supplied content type for
00724             // a JAR type.
00725             nsCAutoString header;
00726             httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Content-Type"),
00727                                            header);
00728 
00729             nsCAutoString contentType;
00730             nsCAutoString charset;
00731             NS_ParseContentType(header, contentType, charset);
00732 
00733             mIsUnsafe = !contentType.EqualsLiteral("application/java-archive") &&
00734                         !contentType.EqualsLiteral("application/x-jar");
00735         } else {
00736             nsCOMPtr<nsIJARChannel_MOZILLA_1_8_BRANCH> innerJARChannel(do_QueryInterface(channel));
00737             if (innerJARChannel) {
00738                 PRBool unsafe;
00739                 innerJARChannel->GetIsUnsafe(&unsafe);
00740                 mIsUnsafe = unsafe;
00741             }
00742         }
00743     }
00744 
00745     if (NS_SUCCEEDED(status) && mIsUnsafe) {
00746         PRBool allowUnpack = PR_FALSE;
00747 
00748         nsCOMPtr<nsIPrefBranch> prefs =
00749             do_GetService(NS_PREFSERVICE_CONTRACTID);
00750         if (prefs) {
00751             prefs->GetBoolPref("network.jar.open-unsafe-types", &allowUnpack);
00752         }
00753 
00754         if (!allowUnpack) {
00755             status = NS_ERROR_UNSAFE_CONTENT_TYPE;
00756         }
00757     }
00758 
00759     if (NS_SUCCEEDED(status)) {
00760         mJarFile = file;
00761     
00762         rv = CreateJarInput(nsnull);
00763         if (NS_SUCCEEDED(rv)) {
00764             // create input stream pump
00765             rv = NS_NewInputStreamPump(getter_AddRefs(mPump), mJarInput);
00766             if (NS_SUCCEEDED(rv))
00767                 rv = mPump->AsyncRead(this, nsnull);
00768         }
00769         status = rv;
00770     }
00771 
00772     if (NS_FAILED(status)) {
00773         mStatus = status;
00774         OnStartRequest(nsnull, nsnull);
00775         OnStopRequest(nsnull, nsnull, status);
00776     }
00777 
00778     return NS_OK;
00779 }
00780 
00781 //-----------------------------------------------------------------------------
00782 // nsIStreamListener
00783 //-----------------------------------------------------------------------------
00784 
00785 NS_IMETHODIMP
00786 nsJARChannel::OnStartRequest(nsIRequest *req, nsISupports *ctx)
00787 {
00788     LOG(("nsJARChannel::OnStartRequest [this=%x %s]\n", this, mSpec.get()));
00789 
00790     return mListener->OnStartRequest(this, mListenerContext);
00791 }
00792 
00793 NS_IMETHODIMP
00794 nsJARChannel::OnStopRequest(nsIRequest *req, nsISupports *ctx, nsresult status)
00795 {
00796     LOG(("nsJARChannel::OnStopRequest [this=%x %s status=%x]\n",
00797         this, mSpec.get(), status));
00798 
00799     if (NS_SUCCEEDED(mStatus))
00800         mStatus = status;
00801 
00802     if (mListener) {
00803         mListener->OnStopRequest(this, mListenerContext, status);
00804         mListener = 0;
00805         mListenerContext = 0;
00806     }
00807 
00808     if (mLoadGroup)
00809         mLoadGroup->RemoveRequest(this, nsnull, status);
00810 
00811     mPump = 0;
00812     NS_IF_RELEASE(mJarInput);
00813     mIsPending = PR_FALSE;
00814     mDownloader = 0; // this may delete the underlying jar file
00815 
00816     // Drop notification callbacks to prevent cycles.
00817     mCallbacks = 0;
00818     mProgressSink = 0;
00819 
00820     return NS_OK;
00821 }
00822 
00823 NS_IMETHODIMP
00824 nsJARChannel::OnDataAvailable(nsIRequest *req, nsISupports *ctx,
00825                                nsIInputStream *stream,
00826                                PRUint32 offset, PRUint32 count)
00827 {
00828 #if defined(PR_LOGGING)
00829     LOG(("nsJARChannel::OnDataAvailable [this=%x %s]\n", this, mSpec.get()));
00830 #endif
00831 
00832     nsresult rv;
00833 
00834     rv = mListener->OnDataAvailable(this, mListenerContext, stream, offset, count);
00835 
00836     // simply report progress here instead of hooking ourselves up as a
00837     // nsITransportEventSink implementation.
00838     // XXX do the 64-bit stuff for real
00839     if (mProgressSink && NS_SUCCEEDED(rv) && !(mLoadFlags & LOAD_BACKGROUND))
00840         mProgressSink->OnProgress(this, nsnull, nsUint64(offset + count),
00841                                   nsUint64(mContentLength));
00842 
00843     return rv; // let the pump cancel on failure
00844 }