LCOV - code coverage report
Current view: top level - modules/libjar - nsJAR.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 12 645 1.9 %
Date: 2017-07-14 16:53:18 Functions: 5 62 8.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 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/. */
       6             : #include <string.h>
       7             : #include "nsJARInputStream.h"
       8             : #include "nsJAR.h"
       9             : #include "nsIFile.h"
      10             : #include "nsIX509Cert.h"
      11             : #include "nsIConsoleService.h"
      12             : #include "nsICryptoHash.h"
      13             : #include "nsIDataSignatureVerifier.h"
      14             : #include "mozilla/Omnijar.h"
      15             : #include "mozilla/Unused.h"
      16             : 
      17             : #ifdef XP_UNIX
      18             :   #include <sys/stat.h>
      19             : #elif defined (XP_WIN)
      20             :   #include <io.h>
      21             : #endif
      22             : 
      23             : using namespace mozilla;
      24             : 
      25             : //----------------------------------------------
      26             : // nsJARManifestItem declaration
      27             : //----------------------------------------------
      28             : /*
      29             :  * nsJARManifestItem contains meta-information pertaining
      30             :  * to an individual JAR entry, taken from the
      31             :  * META-INF/MANIFEST.MF and META-INF/ *.SF files.
      32             :  * This is security-critical information, defined here so it is not
      33             :  * accessible from anywhere else.
      34             :  */
      35             : typedef enum
      36             : {
      37             :   JAR_INVALID       = 1,
      38             :   JAR_INTERNAL      = 2,
      39             :   JAR_EXTERNAL      = 3
      40             : } JARManifestItemType;
      41             : 
      42             : class nsJARManifestItem
      43             : {
      44             : public:
      45             :   JARManifestItemType mType;
      46             : 
      47             :   // True if the second step of verification (VerifyEntry)
      48             :   // has taken place:
      49             :   bool                entryVerified;
      50             : 
      51             :   // Not signed, valid, or failure code
      52             :   int16_t             status;
      53             : 
      54             :   // Internal storage of digests
      55             :   nsCString           calculatedSectionDigest;
      56             :   nsCString           storedEntryDigest;
      57             : 
      58             :   nsJARManifestItem();
      59             :   virtual ~nsJARManifestItem();
      60             : };
      61             : 
      62             : //-------------------------------------------------
      63             : // nsJARManifestItem constructors and destructor
      64             : //-------------------------------------------------
      65           0 : nsJARManifestItem::nsJARManifestItem(): mType(JAR_INTERNAL),
      66             :                                         entryVerified(false),
      67           0 :                                         status(JAR_NOT_SIGNED)
      68             : {
      69           0 : }
      70             : 
      71           0 : nsJARManifestItem::~nsJARManifestItem()
      72             : {
      73           0 : }
      74             : 
      75             : //----------------------------------------------
      76             : // nsJAR constructor/destructor
      77             : //----------------------------------------------
      78             : 
      79             : // The following initialization makes a guess of 10 entries per jarfile.
      80           0 : nsJAR::nsJAR(): mZip(new nsZipArchive()),
      81             :                 mManifestData(8),
      82             :                 mParsedManifest(false),
      83             :                 mGlobalStatus(JAR_MANIFEST_NOT_PARSED),
      84             :                 mReleaseTime(PR_INTERVAL_NO_TIMEOUT),
      85             :                 mCache(nullptr),
      86             :                 mLock("nsJAR::mLock"),
      87             :                 mMtime(0),
      88             :                 mTotalItemsInManifest(0),
      89             :                 mOpened(false),
      90           0 :                 mIsOmnijar(false)
      91             : {
      92           0 : }
      93             : 
      94           0 : nsJAR::~nsJAR()
      95             : {
      96           0 :   Close();
      97           0 : }
      98             : 
      99           0 : NS_IMPL_QUERY_INTERFACE(nsJAR, nsIZipReader)
     100           0 : NS_IMPL_ADDREF(nsJAR)
     101             : 
     102             : // Custom Release method works with nsZipReaderCache...
     103           0 : MozExternalRefCountType nsJAR::Release(void)
     104             : {
     105             :   nsrefcnt count;
     106           0 :   NS_PRECONDITION(0 != mRefCnt, "dup release");
     107           0 :   count = --mRefCnt;
     108           0 :   NS_LOG_RELEASE(this, count, "nsJAR");
     109           0 :   if (0 == count) {
     110           0 :     mRefCnt = 1; /* stabilize */
     111             :     /* enable this to find non-threadsafe destructors: */
     112             :     /* NS_ASSERT_OWNINGTHREAD(nsJAR); */
     113           0 :     delete this;
     114           0 :     return 0;
     115             :   }
     116           0 :   if (1 == count && mCache) {
     117             : #ifdef DEBUG
     118             :     nsresult rv =
     119             : #endif
     120           0 :       mCache->ReleaseZip(this);
     121           0 :     NS_ASSERTION(NS_SUCCEEDED(rv), "failed to release zip file");
     122             :   }
     123           0 :   return count;
     124             : }
     125             : 
     126             : //----------------------------------------------
     127             : // nsIZipReader implementation
     128             : //----------------------------------------------
     129             : 
     130             : NS_IMETHODIMP
     131           0 : nsJAR::Open(nsIFile* zipFile)
     132             : {
     133           0 :   NS_ENSURE_ARG_POINTER(zipFile);
     134           0 :   if (mOpened) return NS_ERROR_FAILURE; // Already open!
     135             : 
     136           0 :   mZipFile = zipFile;
     137           0 :   mOuterZipEntry.Truncate();
     138           0 :   mOpened = true;
     139             : 
     140             :   // The omnijar is special, it is opened early on and closed late
     141             :   // this avoids reopening it
     142           0 :   RefPtr<nsZipArchive> zip = mozilla::Omnijar::GetReader(zipFile);
     143           0 :   if (zip) {
     144           0 :     mZip = zip;
     145           0 :     mIsOmnijar = true;
     146           0 :     return NS_OK;
     147             :   }
     148           0 :   return mZip->OpenArchive(zipFile);
     149             : }
     150             : 
     151             : NS_IMETHODIMP
     152           0 : nsJAR::OpenInner(nsIZipReader *aZipReader, const nsACString &aZipEntry)
     153             : {
     154           0 :   NS_ENSURE_ARG_POINTER(aZipReader);
     155           0 :   if (mOpened) return NS_ERROR_FAILURE; // Already open!
     156             : 
     157             :   bool exist;
     158           0 :   nsresult rv = aZipReader->HasEntry(aZipEntry, &exist);
     159           0 :   NS_ENSURE_SUCCESS(rv, rv);
     160           0 :   NS_ENSURE_TRUE(exist, NS_ERROR_FILE_NOT_FOUND);
     161             : 
     162           0 :   rv = aZipReader->GetFile(getter_AddRefs(mZipFile));
     163           0 :   NS_ENSURE_SUCCESS(rv, rv);
     164             : 
     165           0 :   mOpened = true;
     166             : 
     167           0 :   mOuterZipEntry.Assign(aZipEntry);
     168             : 
     169           0 :   RefPtr<nsZipHandle> handle;
     170           0 :   rv = nsZipHandle::Init(static_cast<nsJAR*>(aZipReader)->mZip.get(), PromiseFlatCString(aZipEntry).get(),
     171           0 :                          getter_AddRefs(handle));
     172           0 :   if (NS_FAILED(rv))
     173           0 :     return rv;
     174             : 
     175           0 :   return mZip->OpenArchive(handle);
     176             : }
     177             : 
     178             : NS_IMETHODIMP
     179           0 : nsJAR::OpenMemory(void* aData, uint32_t aLength)
     180             : {
     181           0 :   NS_ENSURE_ARG_POINTER(aData);
     182           0 :   if (mOpened) return NS_ERROR_FAILURE; // Already open!
     183             : 
     184           0 :   mOpened = true;
     185             : 
     186           0 :   RefPtr<nsZipHandle> handle;
     187           0 :   nsresult rv = nsZipHandle::Init(static_cast<uint8_t*>(aData), aLength,
     188           0 :                                   getter_AddRefs(handle));
     189           0 :   if (NS_FAILED(rv))
     190           0 :     return rv;
     191             : 
     192           0 :   return mZip->OpenArchive(handle);
     193             : }
     194             : 
     195             : NS_IMETHODIMP
     196           0 : nsJAR::GetFile(nsIFile* *result)
     197             : {
     198           0 :   *result = mZipFile;
     199           0 :   NS_IF_ADDREF(*result);
     200           0 :   return NS_OK;
     201             : }
     202             : 
     203             : NS_IMETHODIMP
     204           0 : nsJAR::Close()
     205             : {
     206           0 :   if (!mOpened) {
     207           0 :     return NS_ERROR_FAILURE; // Never opened or already closed.
     208             :   }
     209             : 
     210           0 :   mOpened = false;
     211           0 :   mParsedManifest = false;
     212           0 :   mManifestData.Clear();
     213           0 :   mGlobalStatus = JAR_MANIFEST_NOT_PARSED;
     214           0 :   mTotalItemsInManifest = 0;
     215             : 
     216           0 :   if (mIsOmnijar) {
     217             :     // Reset state, but don't close the omnijar because we did not open it.
     218           0 :     mIsOmnijar = false;
     219           0 :     mZip = new nsZipArchive();
     220           0 :     return NS_OK;
     221             :   }
     222             : 
     223           0 :   return mZip->CloseArchive();
     224             : }
     225             : 
     226             : NS_IMETHODIMP
     227           0 : nsJAR::Test(const nsACString &aEntryName)
     228             : {
     229           0 :   return mZip->Test(aEntryName.IsEmpty()? nullptr : PromiseFlatCString(aEntryName).get());
     230             : }
     231             : 
     232             : NS_IMETHODIMP
     233           0 : nsJAR::Extract(const nsACString &aEntryName, nsIFile* outFile)
     234             : {
     235             :   // nsZipArchive and zlib are not thread safe
     236             :   // we need to use a lock to prevent bug #51267
     237           0 :   MutexAutoLock lock(mLock);
     238             : 
     239           0 :   nsZipItem *item = mZip->GetItem(PromiseFlatCString(aEntryName).get());
     240           0 :   NS_ENSURE_TRUE(item, NS_ERROR_FILE_TARGET_DOES_NOT_EXIST);
     241             : 
     242             :   // Remove existing file or directory so we set permissions correctly.
     243             :   // If it's a directory that already exists and contains files, throw
     244             :   // an exception and return.
     245             : 
     246           0 :   nsresult rv = outFile->Remove(false);
     247           0 :   if (rv == NS_ERROR_FILE_DIR_NOT_EMPTY ||
     248             :       rv == NS_ERROR_FAILURE)
     249           0 :     return rv;
     250             : 
     251           0 :   if (item->IsDirectory())
     252             :   {
     253           0 :     rv = outFile->Create(nsIFile::DIRECTORY_TYPE, item->Mode());
     254             :     //XXX Do this in nsZipArchive?  It would be nice to keep extraction
     255             :     //XXX code completely there, but that would require a way to get a
     256             :     //XXX PRDir from outFile.
     257             :   }
     258             :   else
     259             :   {
     260             :     PRFileDesc* fd;
     261           0 :     rv = outFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE, item->Mode(), &fd);
     262           0 :     if (NS_FAILED(rv)) return rv;
     263             : 
     264             :     // ExtractFile also closes the fd handle and resolves the symlink if needed
     265           0 :     nsAutoCString path;
     266           0 :     rv = outFile->GetNativePath(path);
     267           0 :     if (NS_FAILED(rv)) return rv;
     268             : 
     269           0 :     rv = mZip->ExtractFile(item, path.get(), fd);
     270             :   }
     271           0 :   if (NS_FAILED(rv)) return rv;
     272             : 
     273             :   // nsIFile needs milliseconds, while prtime is in microseconds.
     274             :   // non-fatal if this fails, ignore errors
     275           0 :   outFile->SetLastModifiedTime(item->LastModTime() / PR_USEC_PER_MSEC);
     276             : 
     277           0 :   return NS_OK;
     278             : }
     279             : 
     280             : NS_IMETHODIMP
     281           0 : nsJAR::GetEntry(const nsACString &aEntryName, nsIZipEntry* *result)
     282             : {
     283           0 :   nsZipItem* zipItem = mZip->GetItem(PromiseFlatCString(aEntryName).get());
     284           0 :   NS_ENSURE_TRUE(zipItem, NS_ERROR_FILE_TARGET_DOES_NOT_EXIST);
     285             : 
     286           0 :   nsJARItem* jarItem = new nsJARItem(zipItem);
     287             : 
     288           0 :   NS_ADDREF(*result = jarItem);
     289           0 :   return NS_OK;
     290             : }
     291             : 
     292             : NS_IMETHODIMP
     293           0 : nsJAR::HasEntry(const nsACString &aEntryName, bool *result)
     294             : {
     295           0 :   *result = mZip->GetItem(PromiseFlatCString(aEntryName).get()) != nullptr;
     296           0 :   return NS_OK;
     297             : }
     298             : 
     299             : NS_IMETHODIMP
     300           0 : nsJAR::FindEntries(const nsACString &aPattern, nsIUTF8StringEnumerator **result)
     301             : {
     302           0 :   NS_ENSURE_ARG_POINTER(result);
     303             : 
     304             :   nsZipFind *find;
     305           0 :   nsresult rv = mZip->FindInit(aPattern.IsEmpty()? nullptr : PromiseFlatCString(aPattern).get(), &find);
     306           0 :   NS_ENSURE_SUCCESS(rv, rv);
     307             : 
     308           0 :   nsIUTF8StringEnumerator *zipEnum = new nsJAREnumerator(find);
     309             : 
     310           0 :   NS_ADDREF(*result = zipEnum);
     311           0 :   return NS_OK;
     312             : }
     313             : 
     314             : NS_IMETHODIMP
     315           0 : nsJAR::GetInputStream(const nsACString &aFilename, nsIInputStream** result)
     316             : {
     317           0 :   return GetInputStreamWithSpec(EmptyCString(), aFilename, result);
     318             : }
     319             : 
     320             : NS_IMETHODIMP
     321           0 : nsJAR::GetInputStreamWithSpec(const nsACString& aJarDirSpec,
     322             :                           const nsACString &aEntryName, nsIInputStream** result)
     323             : {
     324           0 :   NS_ENSURE_ARG_POINTER(result);
     325             : 
     326             :   // Watch out for the jar:foo.zip!/ (aDir is empty) top-level special case!
     327           0 :   nsZipItem *item = nullptr;
     328           0 :   const char *entry = PromiseFlatCString(aEntryName).get();
     329           0 :   if (*entry) {
     330             :     // First check if item exists in jar
     331           0 :     item = mZip->GetItem(entry);
     332           0 :     if (!item) return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
     333             :   }
     334           0 :   nsJARInputStream* jis = new nsJARInputStream();
     335             :   // addref now so we can call InitFile/InitDirectory()
     336           0 :   NS_ADDREF(*result = jis);
     337             : 
     338           0 :   nsresult rv = NS_OK;
     339           0 :   if (!item || item->IsDirectory()) {
     340           0 :     rv = jis->InitDirectory(this, aJarDirSpec, entry);
     341             :   } else {
     342           0 :     rv = jis->InitFile(this, item);
     343             :   }
     344           0 :   if (NS_FAILED(rv)) {
     345           0 :     NS_RELEASE(*result);
     346             :   }
     347           0 :   return rv;
     348             : }
     349             : 
     350             : NS_IMETHODIMP
     351           0 : nsJAR::GetSigningCert(const nsACString& aFilename, nsIX509Cert** aSigningCert)
     352             : {
     353             :   //-- Parameter check
     354           0 :   if (!aSigningCert) {
     355           0 :     return NS_ERROR_NULL_POINTER;
     356             :   }
     357           0 :   *aSigningCert = nullptr;
     358             : 
     359             :   // Don't check signatures in the omnijar - this is only
     360             :   // interesting for extensions/XPIs.
     361           0 :   RefPtr<nsZipArchive> greOmni = mozilla::Omnijar::GetReader(mozilla::Omnijar::GRE);
     362           0 :   RefPtr<nsZipArchive> appOmni = mozilla::Omnijar::GetReader(mozilla::Omnijar::APP);
     363             : 
     364           0 :   if (mZip == greOmni || mZip == appOmni)
     365           0 :     return NS_OK;
     366             : 
     367             :   //-- Parse the manifest
     368           0 :   nsresult rv = ParseManifest();
     369           0 :   if (NS_FAILED(rv)) return rv;
     370           0 :   if (mGlobalStatus == JAR_NO_MANIFEST)
     371           0 :     return NS_OK;
     372             : 
     373             :   int16_t requestedStatus;
     374           0 :   if (!aFilename.IsEmpty())
     375             :   {
     376             :     //-- Find the item
     377           0 :     nsJARManifestItem* manItem = mManifestData.Get(aFilename);
     378           0 :     if (!manItem)
     379           0 :       return NS_OK;
     380             :     //-- Verify the item against the manifest
     381           0 :     if (!manItem->entryVerified)
     382             :     {
     383           0 :       nsXPIDLCString entryData;
     384           0 :       rv = LoadEntry(aFilename, entryData);
     385           0 :       if (NS_FAILED(rv)) return rv;
     386           0 :       rv = VerifyEntry(manItem, entryData, entryData.Length());
     387           0 :       if (NS_FAILED(rv)) return rv;
     388             :     }
     389           0 :     requestedStatus = manItem->status;
     390             :   }
     391             :   else // User wants identity of signer w/o verifying any entries
     392           0 :     requestedStatus = mGlobalStatus;
     393             : 
     394           0 :   if (requestedStatus != JAR_VALID_MANIFEST) {
     395           0 :     ReportError(aFilename, requestedStatus);
     396             :   } else { // Valid signature
     397           0 :     *aSigningCert = mSigningCert;
     398           0 :     NS_IF_ADDREF(*aSigningCert);
     399             :   }
     400           0 :   return NS_OK;
     401             : }
     402             : 
     403             : NS_IMETHODIMP
     404           0 : nsJAR::GetManifestEntriesCount(uint32_t* count)
     405             : {
     406           0 :   *count = mTotalItemsInManifest;
     407           0 :   return NS_OK;
     408             : }
     409             : 
     410             : nsresult
     411           0 : nsJAR::GetJarPath(nsACString& aResult)
     412             : {
     413           0 :   NS_ENSURE_ARG_POINTER(mZipFile);
     414             : 
     415           0 :   return mZipFile->GetNativePath(aResult);
     416             : }
     417             : 
     418             : nsresult
     419           0 : nsJAR::GetNSPRFileDesc(PRFileDesc** aNSPRFileDesc)
     420             : {
     421           0 :   if (!aNSPRFileDesc) {
     422           0 :     return NS_ERROR_ILLEGAL_VALUE;
     423             :   }
     424           0 :   *aNSPRFileDesc = nullptr;
     425             : 
     426           0 :   if (!mZip) {
     427           0 :     return NS_ERROR_FAILURE;
     428             :   }
     429             : 
     430           0 :   RefPtr<nsZipHandle> handle = mZip->GetFD();
     431           0 :   if (!handle) {
     432           0 :     return NS_ERROR_FAILURE;
     433             :   }
     434             : 
     435           0 :   return handle->GetNSPRFileDesc(aNSPRFileDesc);
     436             : }
     437             : 
     438             : //----------------------------------------------
     439             : // nsJAR private implementation
     440             : //----------------------------------------------
     441             : nsresult
     442           0 : nsJAR::LoadEntry(const nsACString& aFilename, nsCString& aBuf)
     443             : {
     444             :   //-- Get a stream for reading the file
     445             :   nsresult rv;
     446           0 :   nsCOMPtr<nsIInputStream> manifestStream;
     447           0 :   rv = GetInputStream(aFilename, getter_AddRefs(manifestStream));
     448           0 :   if (NS_FAILED(rv)) return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
     449             : 
     450             :   //-- Read the manifest file into memory
     451             :   char* buf;
     452             :   uint64_t len64;
     453           0 :   rv = manifestStream->Available(&len64);
     454           0 :   if (NS_FAILED(rv)) return rv;
     455           0 :   if (len64 >= UINT32_MAX) { // bug 164695
     456           0 :     nsZipArchive::sFileCorruptedReason = "nsJAR: invalid manifest size";
     457           0 :     return NS_ERROR_FILE_CORRUPTED;
     458             :   }
     459           0 :   uint32_t len = (uint32_t)len64;
     460           0 :   buf = (char*)malloc(len+1);
     461           0 :   if (!buf) return NS_ERROR_OUT_OF_MEMORY;
     462             :   uint32_t bytesRead;
     463           0 :   rv = manifestStream->Read(buf, len, &bytesRead);
     464           0 :   if (bytesRead != len) {
     465           0 :     nsZipArchive::sFileCorruptedReason = "nsJAR: manifest too small";
     466           0 :     rv = NS_ERROR_FILE_CORRUPTED;
     467             :   }
     468           0 :   if (NS_FAILED(rv)) {
     469           0 :     free(buf);
     470           0 :     return rv;
     471             :   }
     472           0 :   buf[len] = '\0'; //Null-terminate the buffer
     473           0 :   aBuf.Adopt(buf, len);
     474           0 :   return NS_OK;
     475             : }
     476             : 
     477             : 
     478             : int32_t
     479           0 : nsJAR::ReadLine(const char** src)
     480             : {
     481           0 :   if (!*src) {
     482           0 :     return 0;
     483             :   }
     484             : 
     485             :   //--Moves pointer to beginning of next line and returns line length
     486             :   //  not including CR/LF.
     487             :   int32_t length;
     488           0 :   char* eol = PL_strpbrk(*src, "\r\n");
     489             : 
     490           0 :   if (eol == nullptr) // Probably reached end of file before newline
     491             :   {
     492           0 :     length = strlen(*src);
     493           0 :     if (length == 0) // immediate end-of-file
     494           0 :       *src = nullptr;
     495             :     else             // some data left on this line
     496           0 :       *src += length;
     497             :   }
     498             :   else
     499             :   {
     500           0 :     length = eol - *src;
     501           0 :     if (eol[0] == '\r' && eol[1] == '\n')      // CR LF, so skip 2
     502           0 :       *src = eol+2;
     503             :     else                                       // Either CR or LF, so skip 1
     504           0 :       *src = eol+1;
     505             :   }
     506           0 :   return length;
     507             : }
     508             : 
     509             : //-- The following #defines are used by ParseManifest()
     510             : //   and ParseOneFile(). The header strings are defined in the JAR specification.
     511             : #define JAR_MF 1
     512             : #define JAR_SF 2
     513             : #define JAR_MF_SEARCH_STRING "(M|/M)ETA-INF/(M|m)(ANIFEST|anifest).(MF|mf)$"
     514             : #define JAR_SF_SEARCH_STRING "(M|/M)ETA-INF/*.(SF|sf)$"
     515             : #define JAR_MF_HEADER (const char*)"Manifest-Version: 1.0"
     516             : #define JAR_SF_HEADER (const char*)"Signature-Version: 1.0"
     517             : 
     518             : nsresult
     519           0 : nsJAR::ParseManifest()
     520             : {
     521             :   //-- Verification Step 1
     522           0 :   if (mParsedManifest)
     523           0 :     return NS_OK;
     524             :   //-- (1)Manifest (MF) file
     525           0 :   nsCOMPtr<nsIUTF8StringEnumerator> files;
     526           0 :   nsresult rv = FindEntries(nsDependentCString(JAR_MF_SEARCH_STRING), getter_AddRefs(files));
     527           0 :   if (!files) rv = NS_ERROR_FAILURE;
     528           0 :   if (NS_FAILED(rv)) return rv;
     529             : 
     530             :   //-- Load the file into memory
     531             :   bool more;
     532           0 :   rv = files->HasMore(&more);
     533           0 :   NS_ENSURE_SUCCESS(rv, rv);
     534           0 :   if (!more)
     535             :   {
     536           0 :     mGlobalStatus = JAR_NO_MANIFEST;
     537           0 :     mParsedManifest = true;
     538           0 :     return NS_OK;
     539             :   }
     540             : 
     541           0 :   nsAutoCString manifestFilename;
     542           0 :   rv = files->GetNext(manifestFilename);
     543           0 :   NS_ENSURE_SUCCESS(rv, rv);
     544             : 
     545             :   // Check if there is more than one manifest, if so then error!
     546           0 :   rv = files->HasMore(&more);
     547           0 :   if (NS_FAILED(rv)) return rv;
     548           0 :   if (more)
     549             :   {
     550           0 :     mParsedManifest = true;
     551           0 :     nsZipArchive::sFileCorruptedReason = "nsJAR: duplicate manifests";
     552           0 :     return NS_ERROR_FILE_CORRUPTED; // More than one MF file
     553             :   }
     554             : 
     555           0 :   nsXPIDLCString manifestBuffer;
     556           0 :   rv = LoadEntry(manifestFilename, manifestBuffer);
     557           0 :   if (NS_FAILED(rv)) return rv;
     558             : 
     559             :   //-- Parse it
     560           0 :   rv = ParseOneFile(manifestBuffer, JAR_MF);
     561           0 :   if (NS_FAILED(rv)) return rv;
     562             : 
     563             :   //-- (2)Signature (SF) file
     564             :   // If there are multiple signatures, we select one.
     565           0 :   rv = FindEntries(nsDependentCString(JAR_SF_SEARCH_STRING), getter_AddRefs(files));
     566           0 :   if (!files) rv = NS_ERROR_FAILURE;
     567           0 :   if (NS_FAILED(rv)) return rv;
     568             :   //-- Get an SF file
     569           0 :   rv = files->HasMore(&more);
     570           0 :   if (NS_FAILED(rv)) return rv;
     571           0 :   if (!more)
     572             :   {
     573           0 :     mGlobalStatus = JAR_NO_MANIFEST;
     574           0 :     mParsedManifest = true;
     575           0 :     return NS_OK;
     576             :   }
     577           0 :   rv = files->GetNext(manifestFilename);
     578           0 :   if (NS_FAILED(rv)) return rv;
     579             : 
     580           0 :   rv = LoadEntry(manifestFilename, manifestBuffer);
     581           0 :   if (NS_FAILED(rv)) return rv;
     582             : 
     583             :   //-- Get its corresponding signature file
     584           0 :   nsAutoCString sigFilename(manifestFilename);
     585           0 :   int32_t extension = sigFilename.RFindChar('.') + 1;
     586           0 :   NS_ASSERTION(extension != 0, "Manifest Parser: Missing file extension.");
     587           0 :   (void)sigFilename.Cut(extension, 2);
     588           0 :   nsXPIDLCString sigBuffer;
     589             :   {
     590           0 :     nsAutoCString tempFilename(sigFilename); tempFilename.Append("rsa", 3);
     591           0 :     rv = LoadEntry(tempFilename, sigBuffer);
     592             :   }
     593           0 :   if (NS_FAILED(rv))
     594             :   {
     595           0 :     nsAutoCString tempFilename(sigFilename); tempFilename.Append("RSA", 3);
     596           0 :     rv = LoadEntry(tempFilename, sigBuffer);
     597             :   }
     598           0 :   if (NS_FAILED(rv))
     599             :   {
     600           0 :     mGlobalStatus = JAR_NO_MANIFEST;
     601           0 :     mParsedManifest = true;
     602           0 :     return NS_OK;
     603             :   }
     604             : 
     605             :   //-- Get the signature verifier service
     606             :   nsCOMPtr<nsIDataSignatureVerifier> verifier(
     607           0 :     do_GetService("@mozilla.org/security/datasignatureverifier;1", &rv));
     608           0 :   if (NS_FAILED(rv)) // No signature verifier available
     609             :   {
     610           0 :     mGlobalStatus = JAR_NO_MANIFEST;
     611           0 :     mParsedManifest = true;
     612           0 :     return NS_OK;
     613             :   }
     614             : 
     615             :   //-- Verify that the signature file is a valid signature of the SF file
     616             :   int32_t verifyError;
     617           0 :   rv = verifier->VerifySignature(sigBuffer, manifestBuffer,
     618           0 :                                  &verifyError, getter_AddRefs(mSigningCert));
     619           0 :   if (NS_FAILED(rv)) return rv;
     620           0 :   if (mSigningCert && verifyError == nsIDataSignatureVerifier::VERIFY_OK) {
     621           0 :     mGlobalStatus = JAR_VALID_MANIFEST;
     622           0 :   } else if (verifyError == nsIDataSignatureVerifier::VERIFY_ERROR_UNKNOWN_ISSUER) {
     623           0 :     mGlobalStatus = JAR_INVALID_UNKNOWN_CA;
     624             :   } else {
     625           0 :     mGlobalStatus = JAR_INVALID_SIG;
     626             :   }
     627             : 
     628             :   //-- Parse the SF file. If the verification above failed, principal
     629             :   // is null, and ParseOneFile will mark the relevant entries as invalid.
     630             :   // if ParseOneFile fails, then it has no effect, and we can safely
     631             :   // continue to the next SF file, or return.
     632           0 :   ParseOneFile(manifestBuffer, JAR_SF);
     633           0 :   mParsedManifest = true;
     634             : 
     635           0 :   return NS_OK;
     636             : }
     637             : 
     638             : nsresult
     639           0 : nsJAR::ParseOneFile(const char* filebuf, int16_t aFileType)
     640             : {
     641             :   //-- Check file header
     642           0 :   const char* nextLineStart = filebuf;
     643           0 :   nsAutoCString curLine;
     644             :   int32_t linelen;
     645           0 :   linelen = ReadLine(&nextLineStart);
     646           0 :   curLine.Assign(filebuf, linelen);
     647             : 
     648           0 :   if ( ((aFileType == JAR_MF) && !curLine.Equals(JAR_MF_HEADER) ) ||
     649           0 :        ((aFileType == JAR_SF) && !curLine.Equals(JAR_SF_HEADER) ) ) {
     650           0 :      nsZipArchive::sFileCorruptedReason = "nsJAR: invalid manifest header";
     651           0 :      return NS_ERROR_FILE_CORRUPTED;
     652             :   }
     653             : 
     654             :   //-- Skip header section
     655           0 :   do {
     656           0 :     linelen = ReadLine(&nextLineStart);
     657           0 :   } while (linelen > 0);
     658             : 
     659             :   //-- Set up parsing variables
     660             :   const char* curPos;
     661           0 :   const char* sectionStart = nextLineStart;
     662             : 
     663           0 :   nsJARManifestItem* curItemMF = nullptr;
     664           0 :   bool foundName = false;
     665           0 :   if (aFileType == JAR_MF) {
     666           0 :     curItemMF = new nsJARManifestItem();
     667             :   }
     668             : 
     669           0 :   nsAutoCString curItemName;
     670           0 :   nsAutoCString storedSectionDigest;
     671             : 
     672             :   for(;;)
     673             :   {
     674           0 :     curPos = nextLineStart;
     675           0 :     linelen = ReadLine(&nextLineStart);
     676           0 :     curLine.Assign(curPos, linelen);
     677           0 :     if (linelen == 0)
     678             :     // end of section (blank line or end-of-file)
     679             :     {
     680           0 :       if (aFileType == JAR_MF)
     681             :       {
     682           0 :         mTotalItemsInManifest++;
     683           0 :         if (curItemMF->mType != JAR_INVALID)
     684             :         {
     685             :           //-- Did this section have a name: line?
     686           0 :           if(!foundName)
     687           0 :             curItemMF->mType = JAR_INVALID;
     688             :           else
     689             :           {
     690             :             //-- If it's an internal item, it must correspond
     691             :             //   to a valid jar entry
     692           0 :             if (curItemMF->mType == JAR_INTERNAL)
     693             :             {
     694             :               bool exists;
     695           0 :               nsresult rv = HasEntry(curItemName, &exists);
     696           0 :               if (NS_FAILED(rv) || !exists)
     697           0 :                 curItemMF->mType = JAR_INVALID;
     698             :             }
     699             :             //-- Check for duplicates
     700           0 :             if (mManifestData.Contains(curItemName)) {
     701           0 :               curItemMF->mType = JAR_INVALID;
     702             :             }
     703             :           }
     704             :         }
     705             : 
     706           0 :         if (curItemMF->mType == JAR_INVALID)
     707           0 :           delete curItemMF;
     708             :         else //-- calculate section digest
     709             :         {
     710           0 :           uint32_t sectionLength = curPos - sectionStart;
     711           0 :           CalculateDigest(sectionStart, sectionLength,
     712           0 :                           curItemMF->calculatedSectionDigest);
     713             :           //-- Save item in the hashtable
     714           0 :           mManifestData.Put(curItemName, curItemMF);
     715             :         }
     716           0 :         if (nextLineStart == nullptr) // end-of-file
     717           0 :           break;
     718             : 
     719           0 :         sectionStart = nextLineStart;
     720           0 :         curItemMF = new nsJARManifestItem();
     721             :       } // (aFileType == JAR_MF)
     722             :       else
     723             :         //-- file type is SF, compare digest with calculated
     724             :         //   section digests from MF file.
     725             :       {
     726           0 :         if (foundName)
     727             :         {
     728           0 :           nsJARManifestItem* curItemSF = mManifestData.Get(curItemName);
     729           0 :           if(curItemSF)
     730             :           {
     731           0 :             NS_ASSERTION(curItemSF->status == JAR_NOT_SIGNED,
     732             :                          "SECURITY ERROR: nsJARManifestItem not correctly initialized");
     733           0 :             curItemSF->status = mGlobalStatus;
     734           0 :             if (curItemSF->status == JAR_VALID_MANIFEST)
     735             :             { // Compare digests
     736           0 :               if (storedSectionDigest.IsEmpty())
     737           0 :                 curItemSF->status = JAR_NOT_SIGNED;
     738             :               else
     739             :               {
     740           0 :                 if (!storedSectionDigest.Equals(curItemSF->calculatedSectionDigest))
     741           0 :                   curItemSF->status = JAR_INVALID_MANIFEST;
     742           0 :                 curItemSF->calculatedSectionDigest.Truncate();
     743           0 :                 storedSectionDigest.Truncate();
     744             :               }
     745             :             } // (aPrincipal != nullptr)
     746             :           } // if(curItemSF)
     747             :         } // if(foundName)
     748             : 
     749           0 :         if(nextLineStart == nullptr) // end-of-file
     750           0 :           break;
     751             :       } // aFileType == JAR_SF
     752           0 :       foundName = false;
     753           0 :       continue;
     754             :     } // if(linelen == 0)
     755             : 
     756             :     //-- Look for continuations (beginning with a space) on subsequent lines
     757             :     //   and append them to the current line.
     758           0 :     while(*nextLineStart == ' ')
     759             :     {
     760           0 :       curPos = nextLineStart;
     761           0 :       int32_t continuationLen = ReadLine(&nextLineStart) - 1;
     762           0 :       nsAutoCString continuation(curPos+1, continuationLen);
     763           0 :       curLine += continuation;
     764           0 :       linelen += continuationLen;
     765             :     }
     766             : 
     767             :     //-- Find colon in current line, this separates name from value
     768           0 :     int32_t colonPos = curLine.FindChar(':');
     769           0 :     if (colonPos == -1)    // No colon on line, ignore line
     770           0 :       continue;
     771             :     //-- Break down the line
     772           0 :     nsAutoCString lineName;
     773           0 :     curLine.Left(lineName, colonPos);
     774           0 :     nsAutoCString lineData;
     775           0 :     curLine.Mid(lineData, colonPos+2, linelen - (colonPos+2));
     776             : 
     777             :     //-- Lines to look for:
     778             :     // (1) Digest:
     779           0 :     if (lineName.LowerCaseEqualsLiteral("sha1-digest"))
     780             :     //-- This is a digest line, save the data in the appropriate place
     781             :     {
     782           0 :       if(aFileType == JAR_MF)
     783           0 :         curItemMF->storedEntryDigest = lineData;
     784             :       else
     785           0 :         storedSectionDigest = lineData;
     786           0 :       continue;
     787             :     }
     788             : 
     789             :     // (2) Name: associates this manifest section with a file in the jar.
     790           0 :     if (!foundName && lineName.LowerCaseEqualsLiteral("name"))
     791             :     {
     792           0 :       curItemName = lineData;
     793           0 :       foundName = true;
     794           0 :       continue;
     795             :     }
     796             : 
     797             :     // (3) Magic: this may be an inline Javascript.
     798             :     //     We can't do any other kind of magic.
     799           0 :     if (aFileType == JAR_MF && lineName.LowerCaseEqualsLiteral("magic"))
     800             :     {
     801           0 :       if (lineData.LowerCaseEqualsLiteral("javascript"))
     802           0 :         curItemMF->mType = JAR_EXTERNAL;
     803             :       else
     804           0 :         curItemMF->mType = JAR_INVALID;
     805           0 :       continue;
     806             :     }
     807             : 
     808           0 :   } // for (;;)
     809           0 :   return NS_OK;
     810             : } //ParseOneFile()
     811             : 
     812             : nsresult
     813           0 : nsJAR::VerifyEntry(nsJARManifestItem* aManItem, const char* aEntryData,
     814             :                    uint32_t aLen)
     815             : {
     816           0 :   if (aManItem->status == JAR_VALID_MANIFEST)
     817             :   {
     818           0 :     if (aManItem->storedEntryDigest.IsEmpty())
     819             :       // No entry digests in manifest file. Entry is unsigned.
     820           0 :       aManItem->status = JAR_NOT_SIGNED;
     821             :     else
     822             :     { //-- Calculate and compare digests
     823           0 :       nsCString calculatedEntryDigest;
     824           0 :       nsresult rv = CalculateDigest(aEntryData, aLen, calculatedEntryDigest);
     825           0 :       if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
     826           0 :       if (!aManItem->storedEntryDigest.Equals(calculatedEntryDigest))
     827           0 :         aManItem->status = JAR_INVALID_ENTRY;
     828           0 :       aManItem->storedEntryDigest.Truncate();
     829             :     }
     830             :   }
     831           0 :   aManItem->entryVerified = true;
     832           0 :   return NS_OK;
     833             : }
     834             : 
     835           0 : void nsJAR::ReportError(const nsACString &aFilename, int16_t errorCode)
     836             : {
     837             :   //-- Generate error message
     838           0 :   nsAutoString message;
     839           0 :   message.AssignLiteral("Signature Verification Error: the signature on ");
     840           0 :   if (!aFilename.IsEmpty())
     841           0 :     AppendASCIItoUTF16(aFilename, message);
     842             :   else
     843           0 :     message.AppendLiteral("this .jar archive");
     844           0 :   message.AppendLiteral(" is invalid because ");
     845           0 :   switch(errorCode)
     846             :   {
     847             :   case JAR_NOT_SIGNED:
     848           0 :     message.AppendLiteral("the archive did not contain a valid PKCS7 signature.");
     849           0 :     break;
     850             :   case JAR_INVALID_SIG:
     851           0 :     message.AppendLiteral("the digital signature (*.RSA) file is not a valid signature of the signature instruction file (*.SF).");
     852           0 :     break;
     853             :   case JAR_INVALID_UNKNOWN_CA:
     854           0 :     message.AppendLiteral("the certificate used to sign this file has an unrecognized issuer.");
     855           0 :     break;
     856             :   case JAR_INVALID_MANIFEST:
     857           0 :     message.AppendLiteral("the signature instruction file (*.SF) does not contain a valid hash of the MANIFEST.MF file.");
     858           0 :     break;
     859             :   case JAR_INVALID_ENTRY:
     860           0 :     message.AppendLiteral("the MANIFEST.MF file does not contain a valid hash of the file being verified.");
     861           0 :     break;
     862             :   case JAR_NO_MANIFEST:
     863           0 :     message.AppendLiteral("the archive did not contain a manifest.");
     864           0 :     break;
     865             :   default:
     866           0 :     message.AppendLiteral("of an unknown problem.");
     867             :   }
     868             : 
     869             :   // Report error in JS console
     870           0 :   nsCOMPtr<nsIConsoleService> console(do_GetService("@mozilla.org/consoleservice;1"));
     871           0 :   if (console)
     872             :   {
     873           0 :     console->LogStringMessage(message.get());
     874             :   }
     875             : #ifdef DEBUG
     876           0 :   char* messageCstr = ToNewCString(message);
     877           0 :   if (!messageCstr) return;
     878           0 :   fprintf(stderr, "%s\n", messageCstr);
     879           0 :   free(messageCstr);
     880             : #endif
     881             : }
     882             : 
     883             : 
     884           0 : nsresult nsJAR::CalculateDigest(const char* aInBuf, uint32_t aLen,
     885             :                                 nsCString& digest)
     886             : {
     887             :   nsresult rv;
     888             : 
     889           0 :   nsCOMPtr<nsICryptoHash> hasher = do_CreateInstance("@mozilla.org/security/hash;1", &rv);
     890           0 :   if (NS_FAILED(rv)) return rv;
     891             : 
     892           0 :   rv = hasher->Init(nsICryptoHash::SHA1);
     893           0 :   if (NS_FAILED(rv)) return rv;
     894             : 
     895           0 :   rv = hasher->Update((const uint8_t*) aInBuf, aLen);
     896           0 :   if (NS_FAILED(rv)) return rv;
     897             : 
     898           0 :   return hasher->Finish(true, digest);
     899             : }
     900             : 
     901           0 : NS_IMPL_ISUPPORTS(nsJAREnumerator, nsIUTF8StringEnumerator)
     902             : 
     903             : //----------------------------------------------
     904             : // nsJAREnumerator::HasMore
     905             : //----------------------------------------------
     906             : NS_IMETHODIMP
     907           0 : nsJAREnumerator::HasMore(bool* aResult)
     908             : {
     909             :     // try to get the next element
     910           0 :     if (!mName) {
     911           0 :         NS_ASSERTION(mFind, "nsJAREnumerator: Missing zipFind.");
     912           0 :         nsresult rv = mFind->FindNext( &mName, &mNameLen );
     913           0 :         if (rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) {
     914           0 :             *aResult = false;                    // No more matches available
     915           0 :             return NS_OK;
     916             :         }
     917           0 :         NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);    // no error translation
     918             :     }
     919             : 
     920           0 :     *aResult = true;
     921           0 :     return NS_OK;
     922             : }
     923             : 
     924             : //----------------------------------------------
     925             : // nsJAREnumerator::GetNext
     926             : //----------------------------------------------
     927             : NS_IMETHODIMP
     928           0 : nsJAREnumerator::GetNext(nsACString& aResult)
     929             : {
     930             :     // check if the current item is "stale"
     931           0 :     if (!mName) {
     932             :         bool     bMore;
     933           0 :         nsresult rv = HasMore(&bMore);
     934           0 :         if (NS_FAILED(rv) || !bMore)
     935           0 :             return NS_ERROR_FAILURE; // no error translation
     936             :     }
     937           0 :     aResult.Assign(mName, mNameLen);
     938           0 :     mName = 0; // we just gave this one away
     939           0 :     return NS_OK;
     940             : }
     941             : 
     942             : 
     943           0 : NS_IMPL_ISUPPORTS(nsJARItem, nsIZipEntry)
     944             : 
     945           0 : nsJARItem::nsJARItem(nsZipItem* aZipItem)
     946           0 :     : mSize(aZipItem->Size()),
     947           0 :       mRealsize(aZipItem->RealSize()),
     948           0 :       mCrc32(aZipItem->CRC32()),
     949           0 :       mLastModTime(aZipItem->LastModTime()),
     950           0 :       mCompression(aZipItem->Compression()),
     951           0 :       mPermissions(aZipItem->Mode()),
     952           0 :       mIsDirectory(aZipItem->IsDirectory()),
     953           0 :       mIsSynthetic(aZipItem->isSynthetic)
     954             : {
     955           0 : }
     956             : 
     957             : //------------------------------------------
     958             : // nsJARItem::GetCompression
     959             : //------------------------------------------
     960             : NS_IMETHODIMP
     961           0 : nsJARItem::GetCompression(uint16_t *aCompression)
     962             : {
     963           0 :     NS_ENSURE_ARG_POINTER(aCompression);
     964             : 
     965           0 :     *aCompression = mCompression;
     966           0 :     return NS_OK;
     967             : }
     968             : 
     969             : //------------------------------------------
     970             : // nsJARItem::GetSize
     971             : //------------------------------------------
     972             : NS_IMETHODIMP
     973           0 : nsJARItem::GetSize(uint32_t *aSize)
     974             : {
     975           0 :     NS_ENSURE_ARG_POINTER(aSize);
     976             : 
     977           0 :     *aSize = mSize;
     978           0 :     return NS_OK;
     979             : }
     980             : 
     981             : //------------------------------------------
     982             : // nsJARItem::GetRealSize
     983             : //------------------------------------------
     984             : NS_IMETHODIMP
     985           0 : nsJARItem::GetRealSize(uint32_t *aRealsize)
     986             : {
     987           0 :     NS_ENSURE_ARG_POINTER(aRealsize);
     988             : 
     989           0 :     *aRealsize = mRealsize;
     990           0 :     return NS_OK;
     991             : }
     992             : 
     993             : //------------------------------------------
     994             : // nsJARItem::GetCrc32
     995             : //------------------------------------------
     996             : NS_IMETHODIMP
     997           0 : nsJARItem::GetCRC32(uint32_t *aCrc32)
     998             : {
     999           0 :     NS_ENSURE_ARG_POINTER(aCrc32);
    1000             : 
    1001           0 :     *aCrc32 = mCrc32;
    1002           0 :     return NS_OK;
    1003             : }
    1004             : 
    1005             : //------------------------------------------
    1006             : // nsJARItem::GetIsDirectory
    1007             : //------------------------------------------
    1008             : NS_IMETHODIMP
    1009           0 : nsJARItem::GetIsDirectory(bool *aIsDirectory)
    1010             : {
    1011           0 :     NS_ENSURE_ARG_POINTER(aIsDirectory);
    1012             : 
    1013           0 :     *aIsDirectory = mIsDirectory;
    1014           0 :     return NS_OK;
    1015             : }
    1016             : 
    1017             : //------------------------------------------
    1018             : // nsJARItem::GetIsSynthetic
    1019             : //------------------------------------------
    1020             : NS_IMETHODIMP
    1021           0 : nsJARItem::GetIsSynthetic(bool *aIsSynthetic)
    1022             : {
    1023           0 :     NS_ENSURE_ARG_POINTER(aIsSynthetic);
    1024             : 
    1025           0 :     *aIsSynthetic = mIsSynthetic;
    1026           0 :     return NS_OK;
    1027             : }
    1028             : 
    1029             : //------------------------------------------
    1030             : // nsJARItem::GetLastModifiedTime
    1031             : //------------------------------------------
    1032             : NS_IMETHODIMP
    1033           0 : nsJARItem::GetLastModifiedTime(PRTime* aLastModTime)
    1034             : {
    1035           0 :     NS_ENSURE_ARG_POINTER(aLastModTime);
    1036             : 
    1037           0 :     *aLastModTime = mLastModTime;
    1038           0 :     return NS_OK;
    1039             : }
    1040             : 
    1041             : //------------------------------------------
    1042             : // nsJARItem::GetPermissions
    1043             : //------------------------------------------
    1044             : NS_IMETHODIMP
    1045           0 : nsJARItem::GetPermissions(uint32_t* aPermissions)
    1046             : {
    1047           0 :     NS_ENSURE_ARG_POINTER(aPermissions);
    1048             : 
    1049           0 :     *aPermissions = mPermissions;
    1050           0 :     return NS_OK;
    1051             : }
    1052             : 
    1053             : ////////////////////////////////////////////////////////////////////////////////
    1054             : // nsIZipReaderCache
    1055             : 
    1056          16 : NS_IMPL_ISUPPORTS(nsZipReaderCache, nsIZipReaderCache, nsIObserver, nsISupportsWeakReference)
    1057             : 
    1058           1 : nsZipReaderCache::nsZipReaderCache()
    1059             :   : mLock("nsZipReaderCache.mLock")
    1060             :   , mCacheSize(0)
    1061           1 :   , mZips()
    1062             : #ifdef ZIP_CACHE_HIT_RATE
    1063             :     ,
    1064             :     mZipCacheLookups(0),
    1065             :     mZipCacheHits(0),
    1066             :     mZipCacheFlushes(0),
    1067             :     mZipSyncMisses(0)
    1068             : #endif
    1069             : {
    1070           1 : }
    1071             : 
    1072             : NS_IMETHODIMP
    1073           1 : nsZipReaderCache::Init(uint32_t cacheSize)
    1074             : {
    1075           1 :   mCacheSize = cacheSize;
    1076             : 
    1077             : // Register as a memory pressure observer
    1078             :   nsCOMPtr<nsIObserverService> os =
    1079           2 :            do_GetService("@mozilla.org/observer-service;1");
    1080           1 :   if (os)
    1081             :   {
    1082           1 :     os->AddObserver(this, "memory-pressure", true);
    1083           1 :     os->AddObserver(this, "chrome-flush-caches", true);
    1084           1 :     os->AddObserver(this, "flush-cache-entry", true);
    1085             :   }
    1086             : // ignore failure of the observer registration.
    1087             : 
    1088           2 :   return NS_OK;
    1089             : }
    1090             : 
    1091           0 : nsZipReaderCache::~nsZipReaderCache()
    1092             : {
    1093           0 :   for (auto iter = mZips.Iter(); !iter.Done(); iter.Next()) {
    1094           0 :     iter.UserData()->SetZipReaderCache(nullptr);
    1095             :   }
    1096             : 
    1097             : #ifdef ZIP_CACHE_HIT_RATE
    1098             :   printf("nsZipReaderCache size=%d hits=%d lookups=%d rate=%f%% flushes=%d missed %d\n",
    1099             :          mCacheSize, mZipCacheHits, mZipCacheLookups,
    1100             :          (float)mZipCacheHits / mZipCacheLookups,
    1101             :          mZipCacheFlushes, mZipSyncMisses);
    1102             : #endif
    1103           0 : }
    1104             : 
    1105             : NS_IMETHODIMP
    1106           0 : nsZipReaderCache::IsCached(nsIFile* zipFile, bool* aResult)
    1107             : {
    1108           0 :   NS_ENSURE_ARG_POINTER(zipFile);
    1109             :   nsresult rv;
    1110           0 :   MutexAutoLock lock(mLock);
    1111             : 
    1112           0 :   nsAutoCString uri;
    1113           0 :   rv = zipFile->GetNativePath(uri);
    1114           0 :   if (NS_FAILED(rv))
    1115           0 :     return rv;
    1116             : 
    1117           0 :   uri.Insert(NS_LITERAL_CSTRING("file:"), 0);
    1118             : 
    1119           0 :   *aResult = mZips.Contains(uri);
    1120           0 :   return NS_OK;
    1121             : }
    1122             : 
    1123             : NS_IMETHODIMP
    1124           0 : nsZipReaderCache::GetZip(nsIFile* zipFile, nsIZipReader* *result)
    1125             : {
    1126           0 :   NS_ENSURE_ARG_POINTER(zipFile);
    1127             :   nsresult rv;
    1128           0 :   MutexAutoLock lock(mLock);
    1129             : 
    1130             : #ifdef ZIP_CACHE_HIT_RATE
    1131             :   mZipCacheLookups++;
    1132             : #endif
    1133             : 
    1134           0 :   nsAutoCString uri;
    1135           0 :   rv = zipFile->GetNativePath(uri);
    1136           0 :   if (NS_FAILED(rv)) return rv;
    1137             : 
    1138           0 :   uri.Insert(NS_LITERAL_CSTRING("file:"), 0);
    1139             : 
    1140           0 :   RefPtr<nsJAR> zip;
    1141           0 :   mZips.Get(uri, getter_AddRefs(zip));
    1142           0 :   if (zip) {
    1143             : #ifdef ZIP_CACHE_HIT_RATE
    1144             :     mZipCacheHits++;
    1145             : #endif
    1146           0 :     zip->ClearReleaseTime();
    1147             :   } else {
    1148           0 :     zip = new nsJAR();
    1149           0 :     zip->SetZipReaderCache(this);
    1150           0 :     rv = zip->Open(zipFile);
    1151           0 :     if (NS_FAILED(rv)) {
    1152           0 :       return rv;
    1153             :     }
    1154             : 
    1155           0 :     MOZ_ASSERT(!mZips.Contains(uri));
    1156           0 :     mZips.Put(uri, zip);
    1157             :   }
    1158           0 :   zip.forget(result);
    1159           0 :   return rv;
    1160             : }
    1161             : 
    1162             : NS_IMETHODIMP
    1163           0 : nsZipReaderCache::GetInnerZip(nsIFile* zipFile, const nsACString &entry,
    1164             :                               nsIZipReader* *result)
    1165             : {
    1166           0 :   NS_ENSURE_ARG_POINTER(zipFile);
    1167             : 
    1168           0 :   nsCOMPtr<nsIZipReader> outerZipReader;
    1169           0 :   nsresult rv = GetZip(zipFile, getter_AddRefs(outerZipReader));
    1170           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1171             : 
    1172           0 :   MutexAutoLock lock(mLock);
    1173             : 
    1174             : #ifdef ZIP_CACHE_HIT_RATE
    1175             :   mZipCacheLookups++;
    1176             : #endif
    1177             : 
    1178           0 :   nsAutoCString uri;
    1179           0 :   rv = zipFile->GetNativePath(uri);
    1180           0 :   if (NS_FAILED(rv)) return rv;
    1181             : 
    1182           0 :   uri.Insert(NS_LITERAL_CSTRING("jar:"), 0);
    1183           0 :   uri.AppendLiteral("!/");
    1184           0 :   uri.Append(entry);
    1185             : 
    1186           0 :   RefPtr<nsJAR> zip;
    1187           0 :   mZips.Get(uri, getter_AddRefs(zip));
    1188           0 :   if (zip) {
    1189             : #ifdef ZIP_CACHE_HIT_RATE
    1190             :     mZipCacheHits++;
    1191             : #endif
    1192           0 :     zip->ClearReleaseTime();
    1193             :   } else {
    1194           0 :     zip = new nsJAR();
    1195           0 :     zip->SetZipReaderCache(this);
    1196             : 
    1197           0 :     rv = zip->OpenInner(outerZipReader, entry);
    1198           0 :     if (NS_FAILED(rv)) {
    1199           0 :       return rv;
    1200             :     }
    1201             : 
    1202           0 :     MOZ_ASSERT(!mZips.Contains(uri));
    1203           0 :     mZips.Put(uri, zip);
    1204             :   }
    1205           0 :   zip.forget(result);
    1206           0 :   return rv;
    1207             : }
    1208             : 
    1209             : NS_IMETHODIMP
    1210           0 : nsZipReaderCache::GetFd(nsIFile* zipFile, PRFileDesc** aRetVal)
    1211             : {
    1212             : #if defined(XP_WIN)
    1213             :   MOZ_CRASH("Not implemented");
    1214             :   return NS_ERROR_NOT_IMPLEMENTED;
    1215             : #else
    1216           0 :   if (!zipFile) {
    1217           0 :     return NS_ERROR_FAILURE;
    1218             :   }
    1219             : 
    1220             :   nsresult rv;
    1221           0 :   nsAutoCString uri;
    1222           0 :   rv = zipFile->GetNativePath(uri);
    1223           0 :   if (NS_FAILED(rv)) {
    1224           0 :     return rv;
    1225             :   }
    1226           0 :   uri.Insert(NS_LITERAL_CSTRING("file:"), 0);
    1227             : 
    1228           0 :   MutexAutoLock lock(mLock);
    1229           0 :   RefPtr<nsJAR> zip;
    1230           0 :   mZips.Get(uri, getter_AddRefs(zip));
    1231           0 :   if (!zip) {
    1232           0 :     return NS_ERROR_FAILURE;
    1233             :   }
    1234             : 
    1235           0 :   zip->ClearReleaseTime();
    1236           0 :   rv = zip->GetNSPRFileDesc(aRetVal);
    1237             :   // Do this to avoid possible deadlock on mLock with ReleaseZip().
    1238           0 :   MutexAutoUnlock unlock(mLock);
    1239           0 :   RefPtr<nsJAR> zipTemp = zip.forget();
    1240           0 :   return rv;
    1241             : #endif /* XP_WIN */
    1242             : }
    1243             : 
    1244             : nsresult
    1245           0 : nsZipReaderCache::ReleaseZip(nsJAR* zip)
    1246             : {
    1247             :   nsresult rv;
    1248           0 :   MutexAutoLock lock(mLock);
    1249             : 
    1250             :   // It is possible that two thread compete for this zip. The dangerous
    1251             :   // case is where one thread Releases the zip and discovers that the ref
    1252             :   // count has gone to one. Before it can call this ReleaseZip method
    1253             :   // another thread calls our GetZip method. The ref count goes to two. That
    1254             :   // second thread then Releases the zip and the ref count goes to one. It
    1255             :   // then tries to enter this ReleaseZip method and blocks while the first
    1256             :   // thread is still here. The first thread continues and remove the zip from
    1257             :   // the cache and calls its Release method sending the ref count to 0 and
    1258             :   // deleting the zip. However, the second thread is still blocked at the
    1259             :   // start of ReleaseZip, but the 'zip' param now hold a reference to a
    1260             :   // deleted zip!
    1261             :   //
    1262             :   // So, we are going to try safeguarding here by searching our hashtable while
    1263             :   // locked here for the zip. We return fast if it is not found.
    1264             : 
    1265           0 :   bool found = false;
    1266           0 :   for (auto iter = mZips.Iter(); !iter.Done(); iter.Next()) {
    1267           0 :     if (zip == iter.UserData()) {
    1268           0 :       found = true;
    1269           0 :       break;
    1270             :     }
    1271             :   }
    1272             : 
    1273           0 :   if (!found) {
    1274             : #ifdef ZIP_CACHE_HIT_RATE
    1275             :     mZipSyncMisses++;
    1276             : #endif
    1277           0 :     return NS_OK;
    1278             :   }
    1279             : 
    1280           0 :   zip->SetReleaseTime();
    1281             : 
    1282           0 :   if (mZips.Count() <= mCacheSize)
    1283           0 :     return NS_OK;
    1284             : 
    1285             :   // Find the oldest zip.
    1286           0 :   nsJAR* oldest = nullptr;
    1287           0 :   for (auto iter = mZips.Iter(); !iter.Done(); iter.Next()) {
    1288           0 :     nsJAR* current = iter.UserData();
    1289           0 :     PRIntervalTime currentReleaseTime = current->GetReleaseTime();
    1290           0 :     if (currentReleaseTime != PR_INTERVAL_NO_TIMEOUT) {
    1291           0 :       if (oldest == nullptr ||
    1292           0 :           currentReleaseTime < oldest->GetReleaseTime()) {
    1293           0 :         oldest = current;
    1294             :       }
    1295             :     }
    1296             :   }
    1297             : 
    1298             :   // Because of the craziness above it is possible that there is no zip that
    1299             :   // needs removing.
    1300           0 :   if (!oldest)
    1301           0 :     return NS_OK;
    1302             : 
    1303             : #ifdef ZIP_CACHE_HIT_RATE
    1304             :     mZipCacheFlushes++;
    1305             : #endif
    1306             : 
    1307             :   // remove from hashtable
    1308           0 :   nsAutoCString uri;
    1309           0 :   rv = oldest->GetJarPath(uri);
    1310           0 :   if (NS_FAILED(rv))
    1311           0 :     return rv;
    1312             : 
    1313           0 :   if (oldest->mOuterZipEntry.IsEmpty()) {
    1314           0 :     uri.Insert(NS_LITERAL_CSTRING("file:"), 0);
    1315             :   } else {
    1316           0 :     uri.Insert(NS_LITERAL_CSTRING("jar:"), 0);
    1317           0 :     uri.AppendLiteral("!/");
    1318           0 :     uri.Append(oldest->mOuterZipEntry);
    1319             :   }
    1320             : 
    1321             :   // Retrieving and removing the JAR must be done without an extra AddRef
    1322             :   // and Release, or we'll trigger nsJAR::Release's magic refcount 1 case
    1323             :   // an extra time and trigger a deadlock.
    1324           0 :   RefPtr<nsJAR> removed;
    1325           0 :   mZips.Remove(uri, getter_AddRefs(removed));
    1326           0 :   NS_ASSERTION(removed, "botched");
    1327           0 :   NS_ASSERTION(oldest == removed, "removed wrong entry");
    1328             : 
    1329           0 :   if (removed)
    1330           0 :     removed->SetZipReaderCache(nullptr);
    1331             : 
    1332           0 :   return NS_OK;
    1333             : }
    1334             : 
    1335             : NS_IMETHODIMP
    1336           0 : nsZipReaderCache::Observe(nsISupports *aSubject,
    1337             :                           const char *aTopic,
    1338             :                           const char16_t *aSomeData)
    1339             : {
    1340           0 :   if (strcmp(aTopic, "memory-pressure") == 0) {
    1341           0 :     MutexAutoLock lock(mLock);
    1342           0 :     for (auto iter = mZips.Iter(); !iter.Done(); iter.Next()) {
    1343           0 :       RefPtr<nsJAR>& current = iter.Data();
    1344           0 :       if (current->GetReleaseTime() != PR_INTERVAL_NO_TIMEOUT) {
    1345           0 :         current->SetZipReaderCache(nullptr);
    1346           0 :         iter.Remove();
    1347             :       }
    1348             :     }
    1349             :   }
    1350           0 :   else if (strcmp(aTopic, "chrome-flush-caches") == 0) {
    1351           0 :     MutexAutoLock lock(mLock);
    1352           0 :     for (auto iter = mZips.Iter(); !iter.Done(); iter.Next()) {
    1353           0 :       iter.UserData()->SetZipReaderCache(nullptr);
    1354             :     }
    1355           0 :     mZips.Clear();
    1356             :   }
    1357           0 :   else if (strcmp(aTopic, "flush-cache-entry") == 0) {
    1358           0 :     nsCOMPtr<nsIFile> file;
    1359           0 :     if (aSubject) {
    1360           0 :       file = do_QueryInterface(aSubject);
    1361           0 :     } else if (aSomeData) {
    1362           0 :       nsDependentString fileName(aSomeData);
    1363           0 :       Unused << NS_NewLocalFile(fileName, false, getter_AddRefs(file));
    1364             :     }
    1365             : 
    1366           0 :     if (!file)
    1367           0 :       return NS_OK;
    1368             : 
    1369           0 :     nsAutoCString uri;
    1370           0 :     if (NS_FAILED(file->GetNativePath(uri)))
    1371           0 :       return NS_OK;
    1372             : 
    1373           0 :     uri.Insert(NS_LITERAL_CSTRING("file:"), 0);
    1374             : 
    1375           0 :     MutexAutoLock lock(mLock);
    1376             : 
    1377           0 :     RefPtr<nsJAR> zip;
    1378           0 :     mZips.Get(uri, getter_AddRefs(zip));
    1379           0 :     if (!zip)
    1380           0 :       return NS_OK;
    1381             : 
    1382             : #ifdef ZIP_CACHE_HIT_RATE
    1383             :     mZipCacheFlushes++;
    1384             : #endif
    1385             : 
    1386           0 :     zip->SetZipReaderCache(nullptr);
    1387             : 
    1388           0 :     mZips.Remove(uri);
    1389             :   }
    1390           0 :   return NS_OK;
    1391             : }
    1392             : 
    1393             : ////////////////////////////////////////////////////////////////////////////////

Generated by: LCOV version 1.13