modules/libjar/nsJARChannel.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     2 /* vim: set sw=4 ts=8 et tw=80 : */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "nsJAR.h"
     8 #include "nsJARChannel.h"
     9 #include "nsJARProtocolHandler.h"
    10 #include "nsMimeTypes.h"
    11 #include "nsNetUtil.h"
    12 #include "nsEscape.h"
    13 #include "nsIPrefService.h"
    14 #include "nsIPrefBranch.h"
    15 #include "nsIViewSourceChannel.h"
    16 #include "nsChannelProperties.h"
    18 #include "nsIScriptSecurityManager.h"
    19 #include "nsIPrincipal.h"
    20 #include "nsIFileURL.h"
    22 #include "mozilla/Preferences.h"
    23 #include "mozilla/net/RemoteOpenFileChild.h"
    24 #include "nsITabChild.h"
    26 using namespace mozilla;
    27 using namespace mozilla::net;
    29 static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID);
    31 // the entry for a directory will either be empty (in the case of the
    32 // top-level directory) or will end with a slash
    33 #define ENTRY_IS_DIRECTORY(_entry) \
    34   ((_entry).IsEmpty() || '/' == (_entry).Last())
    36 //-----------------------------------------------------------------------------
    38 // Ignore any LOG macro that we inherit from arbitrary headers. (We define our
    39 // own LOG macro below.)
    40 #ifdef LOG
    41 #undef LOG
    42 #endif
    44 #if defined(PR_LOGGING)
    45 //
    46 // set NSPR_LOG_MODULES=nsJarProtocol:5
    47 //
    48 static PRLogModuleInfo *gJarProtocolLog = nullptr;
    49 #endif
    51 // If you ever want to define PR_FORCE_LOGGING in this file, see bug 545995
    52 #define LOG(args)     PR_LOG(gJarProtocolLog, PR_LOG_DEBUG, args)
    53 #define LOG_ENABLED() PR_LOG_TEST(gJarProtocolLog, 4)
    55 //-----------------------------------------------------------------------------
    56 // nsJARInputThunk
    57 //
    58 // this class allows us to do some extra work on the stream transport thread.
    59 //-----------------------------------------------------------------------------
    61 class nsJARInputThunk : public nsIInputStream
    62 {
    63 public:
    64     NS_DECL_THREADSAFE_ISUPPORTS
    65     NS_DECL_NSIINPUTSTREAM
    67     nsJARInputThunk(nsIZipReader *zipReader,
    68                     nsIURI* fullJarURI,
    69                     const nsACString &jarEntry,
    70                     bool usingJarCache)
    71         : mUsingJarCache(usingJarCache)
    72         , mJarReader(zipReader)
    73         , mJarEntry(jarEntry)
    74         , mContentLength(-1)
    75     {
    76         if (fullJarURI) {
    77 #ifdef DEBUG
    78             nsresult rv =
    79 #endif
    80                 fullJarURI->GetAsciiSpec(mJarDirSpec);
    81             NS_ASSERTION(NS_SUCCEEDED(rv), "this shouldn't fail");
    82         }
    83     }
    85     virtual ~nsJARInputThunk()
    86     {
    87         Close();
    88     }
    90     int64_t GetContentLength()
    91     {
    92         return mContentLength;
    93     }
    95     nsresult Init();
    97 private:
    99     bool                        mUsingJarCache;
   100     nsCOMPtr<nsIZipReader>      mJarReader;
   101     nsCString                   mJarDirSpec;
   102     nsCOMPtr<nsIInputStream>    mJarStream;
   103     nsCString                   mJarEntry;
   104     int64_t                     mContentLength;
   105 };
   107 NS_IMPL_ISUPPORTS(nsJARInputThunk, nsIInputStream)
   109 nsresult
   110 nsJARInputThunk::Init()
   111 {
   112     nsresult rv;
   113     if (ENTRY_IS_DIRECTORY(mJarEntry)) {
   114         // A directory stream also needs the Spec of the FullJarURI
   115         // because is included in the stream data itself.
   117         NS_ENSURE_STATE(!mJarDirSpec.IsEmpty());
   119         rv = mJarReader->GetInputStreamWithSpec(mJarDirSpec,
   120                                                 mJarEntry,
   121                                                 getter_AddRefs(mJarStream));
   122     }
   123     else {
   124         rv = mJarReader->GetInputStream(mJarEntry,
   125                                         getter_AddRefs(mJarStream));
   126     }
   127     if (NS_FAILED(rv)) {
   128         // convert to the proper result if the entry wasn't found
   129         // so that error pages work
   130         if (rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST)
   131             rv = NS_ERROR_FILE_NOT_FOUND;
   132         return rv;
   133     }
   135     // ask the JarStream for the content length
   136     uint64_t avail;
   137     rv = mJarStream->Available((uint64_t *) &avail);
   138     if (NS_FAILED(rv)) return rv;
   140     mContentLength = avail < INT64_MAX ? (int64_t) avail : -1;
   142     return NS_OK;
   143 }
   145 NS_IMETHODIMP
   146 nsJARInputThunk::Close()
   147 {
   148     nsresult rv = NS_OK;
   150     if (mJarStream)
   151         rv = mJarStream->Close();
   153     if (!mUsingJarCache && mJarReader)
   154         mJarReader->Close();
   156     mJarReader = nullptr;
   158     return rv;
   159 }
   161 NS_IMETHODIMP
   162 nsJARInputThunk::Available(uint64_t *avail)
   163 {
   164     return mJarStream->Available(avail);
   165 }
   167 NS_IMETHODIMP
   168 nsJARInputThunk::Read(char *buf, uint32_t count, uint32_t *countRead)
   169 {
   170     return mJarStream->Read(buf, count, countRead);
   171 }
   173 NS_IMETHODIMP
   174 nsJARInputThunk::ReadSegments(nsWriteSegmentFun writer, void *closure,
   175                               uint32_t count, uint32_t *countRead)
   176 {
   177     // stream transport does only calls Read()
   178     return NS_ERROR_NOT_IMPLEMENTED;
   179 }
   181 NS_IMETHODIMP
   182 nsJARInputThunk::IsNonBlocking(bool *nonBlocking)
   183 {
   184     *nonBlocking = false;
   185     return NS_OK;
   186 }
   188 //-----------------------------------------------------------------------------
   189 // nsJARChannel
   190 //-----------------------------------------------------------------------------
   193 nsJARChannel::nsJARChannel()
   194     : mOpened(false)
   195     , mAppURI(nullptr)
   196     , mContentLength(-1)
   197     , mLoadFlags(LOAD_NORMAL)
   198     , mStatus(NS_OK)
   199     , mIsPending(false)
   200     , mIsUnsafe(true)
   201     , mOpeningRemote(false)
   202 {
   203 #if defined(PR_LOGGING)
   204     if (!gJarProtocolLog)
   205         gJarProtocolLog = PR_NewLogModule("nsJarProtocol");
   206 #endif
   208     // hold an owning reference to the jar handler
   209     NS_ADDREF(gJarHandler);
   210 }
   212 nsJARChannel::~nsJARChannel()
   213 {
   214     // release owning reference to the jar handler
   215     nsJARProtocolHandler *handler = gJarHandler;
   216     NS_RELEASE(handler); // nullptr parameter
   217 }
   219 NS_IMPL_ISUPPORTS_INHERITED(nsJARChannel,
   220                             nsHashPropertyBag,
   221                             nsIRequest,
   222                             nsIChannel,
   223                             nsIStreamListener,
   224                             nsIRequestObserver,
   225                             nsIDownloadObserver,
   226                             nsIRemoteOpenFileListener,
   227                             nsIJARChannel)
   229 nsresult 
   230 nsJARChannel::Init(nsIURI *uri)
   231 {
   232     nsresult rv;
   233     mJarURI = do_QueryInterface(uri, &rv);
   234     if (NS_FAILED(rv))
   235         return rv;
   237     mOriginalURI = mJarURI;
   239     // Prevent loading jar:javascript URIs (see bug 290982).
   240     nsCOMPtr<nsIURI> innerURI;
   241     rv = mJarURI->GetJARFile(getter_AddRefs(innerURI));
   242     if (NS_FAILED(rv))
   243         return rv;
   244     bool isJS;
   245     rv = innerURI->SchemeIs("javascript", &isJS);
   246     if (NS_FAILED(rv))
   247         return rv;
   248     if (isJS) {
   249         NS_WARNING("blocking jar:javascript:");
   250         return NS_ERROR_INVALID_ARG;
   251     }
   253 #if defined(PR_LOGGING)
   254     mJarURI->GetSpec(mSpec);
   255 #endif
   256     return rv;
   257 }
   259 nsresult
   260 nsJARChannel::CreateJarInput(nsIZipReaderCache *jarCache, nsJARInputThunk **resultInput)
   261 {
   262     MOZ_ASSERT(resultInput);
   264     // important to pass a clone of the file since the nsIFile impl is not
   265     // necessarily MT-safe
   266     nsCOMPtr<nsIFile> clonedFile;
   267     nsresult rv = mJarFile->Clone(getter_AddRefs(clonedFile));
   268     if (NS_FAILED(rv))
   269         return rv;
   271     nsCOMPtr<nsIZipReader> reader;
   272     if (jarCache) {
   273         if (mInnerJarEntry.IsEmpty())
   274             rv = jarCache->GetZip(clonedFile, getter_AddRefs(reader));
   275         else
   276             rv = jarCache->GetInnerZip(clonedFile, mInnerJarEntry,
   277                                        getter_AddRefs(reader));
   278     } else {
   279         // create an uncached jar reader
   280         nsCOMPtr<nsIZipReader> outerReader = do_CreateInstance(kZipReaderCID, &rv);
   281         if (NS_FAILED(rv))
   282             return rv;
   284         rv = outerReader->Open(clonedFile);
   285         if (NS_FAILED(rv))
   286             return rv;
   288         if (mInnerJarEntry.IsEmpty())
   289             reader = outerReader;
   290         else {
   291             reader = do_CreateInstance(kZipReaderCID, &rv);
   292             if (NS_FAILED(rv))
   293                 return rv;
   295             rv = reader->OpenInner(outerReader, mInnerJarEntry);
   296         }
   297     }
   298     if (NS_FAILED(rv))
   299         return rv;
   301     nsRefPtr<nsJARInputThunk> input = new nsJARInputThunk(reader,
   302                                                           mJarURI,
   303                                                           mJarEntry,
   304                                                           jarCache != nullptr
   305                                                           );
   306     rv = input->Init();
   307     if (NS_FAILED(rv))
   308         return rv;
   310     // Make GetContentLength meaningful
   311     mContentLength = input->GetContentLength();
   313     input.forget(resultInput);
   314     return NS_OK;
   315 }
   317 nsresult
   318 nsJARChannel::LookupFile()
   319 {
   320     LOG(("nsJARChannel::LookupFile [this=%x %s]\n", this, mSpec.get()));
   322     nsresult rv;
   323     nsCOMPtr<nsIURI> uri;
   325     rv = mJarURI->GetJARFile(getter_AddRefs(mJarBaseURI));
   326     if (NS_FAILED(rv))
   327         return rv;
   329     rv = mJarURI->GetJAREntry(mJarEntry);
   330     if (NS_FAILED(rv))
   331         return rv;
   333     // The name of the JAR entry must not contain URL-escaped characters:
   334     // we're moving from URL domain to a filename domain here. nsStandardURL
   335     // does basic escaping by default, which breaks reading zipped files which
   336     // have e.g. spaces in their filenames.
   337     NS_UnescapeURL(mJarEntry);
   339     // try to get a nsIFile directly from the url, which will often succeed.
   340     {
   341         nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mJarBaseURI);
   342         if (fileURL)
   343             fileURL->GetFile(getter_AddRefs(mJarFile));
   344     }
   345     // if we're in child process and have special "remoteopenfile:://" scheme,
   346     // create special nsIFile that gets file handle from parent when opened.
   347     if (!mJarFile && !gJarHandler->IsMainProcess()) {
   348         nsAutoCString scheme;
   349         rv = mJarBaseURI->GetScheme(scheme);
   350         if (NS_SUCCEEDED(rv) && scheme.EqualsLiteral("remoteopenfile")) {
   351             nsRefPtr<RemoteOpenFileChild> remoteFile = new RemoteOpenFileChild();
   352             rv = remoteFile->Init(mJarBaseURI, mAppURI);
   353             NS_ENSURE_SUCCESS(rv, rv);
   354             mJarFile = remoteFile;
   356             nsIZipReaderCache *jarCache = gJarHandler->JarCache();
   357             if (jarCache) {
   358                 bool cached = false;
   359                 rv = jarCache->IsCached(mJarFile, &cached);
   360                 if (NS_SUCCEEDED(rv) && cached) {
   361                     // zipcache already has file mmapped: don't open on parent,
   362                     // just return and proceed to cache hit in CreateJarInput()
   363                     return NS_OK;
   364                 }
   365             }
   367             mOpeningRemote = true;
   369             if (gJarHandler->RemoteOpenFileInProgress(remoteFile, this)) {
   370                 // JarHandler will trigger OnRemoteFileOpen() after the first
   371                 // request for this file completes and we'll get a JAR cache
   372                 // hit.
   373                 return NS_OK;
   374             }
   376             // Open file on parent: OnRemoteFileOpenComplete called when done
   377             nsCOMPtr<nsITabChild> tabChild;
   378             NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, tabChild);
   379             rv = remoteFile->AsyncRemoteFileOpen(PR_RDONLY, this, tabChild.get());
   380             NS_ENSURE_SUCCESS(rv, rv);
   381         }
   382     }
   383     // try to handle a nested jar
   384     if (!mJarFile) {
   385         nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(mJarBaseURI);
   386         if (jarURI) {
   387             nsCOMPtr<nsIFileURL> fileURL;
   388             nsCOMPtr<nsIURI> innerJarURI;
   389             rv = jarURI->GetJARFile(getter_AddRefs(innerJarURI));
   390             if (NS_SUCCEEDED(rv))
   391                 fileURL = do_QueryInterface(innerJarURI);
   392             if (fileURL) {
   393                 fileURL->GetFile(getter_AddRefs(mJarFile));
   394                 jarURI->GetJAREntry(mInnerJarEntry);
   395             }
   396         }
   397     }
   399     return rv;
   400 }
   402 nsresult
   403 nsJARChannel::OpenLocalFile()
   404 {
   405     MOZ_ASSERT(mIsPending);
   407     // Local files are always considered safe.
   408     mIsUnsafe = false;
   410     nsRefPtr<nsJARInputThunk> input;
   411     nsresult rv = CreateJarInput(gJarHandler->JarCache(),
   412                                  getter_AddRefs(input));
   413     if (NS_SUCCEEDED(rv)) {
   414         // Create input stream pump and call AsyncRead as a block.
   415         rv = NS_NewInputStreamPump(getter_AddRefs(mPump), input);
   416         if (NS_SUCCEEDED(rv))
   417             rv = mPump->AsyncRead(this, nullptr);
   418     }
   420     return rv;
   421 }
   423 void
   424 nsJARChannel::NotifyError(nsresult aError)
   425 {
   426     MOZ_ASSERT(NS_FAILED(aError));
   428     mStatus = aError;
   430     OnStartRequest(nullptr, nullptr);
   431     OnStopRequest(nullptr, nullptr, aError);
   432 }
   434 //-----------------------------------------------------------------------------
   435 // nsIRequest
   436 //-----------------------------------------------------------------------------
   438 NS_IMETHODIMP
   439 nsJARChannel::GetName(nsACString &result)
   440 {
   441     return mJarURI->GetSpec(result);
   442 }
   444 NS_IMETHODIMP
   445 nsJARChannel::IsPending(bool *result)
   446 {
   447     *result = mIsPending;
   448     return NS_OK;
   449 }
   451 NS_IMETHODIMP
   452 nsJARChannel::GetStatus(nsresult *status)
   453 {
   454     if (mPump && NS_SUCCEEDED(mStatus))
   455         mPump->GetStatus(status);
   456     else
   457         *status = mStatus;
   458     return NS_OK;
   459 }
   461 NS_IMETHODIMP
   462 nsJARChannel::Cancel(nsresult status)
   463 {
   464     mStatus = status;
   465     if (mPump)
   466         return mPump->Cancel(status);
   468     NS_ASSERTION(!mIsPending, "need to implement cancel when downloading");
   469     return NS_OK;
   470 }
   472 NS_IMETHODIMP
   473 nsJARChannel::Suspend()
   474 {
   475     if (mPump)
   476         return mPump->Suspend();
   478     NS_ASSERTION(!mIsPending, "need to implement suspend when downloading");
   479     return NS_OK;
   480 }
   482 NS_IMETHODIMP
   483 nsJARChannel::Resume()
   484 {
   485     if (mPump)
   486         return mPump->Resume();
   488     NS_ASSERTION(!mIsPending, "need to implement resume when downloading");
   489     return NS_OK;
   490 }
   492 NS_IMETHODIMP
   493 nsJARChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
   494 {
   495     *aLoadFlags = mLoadFlags;
   496     return NS_OK;
   497 }
   499 NS_IMETHODIMP
   500 nsJARChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
   501 {
   502     mLoadFlags = aLoadFlags;
   503     return NS_OK;
   504 }
   506 NS_IMETHODIMP
   507 nsJARChannel::GetLoadGroup(nsILoadGroup **aLoadGroup)
   508 {
   509     NS_IF_ADDREF(*aLoadGroup = mLoadGroup);
   510     return NS_OK;
   511 }
   513 NS_IMETHODIMP
   514 nsJARChannel::SetLoadGroup(nsILoadGroup *aLoadGroup)
   515 {
   516     mLoadGroup = aLoadGroup;
   517     return NS_OK;
   518 }
   520 //-----------------------------------------------------------------------------
   521 // nsIChannel
   522 //-----------------------------------------------------------------------------
   524 NS_IMETHODIMP
   525 nsJARChannel::GetOriginalURI(nsIURI **aURI)
   526 {
   527     *aURI = mOriginalURI;
   528     NS_ADDREF(*aURI);
   529     return NS_OK;
   530 }
   532 NS_IMETHODIMP
   533 nsJARChannel::SetOriginalURI(nsIURI *aURI)
   534 {
   535     NS_ENSURE_ARG_POINTER(aURI);
   536     mOriginalURI = aURI;
   537     return NS_OK;
   538 }
   540 NS_IMETHODIMP
   541 nsJARChannel::GetURI(nsIURI **aURI)
   542 {
   543     if (mAppURI) {
   544         NS_IF_ADDREF(*aURI = mAppURI);
   545     } else {
   546         NS_IF_ADDREF(*aURI = mJarURI);
   547     }
   549     return NS_OK;
   550 }
   552 NS_IMETHODIMP
   553 nsJARChannel::GetOwner(nsISupports **aOwner)
   554 {
   555     // JAR signatures are not processed to avoid main-thread network I/O (bug 726125)
   556     *aOwner = mOwner;
   557     NS_IF_ADDREF(*aOwner);
   558     return NS_OK;
   559 }
   561 NS_IMETHODIMP
   562 nsJARChannel::SetOwner(nsISupports *aOwner)
   563 {
   564     mOwner = aOwner;
   565     return NS_OK;
   566 }
   568 NS_IMETHODIMP
   569 nsJARChannel::GetNotificationCallbacks(nsIInterfaceRequestor **aCallbacks)
   570 {
   571     NS_IF_ADDREF(*aCallbacks = mCallbacks);
   572     return NS_OK;
   573 }
   575 NS_IMETHODIMP
   576 nsJARChannel::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks)
   577 {
   578     mCallbacks = aCallbacks;
   579     return NS_OK;
   580 }
   582 NS_IMETHODIMP 
   583 nsJARChannel::GetSecurityInfo(nsISupports **aSecurityInfo)
   584 {
   585     NS_PRECONDITION(aSecurityInfo, "Null out param");
   586     NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo);
   587     return NS_OK;
   588 }
   590 NS_IMETHODIMP
   591 nsJARChannel::GetContentType(nsACString &result)
   592 {
   593     // If the Jar file has not been open yet,
   594     // We return application/x-unknown-content-type
   595     if (!mOpened) {
   596       result.Assign(UNKNOWN_CONTENT_TYPE);
   597       return NS_OK;
   598     }
   600     if (mContentType.IsEmpty()) {
   602         //
   603         // generate content type and set it
   604         //
   605         const char *ext = nullptr, *fileName = mJarEntry.get();
   606         int32_t len = mJarEntry.Length();
   608         // check if we're displaying a directory
   609         // mJarEntry will be empty if we're trying to display
   610         // the topmost directory in a zip, e.g. jar:foo.zip!/
   611         if (ENTRY_IS_DIRECTORY(mJarEntry)) {
   612             mContentType.AssignLiteral(APPLICATION_HTTP_INDEX_FORMAT);
   613         }
   614         else {
   615             // not a directory, take a guess by its extension
   616             for (int32_t i = len-1; i >= 0; i--) {
   617                 if (fileName[i] == '.') {
   618                     ext = &fileName[i + 1];
   619                     break;
   620                 }
   621             }
   622             if (ext) {
   623                 nsIMIMEService *mimeServ = gJarHandler->MimeService();
   624                 if (mimeServ)
   625                     mimeServ->GetTypeFromExtension(nsDependentCString(ext), mContentType);
   626             }
   627             if (mContentType.IsEmpty())
   628                 mContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE);
   629         }
   630     }
   631     result = mContentType;
   632     return NS_OK;
   633 }
   635 NS_IMETHODIMP
   636 nsJARChannel::SetContentType(const nsACString &aContentType)
   637 {
   638     // If someone gives us a type hint we should just use that type instead of
   639     // doing our guessing.  So we don't care when this is being called.
   641     // mContentCharset is unchanged if not parsed
   642     NS_ParseContentType(aContentType, mContentType, mContentCharset);
   643     return NS_OK;
   644 }
   646 NS_IMETHODIMP
   647 nsJARChannel::GetContentCharset(nsACString &aContentCharset)
   648 {
   649     // If someone gives us a charset hint we should just use that charset.
   650     // So we don't care when this is being called.
   651     aContentCharset = mContentCharset;
   652     return NS_OK;
   653 }
   655 NS_IMETHODIMP
   656 nsJARChannel::SetContentCharset(const nsACString &aContentCharset)
   657 {
   658     mContentCharset = aContentCharset;
   659     return NS_OK;
   660 }
   662 NS_IMETHODIMP
   663 nsJARChannel::GetContentDisposition(uint32_t *aContentDisposition)
   664 {
   665     if (mContentDispositionHeader.IsEmpty())
   666         return NS_ERROR_NOT_AVAILABLE;
   668     *aContentDisposition = mContentDisposition;
   669     return NS_OK;
   670 }
   672 NS_IMETHODIMP
   673 nsJARChannel::SetContentDisposition(uint32_t aContentDisposition)
   674 {
   675     return NS_ERROR_NOT_AVAILABLE;
   676 }
   678 NS_IMETHODIMP
   679 nsJARChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename)
   680 {
   681     return NS_ERROR_NOT_AVAILABLE;
   682 }
   684 NS_IMETHODIMP
   685 nsJARChannel::SetContentDispositionFilename(const nsAString &aContentDispositionFilename)
   686 {
   687     return NS_ERROR_NOT_AVAILABLE;
   688 }
   690 NS_IMETHODIMP
   691 nsJARChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader)
   692 {
   693     if (mContentDispositionHeader.IsEmpty())
   694         return NS_ERROR_NOT_AVAILABLE;
   696     aContentDispositionHeader = mContentDispositionHeader;
   697     return NS_OK;
   698 }
   700 NS_IMETHODIMP
   701 nsJARChannel::GetContentLength(int64_t *result)
   702 {
   703     *result = mContentLength;
   704     return NS_OK;
   705 }
   707 NS_IMETHODIMP
   708 nsJARChannel::SetContentLength(int64_t aContentLength)
   709 {
   710     // XXX does this really make any sense at all?
   711     mContentLength = aContentLength;
   712     return NS_OK;
   713 }
   715 NS_IMETHODIMP
   716 nsJARChannel::Open(nsIInputStream **stream)
   717 {
   718     LOG(("nsJARChannel::Open [this=%x]\n", this));
   720     NS_ENSURE_TRUE(!mOpened, NS_ERROR_IN_PROGRESS);
   721     NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
   723     mJarFile = nullptr;
   724     mIsUnsafe = true;
   726     nsresult rv = LookupFile();
   727     if (NS_FAILED(rv))
   728         return rv;
   730     // If mJarInput was not set by LookupFile, the JAR is a remote jar.
   731     if (!mJarFile) {
   732         NS_NOTREACHED("need sync downloader");
   733         return NS_ERROR_NOT_IMPLEMENTED;
   734     }
   736     nsRefPtr<nsJARInputThunk> input;
   737     rv = CreateJarInput(gJarHandler->JarCache(), getter_AddRefs(input));
   738     if (NS_FAILED(rv))
   739         return rv;
   741     input.forget(stream);
   742     mOpened = true;
   743     // local files are always considered safe
   744     mIsUnsafe = false;
   745     return NS_OK;
   746 }
   748 NS_IMETHODIMP
   749 nsJARChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctx)
   750 {
   751     LOG(("nsJARChannel::AsyncOpen [this=%x]\n", this));
   753     NS_ENSURE_ARG_POINTER(listener);
   754     NS_ENSURE_TRUE(!mOpened, NS_ERROR_IN_PROGRESS);
   755     NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
   757     mJarFile = nullptr;
   758     mIsUnsafe = true;
   760     // Initialize mProgressSink
   761     NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, mProgressSink);
   763     nsresult rv = LookupFile();
   764     if (NS_FAILED(rv))
   765         return rv;
   767     // These variables must only be set if we're going to trigger an
   768     // OnStartRequest, either from AsyncRead or OnDownloadComplete.
   769     // 
   770     // That means: Do not add early return statements beyond this point!
   771     mListener = listener;
   772     mListenerContext = ctx;
   773     mIsPending = true;
   775     if (!mJarFile) {
   776         // Not a local file...
   777         // kick off an async download of the base URI...
   778         rv = NS_NewDownloader(getter_AddRefs(mDownloader), this);
   779         if (NS_SUCCEEDED(rv))
   780             rv = NS_OpenURI(mDownloader, nullptr, mJarBaseURI, nullptr,
   781                             mLoadGroup, mCallbacks,
   782                             mLoadFlags & ~(LOAD_DOCUMENT_URI | LOAD_CALL_CONTENT_SNIFFERS));
   783     } else if (mOpeningRemote) {
   784         // nothing to do: already asked parent to open file.
   785     } else {
   786         rv = OpenLocalFile();
   787     }
   789     if (NS_FAILED(rv)) {
   790         mIsPending = false;
   791         mListenerContext = nullptr;
   792         mListener = nullptr;
   793         return rv;
   794     }
   797     if (mLoadGroup)
   798         mLoadGroup->AddRequest(this, nullptr);
   800     mOpened = true;
   801     return NS_OK;
   802 }
   804 //-----------------------------------------------------------------------------
   805 // nsIJARChannel
   806 //-----------------------------------------------------------------------------
   807 NS_IMETHODIMP
   808 nsJARChannel::GetIsUnsafe(bool *isUnsafe)
   809 {
   810     *isUnsafe = mIsUnsafe;
   811     return NS_OK;
   812 }
   814 NS_IMETHODIMP
   815 nsJARChannel::SetAppURI(nsIURI *aURI) {
   816     NS_ENSURE_ARG_POINTER(aURI);
   818     nsAutoCString scheme;
   819     aURI->GetScheme(scheme);
   820     if (!scheme.EqualsLiteral("app")) {
   821         return NS_ERROR_INVALID_ARG;
   822     }
   824     mAppURI = aURI;
   825     return NS_OK;
   826 }
   828 //-----------------------------------------------------------------------------
   829 // nsIDownloadObserver
   830 //-----------------------------------------------------------------------------
   832 NS_IMETHODIMP
   833 nsJARChannel::OnDownloadComplete(nsIDownloader *downloader,
   834                                  nsIRequest    *request,
   835                                  nsISupports   *context,
   836                                  nsresult       status,
   837                                  nsIFile       *file)
   838 {
   839     nsresult rv;
   841     nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
   842     if (channel) {
   843         uint32_t loadFlags;
   844         channel->GetLoadFlags(&loadFlags);
   845         if (loadFlags & LOAD_REPLACE) {
   846             mLoadFlags |= LOAD_REPLACE;
   848             if (!mOriginalURI) {
   849                 SetOriginalURI(mJarURI);
   850             }
   852             nsCOMPtr<nsIURI> innerURI;
   853             rv = channel->GetURI(getter_AddRefs(innerURI));
   854             if (NS_SUCCEEDED(rv)) {
   855                 nsCOMPtr<nsIJARURI> newURI;
   856                 rv = mJarURI->CloneWithJARFile(innerURI,
   857                                                getter_AddRefs(newURI));
   858                 if (NS_SUCCEEDED(rv)) {
   859                     mJarURI = newURI;
   860                 }
   861             }
   862             if (NS_SUCCEEDED(status)) {
   863                 status = rv;
   864             }
   865         }
   866     }
   868     if (NS_SUCCEEDED(status) && channel) {
   869         // Grab the security info from our base channel
   870         channel->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
   872         nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
   873         if (httpChannel) {
   874             // We only want to run scripts if the server really intended to
   875             // send us a JAR file.  Check the server-supplied content type for
   876             // a JAR type.
   877             nsAutoCString header;
   878             httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Content-Type"),
   879                                            header);
   880             nsAutoCString contentType;
   881             nsAutoCString charset;
   882             NS_ParseContentType(header, contentType, charset);
   883             nsAutoCString channelContentType;
   884             channel->GetContentType(channelContentType);
   885             mIsUnsafe = !(contentType.Equals(channelContentType) &&
   886                           (contentType.EqualsLiteral("application/java-archive") ||
   887                            contentType.EqualsLiteral("application/x-jar")));
   888         } else {
   889             nsCOMPtr<nsIJARChannel> innerJARChannel(do_QueryInterface(channel));
   890             if (innerJARChannel) {
   891                 bool unsafe;
   892                 innerJARChannel->GetIsUnsafe(&unsafe);
   893                 mIsUnsafe = unsafe;
   894             }
   895         }
   897         channel->GetContentDispositionHeader(mContentDispositionHeader);
   898         mContentDisposition = NS_GetContentDispositionFromHeader(mContentDispositionHeader, this);
   899     }
   901     if (NS_SUCCEEDED(status) && mIsUnsafe &&
   902         !Preferences::GetBool("network.jar.open-unsafe-types", false)) {
   903         status = NS_ERROR_UNSAFE_CONTENT_TYPE;
   904     }
   906     if (NS_SUCCEEDED(status)) {
   907         // Refuse to unpack view-source: jars even if open-unsafe-types is set.
   908         nsCOMPtr<nsIViewSourceChannel> viewSource = do_QueryInterface(channel);
   909         if (viewSource) {
   910             status = NS_ERROR_UNSAFE_CONTENT_TYPE;
   911         }
   912     }
   914     if (NS_SUCCEEDED(status)) {
   915         mJarFile = file;
   917         nsRefPtr<nsJARInputThunk> input;
   918         rv = CreateJarInput(nullptr, getter_AddRefs(input));
   919         if (NS_SUCCEEDED(rv)) {
   920             // create input stream pump
   921             rv = NS_NewInputStreamPump(getter_AddRefs(mPump), input);
   922             if (NS_SUCCEEDED(rv))
   923                 rv = mPump->AsyncRead(this, nullptr);
   924         }
   925         status = rv;
   926     }
   928     if (NS_FAILED(status)) {
   929         NotifyError(status);
   930     }
   932     return NS_OK;
   933 }
   935 //-----------------------------------------------------------------------------
   936 // nsIRemoteOpenFileListener
   937 //-----------------------------------------------------------------------------
   938 nsresult
   939 nsJARChannel::OnRemoteFileOpenComplete(nsresult aOpenStatus)
   940 {
   941     nsresult rv = aOpenStatus;
   943     // NS_ERROR_ALREADY_OPENED here means we'll hit JAR cache in
   944     // OpenLocalFile().
   945     if (NS_SUCCEEDED(rv) || rv == NS_ERROR_ALREADY_OPENED) {
   946         rv = OpenLocalFile();
   947     }
   949     if (NS_FAILED(rv)) {
   950         NotifyError(rv);
   951     }
   953     return NS_OK;
   954 }
   956 //-----------------------------------------------------------------------------
   957 // nsIStreamListener
   958 //-----------------------------------------------------------------------------
   960 NS_IMETHODIMP
   961 nsJARChannel::OnStartRequest(nsIRequest *req, nsISupports *ctx)
   962 {
   963     LOG(("nsJARChannel::OnStartRequest [this=%x %s]\n", this, mSpec.get()));
   965     return mListener->OnStartRequest(this, mListenerContext);
   966 }
   968 NS_IMETHODIMP
   969 nsJARChannel::OnStopRequest(nsIRequest *req, nsISupports *ctx, nsresult status)
   970 {
   971     LOG(("nsJARChannel::OnStopRequest [this=%x %s status=%x]\n",
   972         this, mSpec.get(), status));
   974     if (NS_SUCCEEDED(mStatus))
   975         mStatus = status;
   977     if (mListener) {
   978         mListener->OnStopRequest(this, mListenerContext, status);
   979         mListener = 0;
   980         mListenerContext = 0;
   981     }
   983     if (mLoadGroup)
   984         mLoadGroup->RemoveRequest(this, nullptr, status);
   986     mPump = 0;
   987     mIsPending = false;
   988     mDownloader = 0; // this may delete the underlying jar file
   990     // Drop notification callbacks to prevent cycles.
   991     mCallbacks = 0;
   992     mProgressSink = 0;
   994     return NS_OK;
   995 }
   997 NS_IMETHODIMP
   998 nsJARChannel::OnDataAvailable(nsIRequest *req, nsISupports *ctx,
   999                                nsIInputStream *stream,
  1000                                uint64_t offset, uint32_t count)
  1002 #if defined(PR_LOGGING)
  1003     LOG(("nsJARChannel::OnDataAvailable [this=%x %s]\n", this, mSpec.get()));
  1004 #endif
  1006     nsresult rv;
  1008     rv = mListener->OnDataAvailable(this, mListenerContext, stream, offset, count);
  1010     // simply report progress here instead of hooking ourselves up as a
  1011     // nsITransportEventSink implementation.
  1012     // XXX do the 64-bit stuff for real
  1013     if (mProgressSink && NS_SUCCEEDED(rv) && !(mLoadFlags & LOAD_BACKGROUND))
  1014         mProgressSink->OnProgress(this, nullptr, offset + count,
  1015                                   uint64_t(mContentLength));
  1017     return rv; // let the pump cancel on failure

mercurial