Back to index

lightning-sunbird  0.9+nobinonly
nsZipArchive.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator client code, released
00016  * March 31, 1998.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1998
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Daniel Veditz <dveditz@netscape.com>
00025  *   Samir Gehani <sgehani@netscape.com>
00026  *   Mitch Stoltz <mstoltz@netscape.com>
00027  *   Jeroen Dobbelaere <jeroen.dobbelaere@acunia.com>
00028  *
00029  * Alternatively, the contents of this file may be used under the terms of
00030  * either the GNU General Public License Version 2 or later (the "GPL"), or
00031  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00032  * in which case the provisions of the GPL or the LGPL are applicable instead
00033  * of those above. If you wish to allow use of your version of this file only
00034  * under the terms of either the GPL or the LGPL, and not to allow others to
00035  * use your version of this file under the terms of the MPL, indicate your
00036  * decision by deleting the provisions above and replace them with the notice
00037  * and other provisions required by the GPL or the LGPL. If you do not delete
00038  * the provisions above, a recipient may use your version of this file under
00039  * the terms of any one of the MPL, the GPL or the LGPL.
00040  *
00041  * ***** END LICENSE BLOCK ***** */
00042 
00043 /*
00044  * This module implements a simple archive extractor for the PKZIP format.
00045  *
00046  * The underlying nsZipArchive is NOT thread-safe. Do not pass references
00047  * or pointers to it across thread boundaries.
00048  */
00049 
00050 
00051 #ifndef STANDALONE
00052 
00053 #include "nsWildCard.h"
00054 #include "nscore.h"
00055 #include "prmem.h"
00056 #include "prio.h"
00057 #include "plstr.h"
00058 #include "prlog.h"
00059 #include "prprf.h"
00060 #define ZFILE_CREATE    PR_WRONLY | PR_CREATE_FILE
00061 #define READTYPE  PRInt32
00062 #include "zlib.h"
00063 #include "nsISupportsUtils.h"
00064 #include "nsRecyclingAllocator.h"
00065 #include "nsPrintfCString.h"
00071 #define NBUCKETS 6
00072 #define BY4ALLOC_ITEMS 320
00073 nsRecyclingAllocator *gZlibAllocator = NULL;
00074 
00075 // For placement new used for arena allocations of zip file list
00076 #include NEW_H
00077 
00078 #else /* STANDALONE */
00079 
00080 #ifdef XP_WIN
00081 #include "windows.h"
00082 #endif
00083 
00084 #undef MOZILLA_CLIENT       // undoes prtypes damage in zlib.h
00085 #define ZFILE_CREATE  "wb"
00086 #define READTYPE  PRUint32
00087 #include "zlib.h"
00088 #undef PR_PUBLIC_API
00089 #include "zipstub.h"
00090 
00091 #ifdef XP_MAC
00092 #include <string.h>
00093 #include <stdlib.h>
00094 
00095 char * strdup(const char *src);
00096 char * strdup(const char *src)
00097 {
00098     long len = strlen(src);
00099     char *dup = (char *)malloc(len+1 * sizeof(char));
00100     memcpy(dup, src, len+1);
00101     return dup;
00102 }
00103 #endif
00104 
00105 #endif /* STANDALONE */
00106 
00107 #ifdef XP_UNIX
00108     #include <sys/types.h>
00109     #include <sys/stat.h>
00110     #include <limits.h>
00111     #include <unistd.h>
00112 #elif defined(XP_WIN) || defined(XP_OS2)
00113     #include <io.h>
00114 #endif
00115 
00116 #ifndef XP_UNIX /* we need to have some constant defined in limits.h and unistd.h */
00117 #  ifndef S_IFMT
00118 #    define S_IFMT 0170000
00119 #  endif
00120 #  ifndef S_IFLNK
00121 #    define S_IFLNK  0120000
00122 #  endif
00123 #  ifndef PATH_MAX
00124 #    define PATH_MAX 1024
00125 #  endif
00126 #endif  /* XP_UNIX */
00127 
00128 #include "zipfile.h"
00129 #include "zipstruct.h"
00130 #include "nsZipArchive.h"
00131 
00132 static PRUint16 xtoint(unsigned char *ii);
00133 static PRUint32 xtolong(unsigned char *ll);
00134 static PRUint16 ExtractMode(PRUint32 ext_attr);
00135 static PRBool   IsSymlink(PRUint32 ext_attr);
00136 
00137 /*---------------------------------------------
00138  * C API wrapper for nsZipArchive
00139  *--------------------------------------------*/
00140 
00141 #ifdef STANDALONE
00142 
00153 PR_PUBLIC_API(PRInt32) ZIP_OpenArchive(const char * zipname, void** hZip)
00154 {
00155   PRInt32 status;
00156 
00157   /*--- error check args ---*/
00158   if (hZip == 0)
00159     return ZIP_ERR_PARAM;
00160 
00161   /*--- NULL output to prevent use by bozos who don't check errors ---*/
00162   *hZip = 0;
00163 
00164   /*--- create and open the archive ---*/
00165   nsZipArchive* zip = new nsZipArchive();
00166   if (zip == 0)
00167     return ZIP_ERR_MEMORY;
00168 
00169   status = zip->OpenArchive(zipname);
00170 
00171   if (status == ZIP_OK)
00172     *hZip = NS_STATIC_CAST(void*,zip);
00173   else
00174       delete zip;
00175 
00176   return status;
00177 }
00178 
00179 
00180 
00190 PR_PUBLIC_API(PRInt32) ZIP_TestArchive(void *hZip)
00191 {
00192   /*--- error check args ---*/
00193   if (hZip == 0)
00194     return ZIP_ERR_PARAM;
00195 
00196   nsZipArchive* zip = NS_STATIC_CAST(nsZipArchive*,hZip);
00197   if (zip->kMagic != ZIP_MAGIC)
00198     return ZIP_ERR_PARAM;   /* whatever it is isn't one of ours! */
00199 
00200   /*--- test the archive ---*/
00201   return zip->Test(NULL, zip->GetFd());
00202 }
00203 
00204 
00212 PR_PUBLIC_API(PRInt32) ZIP_CloseArchive(void** hZip)
00213 {
00214   /*--- error check args ---*/
00215   if (hZip == 0 || *hZip == 0)
00216     return ZIP_ERR_PARAM;
00217 
00218   nsZipArchive* zip = NS_STATIC_CAST(nsZipArchive*,*hZip);
00219   if (zip->kMagic != ZIP_MAGIC)
00220     return ZIP_ERR_PARAM;   /* whatever it is isn't one of ours! */
00221 
00222   /*--- close the archive ---*/
00223   *hZip = 0;
00224   delete zip;
00225 
00226   return ZIP_OK;
00227 }
00228 
00229 
00230 
00240 PR_PUBLIC_API(PRInt32) ZIP_ExtractFile(void* hZip, const char * filename, const char * outname)
00241 {
00242   /*--- error check args ---*/
00243   if (hZip == 0)
00244     return ZIP_ERR_PARAM;
00245 
00246   nsZipArchive* zip = NS_STATIC_CAST(nsZipArchive*,hZip);
00247   if (zip->kMagic != ZIP_MAGIC)
00248     return ZIP_ERR_PARAM;   /* whatever it is isn't one of ours! */
00249 
00250   /*--- extract the file ---*/
00251   return zip->ExtractFile(filename, outname, zip->GetFd());
00252 }
00253 
00254 
00255 
00265 PR_PUBLIC_API(void*) ZIP_FindInit(void* hZip, const char * pattern)
00266 {
00267   /*--- error check args ---*/
00268   if (hZip == 0)
00269     return 0;
00270 
00271   nsZipArchive* zip = NS_STATIC_CAST(nsZipArchive*,hZip);
00272   if (zip->kMagic != ZIP_MAGIC)
00273     return 0;   /* whatever it is isn't one of ours! */
00274 
00275   /*--- initialize the pattern search ---*/
00276   return zip->FindInit(pattern);
00277 }
00278 
00279 
00280 
00292 PR_PUBLIC_API(PRInt32) ZIP_FindNext(void* hFind, char * outbuf, PRUint16 bufsize)
00293 {
00294   PRInt32 status;
00295 
00296   /*--- error check args ---*/
00297   if (hFind == 0)
00298     return ZIP_ERR_PARAM;
00299 
00300   nsZipFind* find = NS_STATIC_CAST(nsZipFind*,hFind);
00301   if (find->kMagic != ZIPFIND_MAGIC)
00302     return ZIP_ERR_PARAM;   /* whatever it is isn't one of ours! */
00303 
00304   /*--- return next filename file ---*/
00305   nsZipItem* item;
00306   status = find->GetArchive()->FindNext(find, &item);
00307   if (status == ZIP_OK)
00308   {
00309     PRUint16 namelen = (PRUint16)PL_strlen(item->name);
00310 
00311     if (bufsize > namelen)
00312     {
00313         PL_strcpy(outbuf, item->name);
00314     }
00315     else
00316         status = ZIP_ERR_SMALLBUF;
00317   }
00318 
00319   return status;
00320 }
00321 
00322 
00323 
00331 PR_PUBLIC_API(PRInt32) ZIP_FindFree(void* hFind)
00332 {
00333   /*--- error check args ---*/
00334   if (hFind == 0)
00335     return ZIP_ERR_PARAM;
00336 
00337   nsZipFind* find = NS_STATIC_CAST(nsZipFind*,hFind);
00338   if (find->kMagic != ZIPFIND_MAGIC)
00339     return ZIP_ERR_PARAM;   /* whatever it is isn't one of ours! */
00340 
00341   /* free the find structure */
00342   return find->GetArchive()->FindFree(find);
00343 }
00344 
00345 #if defined XP_WIN
00346 void ProcessWindowsMessages()
00347 {
00348   MSG msg;
00349 
00350   while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
00351   {
00352     TranslateMessage(&msg);
00353     DispatchMessage(&msg);
00354   }
00355 }
00356 #endif /* XP_WIN */
00357 
00358 #else /* STANDALONE */
00359 
00360 //***********************************************************
00361 // Allocators for use with zlib
00362 //
00363 // These are allocators that are performance tuned for
00364 // use with zlib. Our use of zlib for every file we read from
00365 // the jar file when running navigator, we do these allocation.
00366 // alloc 24
00367 // alloc 64
00368 // alloc 11520
00369 // alloc 32768
00370 // alloc 1216 [304x4] max
00371 // alloc 76   [19x4]
00372 // free  76   [19x4]
00373 // alloc 1152 [288x4]
00374 // free  1152 [288x4]
00375 // free  1216 [304x4]
00376 // alloc 28
00377 // free  28
00378 // free  32768
00379 // free  11520
00380 // free  64
00381 // free  24
00382 //
00383 // The pool will allocate these as:
00384 //
00385 //          32,768
00386 //          11,520
00387 //           1,280 [320x4] - shared by first x4 alloc, 28
00388 //           1,280 [320x4] - shared by second and third x4 alloc
00389 //              64
00390 //              24
00391 //          ------
00392 //          46,936
00393 //
00394 // And almost all of the file reads happen serially. Hence this
00395 // allocator tries to keep one set of memory needed for one file around
00396 // and reused the same blocks for other file reads.
00397 //
00398 // The interesting question is when should be free this ?
00399 // - memory pressure should be one.
00400 // - after startup of navigator
00401 // - after startup of mail
00402 // In general, this allocator should be enabled before
00403 // we startup and disabled after we startup if memory is a concern.
00404 //***********************************************************
00405 
00406 PR_STATIC_CALLBACK(void *)
00407 zlibAlloc(void *opaque, uInt items, uInt size)
00408 {
00409   nsRecyclingAllocator *zallocator = (nsRecyclingAllocator *)opaque;
00410   if (zallocator) {
00411     // Bump up x4 allocations
00412     PRUint32 realitems = items;
00413     if (size == 4 && items < BY4ALLOC_ITEMS)
00414       realitems = BY4ALLOC_ITEMS;
00415      return zallocator->Calloc(realitems, size);
00416   }
00417   else
00418     return calloc(items, size);
00419 }
00420 
00421 PR_STATIC_CALLBACK(void)
00422 zlibFree(void *opaque, void *ptr)
00423 {
00424   nsRecyclingAllocator *zallocator = (nsRecyclingAllocator *)opaque;
00425   if (zallocator)
00426     zallocator->Free(ptr);
00427   else
00428     free(ptr);
00429   return;
00430 }
00431 #endif /* STANDALONE */
00432 
00433 //***********************************************************
00434 //      nsZipReadState  --  public methods
00435 //***********************************************************
00436 
00437 void nsZipReadState::Init(nsZipItem* aZipItem, PRFileDesc* aFd)
00438 {
00439     PR_ASSERT(aFd);
00440     mItem = aZipItem;
00441     mCurPos = 0;
00442 #ifndef STANDALONE
00443     // take ownership of the file descriptor
00444     mFd = aFd;
00445 #endif
00446 
00447     if (mItem->compression != STORED) {
00448       memset(&mZs, 0, sizeof(mZs));
00449 
00450 #ifndef STANDALONE
00451       //-- ensure we have our zlib allocator for better performance
00452       if (!gZlibAllocator) {
00453         gZlibAllocator = new nsRecyclingAllocator(NBUCKETS, NS_DEFAULT_RECYCLE_TIMEOUT, "libjar");
00454       }
00455 
00456       mZs.zalloc = zlibAlloc;
00457       mZs.zfree = zlibFree;
00458       mZs.opaque = gZlibAllocator;
00459 #endif
00460       int zerr = inflateInit2(&mZs, -MAX_WBITS);
00461       PR_ASSERT(zerr == Z_OK);
00462     }
00463     mCrc = crc32(0L, Z_NULL, 0);
00464 }
00465 
00466 //***********************************************************
00467 //      nsZipArchive  --  public methods
00468 //***********************************************************
00469 
00470 
00471 #ifdef STANDALONE
00472 //---------------------------------------------
00473 //  nsZipArchive::OpenArchive
00474 //---------------------------------------------
00475 PRInt32 nsZipArchive::OpenArchive(const char * aArchiveName)
00476 {
00477   //-- validate arguments
00478   if (aArchiveName == 0 || *aArchiveName == '\0')
00479     return ZIP_ERR_PARAM;
00480 
00481   //-- open the physical file
00482   mFd = PR_Open(aArchiveName, PR_RDONLY, 0000);
00483   if (mFd == 0)
00484     return ZIP_ERR_DISK;
00485 
00486   //-- get table of contents for archive
00487   return BuildFileList(GetFd());
00488 }
00489 
00490 #else
00491 PRInt32 nsZipArchive::OpenArchiveWithFileDesc(PRFileDesc* fd)
00492 {
00493   //-- validate arguments
00494   if (fd == 0)
00495     return ZIP_ERR_PARAM;
00496 
00497   //-- get table of contents for archive
00498   return BuildFileList(fd);
00499 }
00500 #endif
00501 
00502 //---------------------------------------------
00503 //  nsZipArchive::Test
00504 //---------------------------------------------
00505 PRInt32 nsZipArchive::Test(const char *aEntryName, PRFileDesc* aFd)
00506 {
00507   PRInt32 rv = ZIP_OK;
00508   nsZipItem *currItem = 0;
00509 
00510   if (aEntryName) // only test specified item
00511   {
00512     currItem = GetFileItem(aEntryName);
00513     if(!currItem)
00514       return ZIP_ERR_FNF;
00515 
00516     rv = TestItem(currItem, aFd);
00517   }
00518   else // test all items in archive
00519   {
00520     nsZipFind *iterator = FindInit(NULL);
00521     if (!iterator)
00522       return ZIP_ERR_GENERAL;
00523 
00524     // iterate over items in list
00525     while (ZIP_OK == FindNext(iterator, &currItem))
00526     {
00527       rv = TestItem(currItem, aFd);
00528 
00529       // check if crc check failed
00530       if (rv != ZIP_OK)
00531         break;
00532 
00533 #if defined STANDALONE && defined XP_WIN
00534       ProcessWindowsMessages();
00535 #endif
00536     }
00537 
00538     FindFree(iterator);
00539   }
00540 
00541   return rv;
00542 }
00543 
00544 //---------------------------------------------
00545 //  nsZipArchive::CloseArchive
00546 //---------------------------------------------
00547 PRInt32 nsZipArchive::CloseArchive()
00548 {
00549   
00550 #ifndef STANDALONE
00551   PL_FinishArenaPool(&mArena);
00552 
00553   // CAUTION:
00554   // We dont need to delete each of the nsZipItem as the memory for
00555   // the zip item and the filename it holds are both allocated from the Arena.
00556   // Hence, destroying the Arena is like destroying all the memory
00557   // for all the nsZipItem in one shot. But if the ~nsZipItem is doing
00558   // anything more than cleaning up memory, we should start calling it.
00559 
00560   memset(mFiles, 0, sizeof(mFiles));
00561 
00562 #else
00563   // delete nsZipItems in table
00564   nsZipItem* pItem;
00565   for (int i = 0; i < ZIP_TABSIZE; ++i)
00566   {
00567     pItem = mFiles[i];
00568     while (pItem != 0)
00569     {
00570       mFiles[i] = pItem->next;
00571       delete pItem;
00572       pItem = mFiles[i];
00573     }
00574     mFiles[i] = 0;              // make sure we don't double-delete
00575   }
00576   
00577   if (mFd) {
00578     PR_Close(mFd);
00579     mFd = 0;
00580   }
00581 #endif
00582 
00583   return ZIP_OK;
00584 }
00585 
00586 //---------------------------------------------
00587 // nsZipArchive::GetItem
00588 //---------------------------------------------
00589 PRInt32 nsZipArchive::GetItem(const char * aFilename, nsZipItem **result)
00590 {
00591     //-- Parameter validity check
00592     if (aFilename == 0)
00593         return ZIP_ERR_PARAM;
00594 
00595     nsZipItem* item;
00596 
00597     //-- find file information
00598     item = GetFileItem(aFilename);
00599     if (item == 0)
00600     {
00601         return ZIP_ERR_FNF;
00602     }
00603 
00604     *result = item; // Return a pointer to the struct
00605     return ZIP_OK;
00606 }
00607 
00608 //---------------------------------------------
00609 // nsZipArchive::ReadInit
00610 //---------------------------------------------
00611 PRInt32 nsZipArchive::ReadInit(const char* zipEntry, nsZipReadState* aRead,
00612                                PRFileDesc* aFd)
00613 {
00614  
00615   //-- Parameter validity check
00616   if (zipEntry == 0 || aRead == 0)
00617     return ZIP_ERR_PARAM;
00618 
00619   //-- find item
00620   nsZipItem* item = GetFileItem(zipEntry);
00621   if (!item) {
00622     PR_Close(aFd);
00623     return ZIP_ERR_FNF;
00624   }
00625 
00626   //-- verify we can handle the compression type
00627   if (item->compression != DEFLATED && item->compression != STORED) {
00628     PR_Close(aFd);
00629     return ZIP_ERR_UNSUPPORTED;
00630   }
00631 
00632   SeekToItem(item, aFd);
00633 
00634 #ifdef STANDALONE
00635   // in standalone, nsZipArchive owns the file descriptor
00636   mFd = aFd;
00637 #endif
00638   
00639   // in non-standalone builds, the nsZipReadState will take ownership
00640   // of the file descriptor
00641   aRead->Init(item, aFd);
00642     
00643   return ZIP_OK;
00644 }
00645 
00646 //---------------------------------------------
00647 // nsZipReadState::Available
00648 //---------------------------------------------
00649 PRUint32 nsZipReadState::Available()
00650 {
00651   if (mItem->compression == DEFLATED)
00652     return (mItem->realsize - mZs.total_out);
00653 
00654   return mItem->size - mCurPos;
00655 }
00656 
00657 //---------------------------------------------
00658 // nsZipArchive::ExtractFile
00659 //---------------------------------------------
00660 PRInt32 nsZipArchive::ExtractFile(const char* zipEntry, const char* aOutname,
00661                                   PRFileDesc* aFd)
00662 {
00663   //-- Find item in archive
00664   nsZipItem* item = GetFileItem(zipEntry);
00665   if (!item)
00666     return ZIP_ERR_FNF;
00667 
00668   // delete any existing file so that we overwrite the file permissions
00669   PR_Delete(aOutname);
00670 
00671   PRFileDesc* fOut = PR_Open(aOutname, ZFILE_CREATE, item->mode);
00672   if (fOut == 0)
00673     return ZIP_ERR_DISK;
00674 
00675 #if defined(XP_UNIX) && defined(STANDALONE)
00676   // When STANDALONE is defined, PR_Open ignores its 3d argument.
00677   mode_t msk = umask(0);
00678   umask(msk);
00679   chmod(aOutname, item->mode & ~msk);
00680 #endif
00681 
00682   PRInt32 status = ExtractItemToFileDesc(item, fOut, aFd);
00683   PR_Close(fOut);
00684 
00685   if (status != ZIP_OK)
00686   {
00687     PR_Delete(aOutname);
00688   }
00689 #if defined(XP_UNIX)
00690   else
00691   {
00692     if (item->isSymlink)
00693     {
00694       status = ResolveSymlink(aOutname, item);
00695     }
00696   }
00697 #endif
00698   return status;
00699 }
00700 
00701 PRInt32
00702 nsZipArchive::ExtractItemToFileDesc(nsZipItem* item, PRFileDesc* outFD,
00703                                     PRFileDesc* aFd)
00704 {
00705   //-- sanity check arguments
00706   if (item == 0 || outFD == 0)
00707     return ZIP_ERR_PARAM;
00708 
00709   PRInt32 status;
00710 
00711   //-- extract the file using the appropriate method
00712   switch(item->compression)
00713   {
00714     case STORED:
00715       status = CopyItemToDisk(item, outFD, aFd);
00716       break;
00717 
00718     case DEFLATED:
00719       status = InflateItem(item, outFD, aFd);
00720       break;
00721 
00722     default:
00723       //-- unsupported compression type
00724       return ZIP_ERR_UNSUPPORTED;
00725   }
00726 
00727   return status;
00728 }
00729 
00730 //---------------------------------------------
00731 // nsZipArchive::FindInit
00732 //---------------------------------------------
00733 nsZipFind* nsZipArchive::FindInit(const char * aPattern)
00734 {
00735   PRBool  regExp = PR_FALSE;
00736   char*   pattern = 0;
00737 
00738   // validate the pattern
00739   if (aPattern)
00740   {
00741     switch (NS_WildCardValid((char*)aPattern))
00742     {
00743       case INVALID_SXP:
00744         return 0;
00745 
00746       case NON_SXP:
00747         regExp = PR_FALSE;
00748         break;
00749 
00750       case VALID_SXP:
00751         regExp = PR_TRUE;
00752         break;
00753 
00754       default:
00755         // undocumented return value from RegExpValid!
00756         PR_ASSERT(PR_FALSE);
00757         return 0;
00758     }
00759 
00760     pattern = PL_strdup(aPattern);
00761     if (!pattern)
00762       return 0;
00763   }
00764 
00765   return new nsZipFind(this, pattern, regExp);
00766 }
00767 
00768 
00769 
00770 //---------------------------------------------
00771 // nsZipArchive::FindNext
00772 //---------------------------------------------
00773 PRInt32 nsZipArchive::FindNext(nsZipFind* aFind, nsZipItem** aResult)
00774 {
00775   PRInt32    status;
00776   PRBool     found  = PR_FALSE;
00777   PRUint16   slot   = aFind->mSlot;
00778   nsZipItem* item   = aFind->mItem;
00779 
00780   if (aFind->mArchive != this)
00781     return ZIP_ERR_PARAM;
00782 
00783   // we start from last match, look for next
00784   while (slot < ZIP_TABSIZE && !found)
00785   {
00786     if (item != 0)
00787       item = item->next;    // move to next in current chain
00788     else
00789       item = mFiles[slot];  // starting a new slot
00790 
00791     if (item == 0)
00792     { // no more in this chain, move to next slot
00793       ++slot;
00794       continue;
00795     }
00796     else if (aFind->mPattern == 0)
00797       found = PR_TRUE;            // always match
00798     else if (aFind->mRegExp)
00799       found = (NS_WildCardMatch(item->name, aFind->mPattern, PR_FALSE) == MATCH);
00800     else
00801 #if defined(STANDALONE) && defined(XP_MAC)
00802       // simulate <regexp>* matches
00803       found = (strncmp(item->name, aFind->mPattern, strlen(aFind->mPattern)) == 0);
00804 #else
00805       found = (PL_strcmp(item->name, aFind->mPattern) == 0);
00806 #endif
00807   }
00808 
00809   if (found)
00810   {
00811       *aResult = item;
00812       aFind->mSlot = slot;
00813       aFind->mItem = item;
00814       status = ZIP_OK;
00815   }
00816   else
00817     status = ZIP_ERR_FNF;
00818 
00819   return status;
00820 }
00821 
00822 
00823 
00824 //---------------------------------------------
00825 // nsZipArchive::FindFree
00826 //---------------------------------------------
00827 PRInt32 nsZipArchive::FindFree(nsZipFind* aFind)
00828 {
00829   if (aFind->mArchive != this)
00830     return ZIP_ERR_PARAM;
00831 
00832   delete aFind;
00833   return ZIP_OK;
00834 }
00835 
00836 #ifdef XP_UNIX
00837 //---------------------------------------------
00838 // nsZipArchive::ResolveSymlink
00839 //---------------------------------------------
00840 PRInt32 nsZipArchive::ResolveSymlink(const char *path, nsZipItem *item)
00841 {
00842   PRInt32    status = ZIP_OK;
00843   if (item->isSymlink)
00844   {
00845     char buf[PATH_MAX+1];
00846     PRFileDesc * fIn = PR_Open(path, PR_RDONLY, 0000);
00847     if (fIn)
00848     {
00849       PRInt32 length = PATH_MAX;
00850       length = PR_Read(fIn,(void*)buf,length);
00851       PR_Close(fIn);
00852       fIn = 0;
00853       if (  length <= 0
00854       || (buf[length] = 0, PR_Delete(path)) != 0
00855       || symlink(buf, path) != 0)
00856       {
00857         status = ZIP_ERR_DISK;
00858       }
00859     } else {
00860       status = ZIP_ERR_DISK;
00861     }
00862     if (fIn)
00863     {
00864       PR_Close(fIn);
00865     }
00866   }
00867   return status;
00868 }
00869 #endif
00870 
00871 //***********************************************************
00872 //      nsZipArchive  --  private implementation
00873 //***********************************************************
00874 
00875 #define BR_BUF_SIZE 1024 /* backward read buffer size */
00876 
00877 //---------------------------------------------
00878 //  nsZipArchive::BuildFileList
00879 //---------------------------------------------
00880 PRInt32 nsZipArchive::BuildFileList(PRFileDesc* aFd)
00881 {
00882   PRInt32   status = ZIP_OK;
00883   PRUint8   buf[4*BR_BUF_SIZE];
00884 
00885   ZipEnd     *End;
00886 
00887   //-----------------------------------------------------------------------
00888   // locate the central directory via the End record
00889   //-----------------------------------------------------------------------
00890   PRInt32  pos = 0L;
00891   PRInt32  bufsize = 0;
00892 
00893   //-- get archive size using end pos
00894   pos = PR_Seek(aFd, 0, PR_SEEK_END);
00895 #ifndef STANDALONE
00896   if (pos <= 0)
00897 #else
00898   if (pos || ((pos = ftell(aFd)) <= 0))
00899 #endif
00900     status = ZIP_ERR_CORRUPT;
00901 
00902   while (status == ZIP_OK)
00903   {
00904     //-- read backwards in 1K-sized chunks (unless file is less than 1K)
00905     pos > BR_BUF_SIZE ? bufsize = BR_BUF_SIZE : bufsize = pos;
00906     pos -= bufsize;
00907 
00908     if (!ZIP_Seek(aFd, pos, PR_SEEK_SET))
00909     {
00910       status = ZIP_ERR_CORRUPT;
00911       break;
00912     }
00913 
00914     if (PR_Read(aFd, buf, bufsize) != (READTYPE)bufsize)
00915     {
00916       status = ZIP_ERR_CORRUPT;
00917       break;
00918     }
00919 
00920     //-- scan for ENDSIG
00921     PRUint8 *endp = buf + bufsize;
00922     PRUint32 endsig;
00923     PRBool bEndsigFound = PR_FALSE;
00924 
00925     for (endp -= ZIPEND_SIZE; endp >= buf; endp--)
00926     {
00927       endsig = xtolong(endp);
00928       if (endsig == ENDSIG)
00929       {
00930         bEndsigFound = PR_TRUE;
00931         break;
00932       }
00933     }
00934 
00935     if (bEndsigFound)
00936     {
00937       End = (ZipEnd *) endp;
00938 
00939       //-- set pos to start of central directory
00940       pos = xtolong(End->offset_central_dir);
00941       if (!ZIP_Seek(aFd, pos, PR_SEEK_SET))
00942         status = ZIP_ERR_CORRUPT;
00943 
00944       break;
00945     }
00946 
00947     if(pos <= 0)
00948       //-- We're at the beginning of the file, and still no sign
00949       //-- of the end signiture.  File must be corrupted!
00950       status = ZIP_ERR_CORRUPT;
00951 
00952     //-- backward read must overlap ZipEnd length
00953     pos += ZIPEND_SIZE;
00954 
00955   } /* while looking for end signature */
00956 
00957 
00958   //-------------------------------------------------------
00959   // read the central directory headers
00960   //-------------------------------------------------------
00961   if (status == ZIP_OK)
00962   {
00963     //-- we think we found the central directory, read in the first chunk
00964     pos = 0;
00965     bufsize = PR_Read(aFd, &buf, sizeof(buf));
00966     if (bufsize < (PRInt32)(ZIPCENTRAL_SIZE + ZIPEND_SIZE))
00967     {
00968       // We know we read the end sig and got pointed at the central
00969       // directory--there should be at least this much
00970       //
00971       // (technically there can be a completely empty archive with only a
00972       // ZipEnd structure; that's pointless and might as well be an error.)
00973       status = ZIP_ERR_DISK;
00974     }
00975 
00976     //-- verify it's a central directory record
00977     if (xtolong(buf) != CENTRALSIG)
00978       status = ZIP_ERR_CORRUPT;
00979   }
00980 
00981   //-- loop through directory records
00982   //
00983   //-- we enter the loop positioned at a central directory record
00984   //-- with enough valid data in the buffer to contain one
00985   while (status == ZIP_OK)
00986   {
00987     //-------------------------------------------------------
00988     // read the fixed-size data
00989     //-------------------------------------------------------
00990     ZipCentral* central = (ZipCentral*)(buf+pos);
00991 
00992     PRUint32 namelen = xtoint(central->filename_len);
00993     PRUint32 extralen = xtoint(central->extrafield_len);
00994     PRUint32 commentlen = xtoint(central->commentfield_len);
00995 
00996     //-- sanity check variable sizes and refuse to deal with
00997     //-- anything too big: it's likely a corrupt archive
00998     if (namelen > BR_BUF_SIZE || extralen > BR_BUF_SIZE || commentlen > 2*BR_BUF_SIZE) {
00999       status = ZIP_ERR_CORRUPT;
01000       break;
01001     }
01002 
01003 #ifndef STANDALONE
01004     // Arena allocate the nsZipItem
01005     void *mem;
01006     PL_ARENA_ALLOCATE(mem, &mArena, sizeof(nsZipItem));
01007     // Use placement new to arena allcoate the nsZipItem
01008     nsZipItem* item = mem ? new (mem) nsZipItem() : nsnull;
01009 #else
01010     nsZipItem* item = new nsZipItem();
01011 #endif
01012     if (!item)
01013     {
01014       status = ZIP_ERR_MEMORY;
01015       break;
01016     }
01017 
01018     item->headerOffset = xtolong(central->localhdr_offset);
01019     item->dataOffset = 0;
01020     item->hasDataOffset = PR_FALSE;
01021     item->size = xtolong(central->size);
01022     item->realsize = xtolong(central->orglen);
01023     item->crc32 = xtolong(central->crc32);
01024     PRUint32 external_attributes = xtolong(central->external_attributes);
01025     item->mode = ExtractMode(external_attributes);
01026     item->isSymlink = IsSymlink(external_attributes);
01027     item->time = xtoint(central->time);
01028     item->date = xtoint(central->date);
01029 
01030     PRUint16 compression = xtoint(central->method);
01031     item->compression = (compression < UNSUPPORTED) ? (PRUint8)compression
01032                                                     : UNSUPPORTED;
01033 
01034     pos += ZIPCENTRAL_SIZE;
01035 
01036     //-------------------------------------------------------
01037     // get the item name
01038     //-------------------------------------------------------
01039 #ifndef STANDALONE
01040     PL_ARENA_ALLOCATE(mem, &mArena, (namelen + 1));
01041     item->name = (char *) mem;
01042     if (!item->name)
01043     {
01044       status = ZIP_ERR_MEMORY;
01045       // No need to delete name. It gets deleted only when the entire arena
01046       // goes away.
01047       break;
01048     }
01049 #else
01050     item->name = new char[namelen + 1];
01051     if (!item->name)
01052     {
01053       status = ZIP_ERR_MEMORY;
01054       delete item;
01055       break;
01056     }
01057 #endif
01058 
01059     PRUint32 leftover = (PRUint32)(bufsize - pos);
01060     if (leftover < namelen)
01061     {
01062       //-- not enough data left in buffer for the name.
01063       //-- move leftover to top of buffer and read more
01064       memcpy(buf, buf+pos, leftover);
01065       bufsize = leftover + PR_Read(aFd, buf+leftover, bufsize-leftover);
01066       pos = 0;
01067 
01068       //-- make sure we were able to read enough
01069       if ((PRUint32)bufsize < namelen)
01070       {
01071         status = ZIP_ERR_CORRUPT;
01072         break;
01073       }
01074     }
01075     memcpy(item->name, buf+pos, namelen);
01076     item->name[namelen] = 0;
01077 
01078     //-- add item to file table
01079     PRUint32 hash = HashName(item->name);
01080     item->next = mFiles[hash];
01081     mFiles[hash] = item;
01082 
01083     pos += namelen;
01084 
01085     //-------------------------------------------------------
01086     // set up to process the next item at the top of loop
01087     //-------------------------------------------------------
01088     leftover = (PRUint32)(bufsize - pos);
01089     if (leftover < (extralen + commentlen + ZIPCENTRAL_SIZE))
01090     {
01091       //-- not enough data left to process at top of loop.
01092       //-- move leftover and read more
01093       memcpy(buf, buf+pos, leftover);
01094       bufsize = leftover + PR_Read(aFd, buf+leftover, bufsize-leftover);
01095       pos = 0;
01096 
01097       //-- make sure we were able to read enough
01098       if ((PRUint32)bufsize < (extralen + commentlen + sizeof(PRUint32)))
01099       {
01100         status = ZIP_ERR_CORRUPT;
01101         break;
01102       }
01103     }
01104     //-- set position to start of next ZipCentral record
01105     pos += extralen + commentlen;
01106 
01107     // verify we're at an expected structure in the file
01108     PRUint32 sig = xtolong(buf+pos);
01109     if (sig != CENTRALSIG)
01110     {
01111       //-- we must be done or else archive is corrupt
01112       if (sig != ENDSIG)
01113         status = ZIP_ERR_CORRUPT;
01114       break;
01115     }
01116 
01117     //-- make sure we've read enough
01118     if (bufsize < pos + ZIPCENTRAL_SIZE)
01119     {
01120       status = ZIP_ERR_CORRUPT;
01121       break;
01122     }
01123   } /* while reading central directory records */
01124 
01125 #if defined(DEBUG)
01126   if (status != ZIP_OK) {
01127     const char* msgs[] = { "ZIP_OK", "ZIP_ERR_GENERAL", "ZIP_ERR_MEMORY", "ZIP_ERR_DISK", "ZIP_ERR_CORRUPT", "ZIP_ERR_PARAM", "ZIP_ERR_FNF", "ZIP_ERR_UNSUPPORTED", "ZIP_ERR_SMALLBUF", "UNKNOWN" };
01128     printf("nsZipArchive::BuildFileList  status = %d '%s'\n", (int)status, msgs[(status <= 0 && status >= -8) ? -status : 9]);
01129   }
01130 #endif
01131 
01132   return status;
01133 }
01134 
01135 //---------------------------------------------
01136 // nsZipArchive::GetFileItem
01137 //---------------------------------------------
01138 nsZipItem*  nsZipArchive::GetFileItem(const char * zipEntry)
01139 {
01140   PR_ASSERT(zipEntry != 0);
01141 
01142   nsZipItem* item = mFiles[ HashName(zipEntry) ];
01143 
01144   for (; item != 0; item = item->next)
01145   {
01146     if (0 == PL_strcmp(zipEntry, item->name))
01147       break; //-- found it
01148   }
01149 
01150   return item;
01151 }
01152 
01153 
01154 //---------------------------------------------
01155 // nsZipArchive::HashName
01156 //---------------------------------------------
01157 PRUint32 nsZipArchive::HashName(const char* aName)
01158 {
01159   PRUint32 val = 0;
01160   PRUint8* c;
01161 
01162   PR_ASSERT(aName != 0);
01163 
01164   for (c = (PRUint8*)aName; *c != 0; c++) {
01165     val = val*37 + *c;
01166   }
01167 
01168   return (val % ZIP_TABSIZE);
01169 }
01170 
01171 //---------------------------------------------
01172 // nsZipArchive::SeekToItem
01173 //---------------------------------------------
01174 PRInt32  nsZipArchive::SeekToItem(const nsZipItem* aItem, PRFileDesc* aFd)
01175 {
01176   PR_ASSERT (aItem);
01177 
01178   PRFileDesc* fd = aFd;
01179   
01180   //-- the first time an item is used we need to calculate its offset
01181   if (!aItem->hasDataOffset)
01182   {
01183     //-- read local header to get variable length values and calculate
01184     //-- the real data offset
01185     //--
01186     //-- NOTE: extralen is different in central header and local header
01187     //--       for archives created using the Unix "zip" utility. To set
01188     //--       the offset accurately we need the _local_ extralen.
01189     if (!ZIP_Seek(fd, aItem->headerOffset, PR_SEEK_SET))
01190       return ZIP_ERR_CORRUPT;
01191 
01192     ZipLocal   Local;
01193     if (PR_Read(fd, (char*)&Local, ZIPLOCAL_SIZE) != (READTYPE) ZIPLOCAL_SIZE
01194          || xtolong(Local.signature) != LOCALSIG)
01195     {
01196       //-- read error or local header not found
01197       return ZIP_ERR_CORRUPT;
01198     }
01199 
01200     ((nsZipItem*)aItem)->dataOffset = aItem->headerOffset +
01201                                       ZIPLOCAL_SIZE +
01202                                       xtoint(Local.filename_len) +
01203                                       xtoint(Local.extrafield_len);
01204     ((nsZipItem*)aItem)->hasDataOffset = PR_TRUE;
01205   }
01206 
01207   //-- move to start of file in archive
01208   if (!ZIP_Seek(fd, aItem->dataOffset, PR_SEEK_SET))
01209     return  ZIP_ERR_CORRUPT;
01210 
01211   return ZIP_OK;
01212 }
01213 
01214 //---------------------------------------------
01215 // nsZipArchive::CopyItemToDisk
01216 //---------------------------------------------
01217 PRInt32
01218 nsZipArchive::CopyItemToDisk(const nsZipItem* aItem,
01219                              PRFileDesc* fOut, PRFileDesc* aFd)
01220 {
01221   PRInt32     status = ZIP_OK;
01222   PRUint32    chunk, pos, size, crc;
01223 
01224   PR_ASSERT(aItem != 0 && fOut != 0);
01225 
01226   PRFileDesc* fd = aFd;
01227   
01228   //-- move to the start of file's data
01229   if (SeekToItem(aItem, aFd) != ZIP_OK)
01230     return ZIP_ERR_CORRUPT;
01231 
01232   char buf[ZIP_BUFLEN];
01233 
01234   //-- initialize crc
01235   crc = crc32(0L, Z_NULL, 0);
01236 
01237   //-- copy chunks until file is done
01238   size = aItem->size;
01239   for (pos=0; pos < size; pos += chunk)
01240   {
01241     chunk = (pos+ZIP_BUFLEN <= size) ? ZIP_BUFLEN : size - pos;
01242 
01243     if (PR_Read(fd, buf, chunk) != (READTYPE)chunk)
01244     {
01245       //-- unexpected end of data in archive
01246       status = ZIP_ERR_CORRUPT;
01247       break;
01248     }
01249 
01250     //-- incrementally update crc32
01251     crc = crc32(crc, (const unsigned char*)buf, chunk);
01252 
01253     if (PR_Write(fOut, buf, chunk) < (READTYPE)chunk)
01254     {
01255       //-- Couldn't write all the data (disk full?)
01256       status = ZIP_ERR_DISK;
01257       break;
01258     }
01259   }
01260 
01261   //-- verify crc32
01262   if ((status == ZIP_OK) && (crc != aItem->crc32))
01263       status = ZIP_ERR_CORRUPT;
01264 
01265   return status;
01266 }
01267 
01268 #ifndef STANDALONE
01269 //------------------------------------------
01270 // nsZipArchive::Read
01271 //------------------------------------------
01272 PRInt32
01273 nsZipReadState::Read(char* aBuffer, PRUint32 aCount,
01274                      PRUint32* aBytesRead)
01275 {
01276   if (!aBuffer)
01277     return ZIP_ERR_GENERAL;
01278 
01279   PRInt32 result;
01280 
01281   if (!Available()) {
01282     *aBytesRead = 0;
01283     return ZIP_OK;
01284   }
01285 
01286   switch (mItem->compression) {
01287   case DEFLATED:
01288     result = ContinueInflate(aBuffer, aCount, aBytesRead);
01289     break;
01290   case STORED:
01291     result = ContinueCopy(aBuffer, aCount, aBytesRead);
01292     break;
01293   default:
01294     result = ZIP_ERR_UNSUPPORTED;
01295   }
01296 
01297   // be agressive about closing!
01298   // note that sometimes, we will close mFd before we've finished
01299   // deflating - this is because zlib buffers the input
01300   if (mCurPos >= mItem->size && mFd) {
01301     PR_Close(mFd);
01302     mFd = NULL;
01303   }
01304 
01305   return result;
01306 }
01307 
01308 PRInt32
01309 nsZipReadState::ContinueInflate(char* aBuffer, PRUint32 aCount,
01310                                 PRUint32* aBytesRead)
01311 {
01312 
01313   // just some stuff that will be helpful later
01314   const PRUint32 inSize = mItem->size;
01315   const PRUint32 outSize = mItem->realsize;
01316 
01317   int zerr = Z_OK;
01318   //-- inflate loop
01319 
01320   const PRUint32 oldTotalOut = mZs.total_out;
01321 
01322   mZs.next_out = (unsigned char*)aBuffer;
01323   mZs.avail_out = ((mZs.total_out + aCount) < outSize) ?
01324     aCount : (outSize - mZs.total_out);
01325 
01326   // make sure we aren't reading too much
01327   PR_ASSERT(mZs.avail_out <= aCount);
01328   
01329   *aBytesRead = 0;
01330   while (mZs.avail_out != 0 && zerr == Z_OK) {
01331     
01332     if (mZs.avail_in == 0 && mCurPos < inSize) {
01333       // time to fill the buffer!
01334       PRUint32 bytesToRead = ((mCurPos + ZIP_BUFLEN) < inSize) ?
01335         ZIP_BUFLEN : inSize - mCurPos;
01336 
01337       PR_ASSERT(mFd);
01338       PRInt32 bytesRead = PR_Read(mFd, mReadBuf, bytesToRead);
01339       if (bytesRead < 0) {
01340         zerr = Z_ERRNO;
01341         break;
01342       }
01343 
01344       mCrc = crc32(mCrc, mReadBuf, bytesRead);
01345       mCurPos += bytesRead;
01346 
01347       // now reset the state
01348       mZs.next_in = mReadBuf;
01349       mZs.avail_in = bytesRead;
01350     }
01351 
01352 #if 0
01353     // stop returning valid data as soon as we know we have a bad CRC
01354     if (mCurPos >= inSize &&
01355         mCrc != mItem->crc32) {
01356       // asserting because while this rarely happens, you definitely
01357       // want to catch it in debug builds!
01358       PR_ASSERT(0);
01359       return ZIP_ERR_CORRUPT;
01360     }
01361 #endif
01362     
01363     // now inflate
01364     zerr = inflate(&mZs, Z_PARTIAL_FLUSH);
01365   }
01366 
01367   if ((zerr != Z_OK) && (zerr != Z_STREAM_END))
01368     return ZIP_ERR_CORRUPT;
01369   
01370   *aBytesRead = (mZs.total_out - oldTotalOut);
01371 
01372   // be aggressive about closing the stream
01373   // for some reason we don't always get Z_STREAM_END
01374   if (zerr == Z_STREAM_END || mZs.total_out == mItem->realsize) {
01375     inflateEnd(&mZs);
01376   }
01377 
01378   return ZIP_OK;
01379 }
01380 
01381 PRInt32 nsZipReadState::ContinueCopy(char* aBuf,
01382                                      PRUint32 aCount,
01383                                      PRUint32* aBytesRead)
01384 {
01385   // we still use the fields of mZs, we just use memcpy rather than inflate
01386 
01387   if (mCurPos + aCount > mItem->size)
01388     aCount = (mItem->size - mCurPos);
01389 
01390   PR_ASSERT(mFd);
01391   PRInt32 bytesRead = PR_Read(mFd, aBuf, aCount);
01392   if (bytesRead < 0)
01393     return ZIP_ERR_DISK;
01394 
01395   mCurPos += bytesRead;
01396   if (bytesRead != aCount)
01397     // either file was truncated or archive lied about size
01398     return ZIP_ERR_CORRUPT;
01399 
01400   *aBytesRead = bytesRead;
01401 
01402   return ZIP_OK;
01403 }
01404 
01405 #endif
01406 
01407 //---------------------------------------------
01408 // nsZipArchive::InflateItem
01409 //---------------------------------------------
01410 PRInt32 nsZipArchive::InflateItem(const nsZipItem* aItem, PRFileDesc* fOut, PRFileDesc* aFd)
01411 /*
01412  * This function either inflates an archive item to disk, to the
01413  * file specified by aOutname, or into a buffer specified by
01414  * bigBuf. bigBuf then gets copied into the "real" output
01415  * buffer a chunk at a time by ReadInflatedItem(). Memory-wise,
01416  * this is inefficient, since it stores an entire copy of the
01417  * decompressed item in memory, then copies it to the caller's
01418  * buffer. A more memory-efficient implementation is possible,
01419  * in which the outbuf in this function could be returned
01420  * directly, but implementing it would be complex.
01421  */
01422 {
01423   PRInt32     status = ZIP_OK;
01424   PRUint32    chunk, inpos, outpos, size, crc;
01425   PRUint32    bigBufSize=0;
01426   z_stream    zs;
01427   int         zerr;
01428   PRBool      bInflating = PR_FALSE;
01429   PRBool      bRead;
01430   PRBool      bWrote;
01431   PRBool      bToFile;
01432   Bytef*      old_next_out;
01433 
01434   // -- if aOutname is null, we'll be writing to a buffer instead of a file
01435   if (fOut != 0)
01436   {
01437     PR_ASSERT(aItem != 0);
01438     bToFile = PR_TRUE;
01439   }
01440   else
01441   {
01442     // -- Writing to a buffer, so bigBuf must not be null.
01443     PR_ASSERT(aItem);
01444     bToFile = PR_FALSE;
01445     bigBufSize = aItem->realsize;
01446   }
01447 
01448   //-- move to the start of file's data
01449   if (SeekToItem(aItem, aFd) != ZIP_OK)
01450     return ZIP_ERR_CORRUPT;
01451 
01452   //-- allocate deflation buffers
01453   Bytef inbuf[ZIP_BUFLEN];
01454   Bytef outbuf[ZIP_BUFLEN];
01455 
01456   //-- set up the inflate
01457   memset(&zs, 0, sizeof(zs));
01458 #ifndef STANDALONE
01459   //-- ensure we have our zlib allocator for better performance
01460   if (!gZlibAllocator) {
01461     gZlibAllocator = new nsRecyclingAllocator(NBUCKETS, NS_DEFAULT_RECYCLE_TIMEOUT, "libjar");
01462   }
01463 
01464   zs.zalloc = zlibAlloc;
01465   zs.zfree = zlibFree;
01466   zs.opaque = gZlibAllocator;
01467 #endif
01468 
01469   zerr = inflateInit2(&zs, -MAX_WBITS);
01470   if (zerr != Z_OK)
01471   {
01472     status = ZIP_ERR_GENERAL;
01473     goto cleanup;
01474   }
01475   bInflating = PR_TRUE;
01476 
01477 
01478   //-- inflate loop
01479   size = aItem->size;
01480   outpos = inpos = 0;
01481   zs.next_out = outbuf;
01482   zs.avail_out = ZIP_BUFLEN;
01483   crc = crc32(0L, Z_NULL, 0);
01484   while (zerr == Z_OK)
01485   {
01486     bRead  = PR_FALSE;
01487     bWrote = PR_FALSE;
01488 
01489     if (zs.avail_in == 0 && zs.total_in < size)
01490     {
01491       //-- no data to inflate yet still more in file:
01492       //-- read another chunk of compressed data
01493 
01494       inpos = zs.total_in;  // input position
01495       chunk = (inpos + ZIP_BUFLEN <= size) ? ZIP_BUFLEN : size - inpos;
01496 
01497       if (PR_Read(aFd, inbuf, chunk) != (READTYPE)chunk)
01498       {
01499         //-- unexpected end of data
01500         status = ZIP_ERR_CORRUPT;
01501         break;
01502       }
01503 
01504       zs.next_in  = inbuf;
01505       zs.avail_in = chunk;
01506       bRead       = PR_TRUE;
01507     }
01508 
01509     if (zs.avail_out == 0)
01510     {
01511       //-- write inflated buffer to disk and make space
01512       if (PR_Write(fOut, outbuf, ZIP_BUFLEN) < ZIP_BUFLEN)
01513       {
01514         //-- Couldn't write all the data (disk full?)
01515         status = ZIP_ERR_DISK;
01516         break;
01517       }
01518 
01519       outpos = zs.total_out;
01520       zs.next_out  = outbuf;
01521       zs.avail_out = ZIP_BUFLEN;
01522       bWrote       = PR_TRUE;
01523     }
01524 
01525     if(bRead || bWrote)
01526     {
01527       old_next_out = zs.next_out;
01528 
01529       zerr = inflate(&zs, Z_PARTIAL_FLUSH);
01530 
01531       //-- incrementally update crc32
01532       crc = crc32(crc, (const unsigned char*)old_next_out, zs.next_out - old_next_out);
01533     }
01534     else
01535       zerr = Z_STREAM_END;
01536 
01537 #if defined STANDALONE && defined XP_WIN
01538     ProcessWindowsMessages();
01539 #endif
01540   } // while
01541 
01542   //-- verify crc32
01543   if ((status == ZIP_OK) && (crc != aItem->crc32))
01544   {
01545       status = ZIP_ERR_CORRUPT;
01546       goto cleanup;
01547   }
01548 
01549   //-- write last inflated bit to disk
01550   if (zerr == Z_STREAM_END && outpos < zs.total_out)
01551   {
01552     chunk = zs.total_out - outpos;
01553     if (PR_Write(fOut, outbuf, chunk) < (READTYPE)chunk)
01554       status = ZIP_ERR_DISK;
01555   }
01556 
01557   //-- convert zlib error to return value
01558   if (status == ZIP_OK && zerr != Z_OK && zerr != Z_STREAM_END)
01559   {
01560     status = (zerr == Z_MEM_ERROR) ? ZIP_ERR_MEMORY : ZIP_ERR_CORRUPT;
01561   }
01562 
01563   //-- if found no errors make sure we've converted the whole thing
01564   PR_ASSERT(status != ZIP_OK || zs.total_in == aItem->size);
01565   PR_ASSERT(status != ZIP_OK || zs.total_out == aItem->realsize);
01566 
01567 cleanup:
01568   if (bInflating)
01569   {
01570     //-- free zlib internal state
01571     inflateEnd(&zs);
01572   }
01573 
01574   return status;
01575 }
01576 
01577 //---------------------------------------------
01578 // nsZipArchive::TestItem
01579 //---------------------------------------------
01580 PRInt32 nsZipArchive::TestItem(const nsZipItem* aItem, PRFileDesc* aFd)
01581 {
01582   Bytef inbuf[ZIP_BUFLEN], outbuf[ZIP_BUFLEN], *old_next_out;
01583   PRUint32 size, chunk=0, inpos, crc;
01584   PRInt32 status = ZIP_OK;
01585   int zerr = Z_OK;
01586   z_stream zs;
01587   PRBool bInflating = PR_FALSE;
01588   PRBool bRead;
01589   PRBool bWrote;
01590 
01591   //-- param checks
01592   if (!aItem)
01593     return ZIP_ERR_PARAM;
01594   if (aItem->compression != STORED && aItem->compression != DEFLATED)
01595     return ZIP_ERR_UNSUPPORTED;
01596 
01597   //-- move to the start of file's data
01598   if (SeekToItem(aItem, aFd) != ZIP_OK)
01599     return ZIP_ERR_CORRUPT;
01600 
01601   //-- set up the inflate if DEFLATED
01602   if (aItem->compression == DEFLATED)
01603   {
01604     memset(&zs, 0, sizeof(zs));
01605     zerr = inflateInit2(&zs, -MAX_WBITS);
01606     if (zerr != Z_OK)
01607     {
01608       status = ZIP_ERR_GENERAL;
01609       goto cleanup;
01610     }
01611     else
01612     {
01613       zs.next_out = outbuf;
01614       zs.avail_out = ZIP_BUFLEN;
01615     }
01616     bInflating = PR_TRUE;
01617   }
01618 
01619   //-- initialize crc checksum
01620   crc = crc32(0L, Z_NULL, 0);
01621 
01622   size = aItem->size;
01623   inpos = 0;
01624 
01625   //-- read in ZIP_BUFLEN-sized chunks of item
01626   //-- inflating if item is DEFLATED
01627   while (zerr == Z_OK)
01628   {
01629     bRead = PR_FALSE;  // used to check if new data to inflate
01630     bWrote = PR_FALSE; // used to reset zs.next_out to outbuf
01631                        //   when outbuf fills up
01632 
01633     //-- read to inbuf
01634     if (aItem->compression == DEFLATED)
01635     {
01636       if (zs.avail_in == 0 && zs.total_in < size)
01637       {
01638         //-- no data to inflate yet still more in file:
01639         //-- read another chunk of compressed data
01640 
01641         inpos = zs.total_in;  // input position
01642         chunk = (inpos + ZIP_BUFLEN <= size) ? ZIP_BUFLEN : size - inpos;
01643 
01644         if (PR_Read(aFd, inbuf, chunk) != (READTYPE)chunk)
01645         {
01646           //-- unexpected end of data
01647           status = ZIP_ERR_CORRUPT;
01648           break;
01649         }
01650 
01651         zs.next_in  = inbuf;
01652         zs.avail_in = chunk;
01653         bRead = PR_TRUE;
01654       }
01655 
01656       if (zs.avail_out == 0)
01657       {
01658         //-- reuse output buffer
01659         zs.next_out = outbuf;
01660         zs.avail_out = ZIP_BUFLEN;
01661         bWrote = PR_TRUE; // mimic writing to disk/memory
01662       }
01663     }
01664     else
01665     {
01666       if (inpos < size)
01667       {
01668         //-- read a chunk in
01669         chunk = (inpos + ZIP_BUFLEN <= size) ? ZIP_BUFLEN : size - inpos;
01670 
01671         if (PR_Read(aFd, inbuf, chunk) != (READTYPE)chunk)
01672         {
01673           //-- unexpected end of data
01674           status = ZIP_ERR_CORRUPT;
01675           break;
01676         }
01677 
01678         inpos += chunk;
01679       }
01680       else
01681       {
01682         //-- finished reading STORED item
01683         break;
01684       }
01685     }
01686 
01687     //-- inflate if item is DEFLATED
01688     if (aItem->compression == DEFLATED)
01689     {
01690       if (bRead || bWrote)
01691       {
01692         old_next_out = zs.next_out;
01693 
01694         zerr = inflate(&zs, Z_PARTIAL_FLUSH);
01695 
01696         //-- incrementally update crc checksum
01697         crc = crc32(crc, (const unsigned char*)old_next_out, zs.next_out - old_next_out);
01698       }
01699       else
01700         zerr = Z_STREAM_END;
01701     }
01702     //-- else just use input buffer containing data from STORED item
01703     else
01704     {
01705       //-- incrementally update crc checksum
01706       crc = crc32(crc, (const unsigned char*)inbuf, chunk);
01707     }
01708   }
01709 
01710   //-- convert zlib error to return value
01711   if (status == ZIP_OK && zerr != Z_OK && zerr != Z_STREAM_END)
01712   {
01713     status = (zerr == Z_MEM_ERROR) ? ZIP_ERR_MEMORY : ZIP_ERR_CORRUPT;
01714     goto cleanup;
01715   }
01716 
01717   //-- verify computed crc checksum against header info crc
01718   if (status == ZIP_OK && crc != aItem->crc32)
01719   {
01720     status = ZIP_ERR_CORRUPT;
01721   }
01722 
01723 cleanup:
01724   if (bInflating)
01725   {
01726     //-- free zlib internal state
01727     inflateEnd(&zs);
01728   }
01729 
01730   return status;
01731 }
01732 
01733 //------------------------------------------
01734 // nsZipArchive constructor and destructor
01735 //------------------------------------------
01736 
01737 MOZ_DECL_CTOR_COUNTER(nsZipArchive)
01738 
01739 nsZipArchive::nsZipArchive()
01740   : kMagic(ZIP_MAGIC), kArenaBlockSize(1*1024)
01741 #ifdef STANDALONE
01742     , mFd(0)
01743 #endif
01744 {
01745   MOZ_COUNT_CTOR(nsZipArchive);
01746 
01747   // initialize the table to NULL
01748   memset(mFiles, 0, sizeof mFiles);
01749 
01750 #ifndef STANDALONE
01751   // Initialize our arena
01752   PL_INIT_ARENA_POOL(&mArena, "ZipArena", kArenaBlockSize);
01753 #endif
01754 }
01755 
01756 nsZipArchive::~nsZipArchive()
01757 {
01758   CloseArchive();
01759 
01760   MOZ_COUNT_DTOR(nsZipArchive);
01761 }
01762 
01763 
01764 
01765 
01766 //------------------------------------------
01767 // nsZipItem constructor and destructor
01768 //------------------------------------------
01769 
01770 nsZipItem::nsZipItem() : name(0), headerOffset(0), dataOffset(0), next(0), hasDataOffset(0), isSymlink(0)
01771 {
01772 }
01773 
01774 nsZipItem::~nsZipItem()
01775 {
01776 #ifdef STANDALONE
01777   if (name != 0)
01778   {
01779     delete [] name;
01780     name = 0;
01781   }
01782 #endif
01783 }
01784 
01785 //------------------------------------------
01786 // nsZipReadState
01787 //------------------------------------------
01788 
01789 MOZ_DECL_CTOR_COUNTER(nsZipReadState)
01790 
01791 //------------------------------------------
01792 // nsZipFind constructor and destructor
01793 //------------------------------------------
01794 
01795 MOZ_DECL_CTOR_COUNTER(nsZipFind)
01796 
01797 nsZipFind::nsZipFind(nsZipArchive* aZip, char* aPattern, PRBool aRegExp)
01798 : kMagic(ZIPFIND_MAGIC),
01799   mArchive(aZip),
01800   mPattern(aPattern),
01801   mSlot(0),
01802   mItem(0),
01803   mRegExp(aRegExp)
01804 {
01805   MOZ_COUNT_CTOR(nsZipFind);
01806 }
01807 
01808 nsZipFind::~nsZipFind()
01809 {
01810   if (mPattern != 0)
01811     PL_strfree(mPattern);
01812 
01813   MOZ_COUNT_DTOR(nsZipFind);
01814 }
01815 
01816 //------------------------------------------
01817 // nsZipFind::GetArchive
01818 //------------------------------------------
01819 nsZipArchive* nsZipFind::GetArchive()
01820 {
01821     if (!mArchive)
01822         return NULL;
01823 
01824     return mArchive;
01825 }
01826 
01827 
01828 //------------------------------------------
01829 // helper functions
01830 //------------------------------------------
01831 
01832 /*
01833  *  x t o i n t
01834  *
01835  *  Converts a two byte ugly endianed integer
01836  *  to our platform's integer.
01837  */
01838 static PRUint16 xtoint (unsigned char *ii)
01839 {
01840   return (PRUint16) ((ii [0]) | (ii [1] << 8));
01841 }
01842 
01843 /*
01844  *  x t o l o n g
01845  *
01846  *  Converts a four byte ugly endianed integer
01847  *  to our platform's integer.
01848  */
01849 static PRUint32 xtolong (unsigned char *ll)
01850 {
01851   PRUint32 ret;
01852 
01853   ret =  (
01854          (((PRUint32) ll [0]) <<  0) |
01855          (((PRUint32) ll [1]) <<  8) |
01856          (((PRUint32) ll [2]) << 16) |
01857          (((PRUint32) ll [3]) << 24)
01858         );
01859 
01860   return ret;
01861 }
01862 
01863 /*
01864  * ExtractMode
01865  *
01866  * Extracts bits 17-24 from a 32-bit unsigned long
01867  * representation of the external attributes field.
01868  * Subsequently it tacks on the implicit user-read
01869  * bit.
01870  */
01871 static PRUint16 ExtractMode(PRUint32 ext_attr)
01872 {
01873     ext_attr &= 0x00FF0000;
01874     ext_attr >>= 16;
01875     ext_attr |= 0x00000100;
01876 
01877     return (PRUint16) ext_attr;
01878 }
01879 
01880 
01881 /*
01882  *
01883  *  Return true if the attributes are for a symbolic link
01884  *
01885  */
01886 
01887 static PRBool IsSymlink(PRUint32 ext_attr)
01888 {
01889   return (((ext_attr>>16) & S_IFMT) == S_IFLNK) ? PR_TRUE : PR_FALSE;
01890 }
01891 
01892 #ifndef STANDALONE
01893 PRTime
01894 nsZipItem::GetModTime()
01895 {
01896   char buffer[17];
01897 
01898   PRUint16 aDate = this->date;
01899   PRUint16 aTime = this->time;
01900 
01901   PR_snprintf(buffer, sizeof(buffer), "%02d/%02d/%04d %02d:%02d",
01902         ((aDate >> 5) & 0x0F), (aDate & 0x1F), (aDate >> 9) + 1980,
01903         ((aTime >> 11) & 0x1F), ((aTime >> 5) & 0x3F));
01904 
01905   PRTime result;
01906   PR_ParseTimeString(buffer, PR_FALSE, &result);
01907   return result;
01908 }
01909 #endif