LCOV - code coverage report
Current view: directory - modules/libjar - nsJAR.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 619 321 51.9 %
Date: 2012-06-02 Functions: 62 49 79.0 %

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

Generated by: LCOV version 1.7