PageRenderTime 82ms CodeModel.GetById 20ms app.highlight 53ms RepoModel.GetById 1ms app.codeStats 1ms

/modules/libjar/nsZipArchive.cpp

http://github.com/zpao/v8monkey
C++ | 1078 lines | 717 code | 161 blank | 200 comment | 160 complexity | e807c4ba40141536e1994c62339fb7f4 MD5 | raw file
   1/* -*- Mode: C++; tab-width: 4; 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@netscape.com>
  27 *   Jeroen Dobbelaere <jeroen.dobbelaere@acunia.com>
  28 *   Jeff Walden <jwalden+code@mit.edu>
  29 *   Taras Glek <tglek@mozilla.com>
  30 *
  31 * Alternatively, the contents of this file may be used under the terms of
  32 * either the GNU General Public License Version 2 or later (the "GPL"), or
  33 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  34 * in which case the provisions of the GPL or the LGPL are applicable instead
  35 * of those above. If you wish to allow use of your version of this file only
  36 * under the terms of either the GPL or the LGPL, and not to allow others to
  37 * use your version of this file under the terms of the MPL, indicate your
  38 * decision by deleting the provisions above and replace them with the notice
  39 * and other provisions required by the GPL or the LGPL. If you do not delete
  40 * the provisions above, a recipient may use your version of this file under
  41 * the terms of any one of the MPL, the GPL or the LGPL.
  42 *
  43 * ***** END LICENSE BLOCK ***** */
  44
  45/*
  46 * This module implements a simple archive extractor for the PKZIP format.
  47 *
  48 * The underlying nsZipArchive is NOT thread-safe. Do not pass references
  49 * or pointers to it across thread boundaries.
  50 */
  51
  52#define READTYPE  PRInt32
  53#include "zlib.h"
  54#include "nsISupportsUtils.h"
  55#include "prio.h"
  56#include "plstr.h"
  57#include "prlog.h"
  58#include "stdlib.h"
  59#include "nsWildCard.h"
  60#include "nsZipArchive.h"
  61#include "nsString.h"
  62#include "mozilla/FunctionTimer.h"
  63#include "prenv.h"
  64#if defined(XP_WIN)
  65#include <windows.h>
  66#endif
  67
  68// For placement new used for arena allocations of zip file list
  69#include NEW_H
  70#define ZIP_ARENABLOCKSIZE (1*1024)
  71
  72#ifdef XP_UNIX
  73    #include <sys/types.h>
  74    #include <sys/stat.h>
  75    #include <limits.h>
  76    #include <unistd.h>
  77#elif defined(XP_WIN) || defined(XP_OS2)
  78    #include <io.h>
  79#endif
  80
  81#ifdef __SYMBIAN32__
  82    #include <sys/syslimits.h>
  83#endif /*__SYMBIAN32__*/
  84
  85
  86#ifndef XP_UNIX /* we need some constants defined in limits.h and unistd.h */
  87#  ifndef S_IFMT
  88#    define S_IFMT 0170000
  89#  endif
  90#  ifndef S_IFLNK
  91#    define S_IFLNK  0120000
  92#  endif
  93#  ifndef PATH_MAX
  94#    define PATH_MAX 1024
  95#  endif
  96#endif  /* XP_UNIX */
  97
  98
  99using namespace mozilla;
 100
 101static const PRUint32 kMaxNameLength = PATH_MAX; /* Maximum name length */
 102// For synthetic zip entries. Date/time corresponds to 1980-01-01 00:00.
 103static const PRUint16 kSyntheticTime = 0;
 104static const PRUint16 kSyntheticDate = (1 + (1 << 5) + (0 << 9));
 105
 106static PRUint16 xtoint(const PRUint8 *ii);
 107static PRUint32 xtolong(const PRUint8 *ll);
 108static PRUint32 HashName(const char* aName, PRUint16 nameLen);
 109#ifdef XP_UNIX
 110static nsresult ResolveSymlink(const char *path);
 111#endif
 112
 113//***********************************************************
 114// For every inflation the following allocations are done:
 115// malloc(1 * 9520)
 116// malloc(32768 * 1)
 117//***********************************************************
 118
 119nsresult gZlibInit(z_stream *zs)
 120{
 121  memset(zs, 0, sizeof(z_stream));
 122  int zerr = inflateInit2(zs, -MAX_WBITS);
 123  if (zerr != Z_OK) return NS_ERROR_OUT_OF_MEMORY;
 124
 125  return NS_OK;
 126}
 127
 128nsZipHandle::nsZipHandle()
 129  : mFileData(nsnull)
 130  , mLen(0)
 131  , mMap(nsnull)
 132  , mRefCnt(0)
 133{
 134  MOZ_COUNT_CTOR(nsZipHandle);
 135}
 136
 137NS_IMPL_THREADSAFE_ADDREF(nsZipHandle)
 138NS_IMPL_THREADSAFE_RELEASE(nsZipHandle)
 139
 140nsresult nsZipHandle::Init(nsILocalFile *file, nsZipHandle **ret)
 141{
 142  mozilla::AutoFDClose fd;
 143  nsresult rv = file->OpenNSPRFileDesc(PR_RDONLY, 0000, &fd);
 144  if (NS_FAILED(rv))
 145    return rv;
 146
 147  PRInt64 size = PR_Available64(fd);
 148  if (size >= PR_INT32_MAX)
 149    return NS_ERROR_FILE_TOO_BIG;
 150
 151  PRFileMap *map = PR_CreateFileMap(fd, size, PR_PROT_READONLY);
 152  if (!map)
 153    return NS_ERROR_FAILURE;
 154  
 155  PRUint8 *buf = (PRUint8*) PR_MemMap(map, 0, (PRUint32) size);
 156  // Bug 525755: PR_MemMap fails when fd points at something other than a normal file.
 157  if (!buf) {
 158    PR_CloseFileMap(map);
 159    return NS_ERROR_FAILURE;
 160  }
 161
 162  nsRefPtr<nsZipHandle> handle = new nsZipHandle();
 163  if (!handle) {
 164    PR_MemUnmap(buf, (PRUint32) size);
 165    PR_CloseFileMap(map);
 166    return NS_ERROR_OUT_OF_MEMORY;
 167  }
 168
 169  handle->mMap = map;
 170  handle->mFile.Init(file);
 171  handle->mLen = (PRUint32) size;
 172  handle->mFileData = buf;
 173  *ret = handle.forget().get();
 174  return NS_OK;
 175}
 176
 177nsresult nsZipHandle::Init(nsZipArchive *zip, const char *entry,
 178                           nsZipHandle **ret)
 179{
 180  nsRefPtr<nsZipHandle> handle = new nsZipHandle();
 181  if (!handle)
 182    return NS_ERROR_OUT_OF_MEMORY;
 183
 184  handle->mBuf = new nsZipItemPtr<PRUint8>(zip, entry);
 185  if (!handle->mBuf)
 186    return NS_ERROR_OUT_OF_MEMORY;
 187
 188  if (!handle->mBuf->Buffer())
 189    return NS_ERROR_UNEXPECTED;
 190
 191  handle->mMap = nsnull;
 192  handle->mFile.Init(zip, entry);
 193  handle->mLen = handle->mBuf->Length();
 194  handle->mFileData = handle->mBuf->Buffer();
 195  *ret = handle.forget().get();
 196  return NS_OK;
 197}
 198
 199PRInt64 nsZipHandle::SizeOfMapping()
 200{
 201    return mLen;
 202}
 203
 204nsZipHandle::~nsZipHandle()
 205{
 206  if (mMap) {
 207    PR_MemUnmap((void *)mFileData, mLen);
 208    PR_CloseFileMap(mMap);
 209  }
 210  mFileData = nsnull;
 211  mMap = nsnull;
 212  mBuf = nsnull;
 213  MOZ_COUNT_DTOR(nsZipHandle);
 214}
 215
 216//***********************************************************
 217//      nsZipArchive  --  public methods
 218//***********************************************************
 219
 220//---------------------------------------------
 221//  nsZipArchive::OpenArchive
 222//---------------------------------------------
 223nsresult nsZipArchive::OpenArchive(nsZipHandle *aZipHandle)
 224{
 225  mFd = aZipHandle;
 226
 227  // Initialize our arena
 228  PL_INIT_ARENA_POOL(&mArena, "ZipArena", ZIP_ARENABLOCKSIZE);
 229
 230  //-- get table of contents for archive
 231  nsresult rv = BuildFileList();
 232  char *env = PR_GetEnv("MOZ_JAR_LOG_DIR");
 233  if (env && NS_SUCCEEDED(rv) && aZipHandle->mFile) {
 234    nsCOMPtr<nsILocalFile> logFile;
 235    nsresult rv2 = NS_NewLocalFile(NS_ConvertUTF8toUTF16(env), false, getter_AddRefs(logFile));
 236    
 237    if (!NS_SUCCEEDED(rv2))
 238      return rv;
 239
 240    // Create a directory for the log (in case it doesn't exist)
 241    logFile->Create(nsIFile::DIRECTORY_TYPE, 0700);
 242
 243    nsAutoString name;
 244    nsCOMPtr<nsILocalFile> file = aZipHandle->mFile.GetBaseFile();
 245    file->GetLeafName(name);
 246    name.Append(NS_LITERAL_STRING(".log"));
 247    logFile->Append(name);
 248
 249    PRFileDesc* fd;
 250    rv2 = logFile->OpenNSPRFileDesc(PR_WRONLY|PR_CREATE_FILE|PR_APPEND, 0644, &fd);
 251    if (NS_SUCCEEDED(rv2))
 252      mLog = fd;
 253  }
 254  return rv;
 255}
 256
 257nsresult nsZipArchive::OpenArchive(nsIFile *aFile)
 258{
 259  nsresult rv;
 260  nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(aFile, &rv);
 261  NS_ENSURE_SUCCESS(rv, rv);
 262
 263  nsRefPtr<nsZipHandle> handle;
 264  rv = nsZipHandle::Init(localFile, getter_AddRefs(handle));
 265  if (NS_FAILED(rv))
 266    return rv;
 267
 268  return OpenArchive(handle);
 269}
 270
 271//---------------------------------------------
 272//  nsZipArchive::Test
 273//---------------------------------------------
 274nsresult nsZipArchive::Test(const char *aEntryName)
 275{
 276  nsZipItem* currItem;
 277
 278  if (aEntryName) // only test specified item
 279  {
 280    currItem = GetItem(aEntryName);
 281    if (!currItem)
 282      return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
 283    //-- don't test (synthetic) directory items
 284    if (currItem->IsDirectory())
 285      return NS_OK;
 286    return ExtractFile(currItem, 0, 0);
 287  }
 288
 289  // test all items in archive
 290  for (int i = 0; i < ZIP_TABSIZE; i++) {
 291    for (currItem = mFiles[i]; currItem; currItem = currItem->next) {
 292      //-- don't test (synthetic) directory items
 293      if (currItem->IsDirectory())
 294        continue;
 295      nsresult rv = ExtractFile(currItem, 0, 0);
 296      if (rv != NS_OK)
 297        return rv;
 298    }
 299  }
 300
 301  return NS_OK;
 302}
 303
 304//---------------------------------------------
 305//  nsZipArchive::CloseArchive
 306//---------------------------------------------
 307nsresult nsZipArchive::CloseArchive()
 308{
 309  if (mFd) {
 310    PL_FinishArenaPool(&mArena);
 311    mFd = NULL;
 312  }
 313
 314  // CAUTION:
 315  // We don't need to delete each of the nsZipItem as the memory for
 316  // the zip item and the filename it holds are both allocated from the Arena.
 317  // Hence, destroying the Arena is like destroying all the memory
 318  // for all the nsZipItem in one shot. But if the ~nsZipItem is doing
 319  // anything more than cleaning up memory, we should start calling it.
 320  // Let us also cleanup the mFiles table for re-use on the next 'open' call
 321  memset(mFiles, 0, sizeof(mFiles));
 322  mBuiltSynthetics = false;
 323  return NS_OK;
 324}
 325
 326//---------------------------------------------
 327// nsZipArchive::GetItem
 328//---------------------------------------------
 329nsZipItem*  nsZipArchive::GetItem(const char * aEntryName)
 330{
 331  if (aEntryName) {
 332    PRUint32 len = strlen(aEntryName);
 333    //-- If the request is for a directory, make sure that synthetic entries 
 334    //-- are created for the directories without their own entry.
 335    if (!mBuiltSynthetics) {
 336        if ((len > 0) && (aEntryName[len-1] == '/')) {
 337            if (BuildSynthetics() != NS_OK)
 338                return 0;
 339        }
 340    }
 341MOZ_WIN_MEM_TRY_BEGIN
 342    nsZipItem* item = mFiles[ HashName(aEntryName, len) ];
 343    while (item) {
 344      if ((len == item->nameLength) && 
 345          (!memcmp(aEntryName, item->Name(), len))) {
 346        
 347        if (mLog) {
 348          // Successful GetItem() is a good indicator that the file is about to be read
 349          char *tmp = PL_strdup(aEntryName);
 350          tmp[len]='\n';
 351          PR_Write(mLog, tmp, len+1);
 352          PL_strfree(tmp);
 353        }
 354        return item; //-- found it
 355      }
 356      item = item->next;
 357    }
 358MOZ_WIN_MEM_TRY_CATCH(return nsnull)
 359  }
 360  return nsnull;
 361}
 362
 363//---------------------------------------------
 364// nsZipArchive::ExtractFile
 365// This extracts the item to the filehandle provided.
 366// If 'aFd' is null, it only tests the extraction.
 367// On extraction error(s) it removes the file.
 368// When needed, it also resolves the symlink.
 369//---------------------------------------------
 370nsresult nsZipArchive::ExtractFile(nsZipItem *item, const char *outname,
 371                                   PRFileDesc* aFd)
 372{
 373  if (!item)
 374    return NS_ERROR_ILLEGAL_VALUE;
 375  if (!mFd)
 376    return NS_ERROR_FAILURE;
 377
 378  // Directory extraction is handled in nsJAR::Extract,
 379  // so the item to be extracted should never be a directory
 380  PR_ASSERT(!item->IsDirectory());
 381
 382  Bytef outbuf[ZIP_BUFLEN];
 383
 384  nsZipCursor cursor(item, this, outbuf, ZIP_BUFLEN, true);
 385
 386  nsresult rv = NS_OK;
 387
 388  while (true) {
 389    PRUint32 count = 0;
 390    PRUint8* buf = cursor.Read(&count);
 391    if (!buf) {
 392      rv = NS_ERROR_FILE_CORRUPTED;
 393      break;
 394    } else if (count == 0) {
 395      break;
 396    }
 397
 398    if (aFd && PR_Write(aFd, buf, count) < (READTYPE)count) {
 399      rv = NS_ERROR_FILE_DISK_FULL;
 400      break;
 401    }
 402  }
 403
 404  //-- delete the file on errors, or resolve symlink if needed
 405  if (aFd) {
 406    PR_Close(aFd);
 407    if (rv != NS_OK)
 408      PR_Delete(outname);
 409#ifdef XP_UNIX
 410    else if (item->IsSymlink())
 411      rv = ResolveSymlink(outname);
 412#endif
 413  }
 414
 415  return rv;
 416}
 417
 418//---------------------------------------------
 419// nsZipArchive::FindInit
 420//---------------------------------------------
 421PRInt32
 422nsZipArchive::FindInit(const char * aPattern, nsZipFind **aFind)
 423{
 424  if (!aFind)
 425    return NS_ERROR_ILLEGAL_VALUE;
 426
 427  // null out param in case an error happens
 428  *aFind = NULL;
 429
 430  bool    regExp = false;
 431  char*   pattern = 0;
 432
 433  // Create synthetic directory entries on demand
 434  nsresult rv = BuildSynthetics();
 435  if (rv != NS_OK)
 436    return rv;
 437
 438  // validate the pattern
 439  if (aPattern)
 440  {
 441    switch (NS_WildCardValid((char*)aPattern))
 442    {
 443      case INVALID_SXP:
 444        return NS_ERROR_ILLEGAL_VALUE;
 445
 446      case NON_SXP:
 447        regExp = false;
 448        break;
 449
 450      case VALID_SXP:
 451        regExp = true;
 452        break;
 453
 454      default:
 455        // undocumented return value from RegExpValid!
 456        PR_ASSERT(false);
 457        return NS_ERROR_ILLEGAL_VALUE;
 458    }
 459
 460    pattern = PL_strdup(aPattern);
 461    if (!pattern)
 462      return NS_ERROR_OUT_OF_MEMORY;
 463  }
 464
 465  *aFind = new nsZipFind(this, pattern, regExp);
 466  if (!*aFind) {
 467    PL_strfree(pattern);
 468    return NS_ERROR_OUT_OF_MEMORY;
 469  }
 470
 471  return NS_OK;
 472}
 473
 474
 475
 476//---------------------------------------------
 477// nsZipFind::FindNext
 478//---------------------------------------------
 479nsresult nsZipFind::FindNext(const char ** aResult, PRUint16 *aNameLen)
 480{
 481  if (!mArchive || !aResult || !aNameLen)
 482    return NS_ERROR_ILLEGAL_VALUE;
 483
 484  *aResult = 0;
 485  *aNameLen = 0;
 486MOZ_WIN_MEM_TRY_BEGIN
 487  // we start from last match, look for next
 488  while (mSlot < ZIP_TABSIZE)
 489  {
 490    // move to next in current chain, or move to new slot
 491    mItem = mItem ? mItem->next : mArchive->mFiles[mSlot];
 492
 493    bool found = false;
 494    if (!mItem)
 495      ++mSlot;                          // no more in this chain, move to next slot
 496    else if (!mPattern)
 497      found = true;            // always match
 498    else if (mRegExp)
 499    {
 500      char buf[kMaxNameLength+1];
 501      memcpy(buf, mItem->Name(), mItem->nameLength);
 502      buf[mItem->nameLength]='\0';
 503      found = (NS_WildCardMatch(buf, mPattern, false) == MATCH);
 504    }
 505    else
 506      found = ((mItem->nameLength == strlen(mPattern)) &&
 507               (memcmp(mItem->Name(), mPattern, mItem->nameLength) == 0));
 508    if (found) {
 509      // Need also to return the name length, as it is NOT zero-terminatdd...
 510      *aResult = mItem->Name();
 511      *aNameLen = mItem->nameLength;
 512      return NS_OK;
 513    }
 514  }
 515MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE)
 516  return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
 517}
 518
 519#ifdef XP_UNIX
 520//---------------------------------------------
 521// ResolveSymlink
 522//---------------------------------------------
 523static nsresult ResolveSymlink(const char *path)
 524{
 525  PRFileDesc * fIn = PR_Open(path, PR_RDONLY, 0000);
 526  if (!fIn)
 527    return NS_ERROR_FILE_DISK_FULL;
 528
 529  char buf[PATH_MAX+1];
 530  PRInt32 length = PR_Read(fIn, (void*)buf, PATH_MAX);
 531  PR_Close(fIn);
 532
 533  if ( (length <= 0)
 534    || ((buf[length] = 0, PR_Delete(path)) != 0)
 535    || (symlink(buf, path) != 0))
 536  {
 537     return NS_ERROR_FILE_DISK_FULL;
 538  }
 539  return NS_OK;
 540}
 541#endif
 542
 543//***********************************************************
 544//      nsZipArchive  --  private implementation
 545//***********************************************************
 546
 547//---------------------------------------------
 548//  nsZipArchive::CreateZipItem
 549//---------------------------------------------
 550nsZipItem* nsZipArchive::CreateZipItem()
 551{
 552  // Arena allocate the nsZipItem
 553  void *mem;
 554  PL_ARENA_ALLOCATE(mem, &mArena, sizeof(nsZipItem));
 555  return (nsZipItem*)mem;
 556}
 557
 558//---------------------------------------------
 559//  nsZipArchive::BuildFileList
 560//---------------------------------------------
 561nsresult nsZipArchive::BuildFileList()
 562{
 563#ifndef XP_WIN
 564  NS_TIME_FUNCTION;
 565#endif
 566  // Get archive size using end pos
 567  const PRUint8* buf;
 568  const PRUint8* startp = mFd->mFileData;
 569  const PRUint8* endp = startp + mFd->mLen;
 570MOZ_WIN_MEM_TRY_BEGIN
 571  PRUint32 centralOffset = 4;
 572  if (mFd->mLen > ZIPCENTRAL_SIZE && xtolong(startp + centralOffset) == CENTRALSIG) {
 573    // Success means optimized jar layout from bug 559961 is in effect
 574  } else {
 575    for (buf = endp - ZIPEND_SIZE; buf > startp; buf--)
 576      {
 577        if (xtolong(buf) == ENDSIG) {
 578          centralOffset = xtolong(((ZipEnd *)buf)->offset_central_dir);
 579          break;
 580        }
 581      }
 582  }
 583
 584  if (!centralOffset)
 585    return NS_ERROR_FILE_CORRUPTED;
 586
 587  //-- Read the central directory headers
 588  buf = startp + centralOffset;
 589  PRUint32 sig = 0;
 590  while (buf + PRInt32(sizeof(PRUint32)) <= endp &&
 591         (sig = xtolong(buf)) == CENTRALSIG) {
 592    // Make sure there is enough data available.
 593    if (endp - buf < ZIPCENTRAL_SIZE)
 594      return NS_ERROR_FILE_CORRUPTED;
 595
 596    // Read the fixed-size data.
 597    ZipCentral* central = (ZipCentral*)buf;
 598
 599    PRUint16 namelen = xtoint(central->filename_len);
 600    PRUint16 extralen = xtoint(central->extrafield_len);
 601    PRUint16 commentlen = xtoint(central->commentfield_len);
 602
 603    // Point to the next item at the top of loop
 604    buf += ZIPCENTRAL_SIZE + namelen + extralen + commentlen;
 605
 606    // Sanity check variable sizes and refuse to deal with
 607    // anything too big: it's likely a corrupt archive.
 608    if (namelen > kMaxNameLength || buf >= endp)
 609      return NS_ERROR_FILE_CORRUPTED;
 610
 611    nsZipItem* item = CreateZipItem();
 612    if (!item)
 613      return NS_ERROR_OUT_OF_MEMORY;
 614
 615    item->central = central;
 616    item->nameLength = namelen;
 617    item->isSynthetic = false;
 618
 619    // Add item to file table
 620    PRUint32 hash = HashName(item->Name(), namelen);
 621    item->next = mFiles[hash];
 622    mFiles[hash] = item;
 623
 624    sig = 0;
 625  } /* while reading central directory records */
 626
 627  if (sig != ENDSIG)
 628    return NS_ERROR_FILE_CORRUPTED;
 629
 630MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE)
 631  return NS_OK;
 632}
 633
 634//---------------------------------------------
 635//  nsZipArchive::BuildSynthetics
 636//---------------------------------------------
 637nsresult nsZipArchive::BuildSynthetics()
 638{
 639  if (mBuiltSynthetics)
 640    return NS_OK;
 641  mBuiltSynthetics = true;
 642
 643MOZ_WIN_MEM_TRY_BEGIN
 644  // Create synthetic entries for any missing directories.
 645  // Do this when all ziptable has scanned to prevent double entries.
 646  for (int i = 0; i < ZIP_TABSIZE; ++i)
 647  {
 648    for (nsZipItem* item = mFiles[i]; item != 0; item = item->next)
 649    {
 650      if (item->isSynthetic)
 651        continue;
 652    
 653      //-- add entries for directories in the current item's path
 654      //-- go from end to beginning, because then we can stop trying
 655      //-- to create diritems if we find that the diritem we want to
 656      //-- create already exists
 657      //-- start just before the last char so as to not add the item
 658      //-- twice if it's a directory
 659      PRUint16 namelen = item->nameLength;
 660      const char *name = item->Name();
 661      for (PRUint16 dirlen = namelen - 1; dirlen > 0; dirlen--)
 662      {
 663        if (name[dirlen-1] != '/')
 664          continue;
 665
 666        // Is the directory already in the file table?
 667        PRUint32 hash = HashName(item->Name(), dirlen);
 668        bool found = false;
 669        for (nsZipItem* zi = mFiles[hash]; zi != NULL; zi = zi->next)
 670        {
 671          if ((dirlen == zi->nameLength) &&
 672              (0 == memcmp(item->Name(), zi->Name(), dirlen)))
 673          {
 674            // we've already added this dir and all its parents
 675            found = true;
 676            break;
 677          }
 678        }
 679        // if the directory was found, break out of the directory
 680        // creation loop now that we know all implicit directories
 681        // are there -- otherwise, start creating the zip item
 682        if (found)
 683          break;
 684
 685        nsZipItem* diritem = CreateZipItem();
 686        if (!diritem)
 687          return NS_ERROR_OUT_OF_MEMORY;
 688
 689        // Point to the central record of the original item for the name part.
 690        diritem->central =  item->central;
 691        diritem->nameLength = dirlen;
 692        diritem->isSynthetic = true;
 693
 694        // add diritem to the file table
 695        diritem->next = mFiles[hash];
 696        mFiles[hash] = diritem;
 697      } /* end processing of dirs in item's name */
 698    }
 699  }
 700MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE)
 701  return NS_OK;
 702}
 703
 704nsZipHandle* nsZipArchive::GetFD()
 705{
 706  if (!mFd)
 707    return NULL;
 708  return mFd.get();
 709}
 710
 711//---------------------------------------------
 712// nsZipArchive::GetData
 713//---------------------------------------------
 714const PRUint8* nsZipArchive::GetData(nsZipItem* aItem)
 715{
 716  PR_ASSERT (aItem);
 717MOZ_WIN_MEM_TRY_BEGIN
 718  //-- read local header to get variable length values and calculate
 719  //-- the real data offset
 720  PRUint32 len = mFd->mLen;
 721  const PRUint8* data = mFd->mFileData;
 722  PRUint32 offset = aItem->LocalOffset();
 723  if (offset + ZIPLOCAL_SIZE > len)
 724    return nsnull;
 725
 726  // -- check signature before using the structure, in case the zip file is corrupt
 727  ZipLocal* Local = (ZipLocal*)(data + offset);
 728  if ((xtolong(Local->signature) != LOCALSIG))
 729    return nsnull;
 730
 731  //-- NOTE: extralen is different in central header and local header
 732  //--       for archives created using the Unix "zip" utility. To set
 733  //--       the offset accurately we need the _local_ extralen.
 734  offset += ZIPLOCAL_SIZE +
 735            xtoint(Local->filename_len) +
 736            xtoint(Local->extrafield_len);
 737
 738  // -- check if there is enough source data in the file
 739  if (offset + aItem->Size() > len)
 740    return nsnull;
 741
 742  return data + offset;
 743MOZ_WIN_MEM_TRY_CATCH(return nsnull)
 744}
 745
 746//---------------------------------------------
 747// nsZipArchive::SizeOfMapping
 748//---------------------------------------------
 749PRInt64 nsZipArchive::SizeOfMapping()
 750{
 751    return mFd ? mFd->SizeOfMapping() : 0;
 752}
 753
 754//------------------------------------------
 755// nsZipArchive constructor and destructor
 756//------------------------------------------
 757
 758nsZipArchive::nsZipArchive()
 759  : mRefCnt(0)
 760  , mBuiltSynthetics(false)
 761{
 762  MOZ_COUNT_CTOR(nsZipArchive);
 763
 764  // initialize the table to NULL
 765  memset(mFiles, 0, sizeof(mFiles));
 766}
 767
 768NS_IMPL_THREADSAFE_ADDREF(nsZipArchive)
 769NS_IMPL_THREADSAFE_RELEASE(nsZipArchive)
 770
 771nsZipArchive::~nsZipArchive()
 772{
 773  CloseArchive();
 774
 775  MOZ_COUNT_DTOR(nsZipArchive);
 776}
 777
 778
 779//------------------------------------------
 780// nsZipFind constructor and destructor
 781//------------------------------------------
 782
 783nsZipFind::nsZipFind(nsZipArchive* aZip, char* aPattern, bool aRegExp) : 
 784  mArchive(aZip),
 785  mPattern(aPattern),
 786  mItem(0),
 787  mSlot(0),
 788  mRegExp(aRegExp)
 789{
 790  MOZ_COUNT_CTOR(nsZipFind);
 791}
 792
 793nsZipFind::~nsZipFind()
 794{
 795  PL_strfree(mPattern);
 796
 797  MOZ_COUNT_DTOR(nsZipFind);
 798}
 799
 800//------------------------------------------
 801// helper functions
 802//------------------------------------------
 803
 804/* 
 805 * HashName 
 806 *
 807 * returns a hash key for the entry name 
 808 */
 809static PRUint32 HashName(const char* aName, PRUint16 len)
 810{
 811  PR_ASSERT(aName != 0);
 812
 813  const PRUint8* p = (const PRUint8*)aName;
 814  const PRUint8* endp = p + len;
 815  PRUint32 val = 0;
 816  while (p != endp) {
 817    val = val*37 + *p++;
 818  }
 819
 820  return (val % ZIP_TABSIZE);
 821}
 822
 823/*
 824 *  x t o i n t
 825 *
 826 *  Converts a two byte ugly endianed integer
 827 *  to our platform's integer.
 828 */
 829static PRUint16 xtoint (const PRUint8 *ii)
 830{
 831  return (PRUint16) ((ii [0]) | (ii [1] << 8));
 832}
 833
 834/*
 835 *  x t o l o n g
 836 *
 837 *  Converts a four byte ugly endianed integer
 838 *  to our platform's integer.
 839 */
 840static PRUint32 xtolong (const PRUint8 *ll)
 841{
 842  return (PRUint32)( (ll [0] <<  0) |
 843                     (ll [1] <<  8) |
 844                     (ll [2] << 16) |
 845                     (ll [3] << 24) );
 846}
 847
 848/*
 849 * GetModTime
 850 *
 851 * returns last modification time in microseconds
 852 */
 853static PRTime GetModTime(PRUint16 aDate, PRUint16 aTime)
 854{
 855  // Note that on DST shift we can't handle correctly the hour that is valid
 856  // in both DST zones
 857  PRExplodedTime time;
 858
 859  time.tm_usec = 0;
 860
 861  time.tm_hour = (aTime >> 11) & 0x1F;
 862  time.tm_min = (aTime >> 5) & 0x3F;
 863  time.tm_sec = (aTime & 0x1F) * 2;
 864
 865  time.tm_year = (aDate >> 9) + 1980;
 866  time.tm_month = ((aDate >> 5) & 0x0F) - 1;
 867  time.tm_mday = aDate & 0x1F;
 868
 869  time.tm_params.tp_gmt_offset = 0;
 870  time.tm_params.tp_dst_offset = 0;
 871
 872  PR_NormalizeTime(&time, PR_GMTParameters);
 873  time.tm_params.tp_gmt_offset = PR_LocalTimeParameters(&time).tp_gmt_offset;
 874  PR_NormalizeTime(&time, PR_GMTParameters);
 875  time.tm_params.tp_dst_offset = PR_LocalTimeParameters(&time).tp_dst_offset;
 876
 877  return PR_ImplodeTime(&time);
 878}
 879
 880PRUint32 nsZipItem::LocalOffset()
 881{
 882  return xtolong(central->localhdr_offset);
 883}
 884
 885PRUint32 nsZipItem::Size()
 886{
 887  return isSynthetic ? 0 : xtolong(central->size);
 888}
 889
 890PRUint32 nsZipItem::RealSize()
 891{
 892  return isSynthetic ? 0 : xtolong(central->orglen);
 893}
 894
 895PRUint32 nsZipItem::CRC32()
 896{
 897  return isSynthetic ? 0 : xtolong(central->crc32);
 898}
 899
 900PRUint16 nsZipItem::Date()
 901{
 902  return isSynthetic ? kSyntheticDate : xtoint(central->date);
 903}
 904
 905PRUint16 nsZipItem::Time()
 906{
 907  return isSynthetic ? kSyntheticTime : xtoint(central->time);
 908}
 909
 910PRUint16 nsZipItem::Compression()
 911{
 912  return isSynthetic ? STORED : xtoint(central->method);
 913}
 914
 915bool nsZipItem::IsDirectory()
 916{
 917  return isSynthetic || ((nameLength > 0) && ('/' == Name()[nameLength - 1]));
 918}
 919
 920PRUint16 nsZipItem::Mode()
 921{
 922  if (isSynthetic) return 0755;
 923  return ((PRUint16)(central->external_attributes[2]) | 0x100);
 924}
 925
 926const PRUint8 * nsZipItem::GetExtraField(PRUint16 aTag, PRUint16 *aBlockSize)
 927{
 928  if (isSynthetic) return nsnull;
 929MOZ_WIN_MEM_TRY_BEGIN
 930  const unsigned char *buf = ((const unsigned char*)central) + ZIPCENTRAL_SIZE +
 931                             nameLength;
 932  PRUint32 buflen = (PRUint32)xtoint(central->extrafield_len);
 933  PRUint32 pos = 0;
 934  PRUint16 tag, blocksize;
 935
 936  while (buf && (pos + 4) <= buflen) {
 937    tag = xtoint(buf + pos);
 938    blocksize = xtoint(buf + pos + 2);
 939
 940    if (aTag == tag && (pos + 4 + blocksize) <= buflen) {
 941      *aBlockSize = blocksize;
 942      return buf + pos;
 943    }
 944
 945    pos += blocksize + 4;
 946  }
 947
 948MOZ_WIN_MEM_TRY_CATCH(return nsnull)
 949  return nsnull;
 950}
 951
 952
 953PRTime nsZipItem::LastModTime()
 954{
 955  if (isSynthetic) return GetModTime(kSyntheticDate, kSyntheticTime);
 956
 957  // Try to read timestamp from extra field
 958  PRUint16 blocksize;
 959  const PRUint8 *tsField = GetExtraField(EXTENDED_TIMESTAMP_FIELD, &blocksize);
 960  if (tsField && blocksize >= 5 && tsField[4] & EXTENDED_TIMESTAMP_MODTIME) {
 961    return (PRTime)(xtolong(tsField + 5)) * PR_USEC_PER_SEC;
 962  }
 963
 964  return GetModTime(Date(), Time());
 965}
 966
 967#ifdef XP_UNIX
 968bool nsZipItem::IsSymlink()
 969{
 970  if (isSynthetic) return false;
 971  return (xtoint(central->external_attributes+2) & S_IFMT) == S_IFLNK;
 972}
 973#endif
 974
 975nsZipCursor::nsZipCursor(nsZipItem *item, nsZipArchive *aZip, PRUint8* aBuf, PRUint32 aBufSize, bool doCRC) :
 976  mItem(item),
 977  mBuf(aBuf),
 978  mBufSize(aBufSize),
 979  mDoCRC(doCRC)
 980{
 981  if (mItem->Compression() == DEFLATED) {
 982#ifdef DEBUG
 983    nsresult status =
 984#endif
 985      gZlibInit(&mZs);
 986    NS_ASSERTION(status == NS_OK, "Zlib failed to initialize");
 987    NS_ASSERTION(aBuf, "Must pass in a buffer for DEFLATED nsZipItem");
 988  }
 989  
 990  mZs.avail_in = item->Size();
 991  mZs.next_in = (Bytef*)aZip->GetData(item);
 992  
 993  if (doCRC)
 994    mCRC = crc32(0L, Z_NULL, 0);
 995}
 996
 997nsZipCursor::~nsZipCursor()
 998{
 999  if (mItem->Compression() == DEFLATED) {
1000    inflateEnd(&mZs);
1001  }
1002}
1003
1004PRUint8* nsZipCursor::ReadOrCopy(PRUint32 *aBytesRead, bool aCopy) {
1005  int zerr;
1006  PRUint8 *buf = nsnull;
1007  bool verifyCRC = true;
1008
1009  if (!mZs.next_in)
1010    return nsnull;
1011MOZ_WIN_MEM_TRY_BEGIN
1012  switch (mItem->Compression()) {
1013  case STORED:
1014    if (!aCopy) {
1015      *aBytesRead = mZs.avail_in;
1016      buf = mZs.next_in;
1017      mZs.next_in += mZs.avail_in;
1018      mZs.avail_in = 0;
1019    } else {
1020      *aBytesRead = mZs.avail_in > mBufSize ? mBufSize : mZs.avail_in;
1021      memcpy(mBuf, mZs.next_in, *aBytesRead);
1022      mZs.avail_in -= *aBytesRead;
1023      mZs.next_in += *aBytesRead;
1024    }
1025    break;
1026  case DEFLATED:
1027    buf = mBuf;
1028    mZs.next_out = buf;
1029    mZs.avail_out = mBufSize;
1030    
1031    zerr = inflate(&mZs, Z_PARTIAL_FLUSH);
1032    if (zerr != Z_OK && zerr != Z_STREAM_END)
1033      return nsnull;
1034    
1035    *aBytesRead = mZs.next_out - buf;
1036    verifyCRC = (zerr == Z_STREAM_END);
1037    break;
1038  default:
1039    return nsnull;
1040  }
1041
1042  if (mDoCRC) {
1043    mCRC = crc32(mCRC, (const unsigned char*)buf, *aBytesRead);
1044    if (verifyCRC && mCRC != mItem->CRC32())
1045      return nsnull;
1046  }
1047MOZ_WIN_MEM_TRY_CATCH(return nsnull)
1048  return buf;
1049}
1050
1051nsZipItemPtr_base::nsZipItemPtr_base(nsZipArchive *aZip, const char * aEntryName, bool doCRC) :
1052  mReturnBuf(nsnull)
1053{
1054  // make sure the ziparchive hangs around
1055  mZipHandle = aZip->GetFD();
1056
1057  nsZipItem* item = aZip->GetItem(aEntryName);
1058  if (!item)
1059    return;
1060
1061  PRUint32 size = 0;
1062  if (item->Compression() == DEFLATED) {
1063    size = item->RealSize();
1064    mAutoBuf = new PRUint8[size];
1065  }
1066
1067  nsZipCursor cursor(item, aZip, mAutoBuf, size, doCRC);
1068  mReturnBuf = cursor.Read(&mReadlen);
1069  if (!mReturnBuf) {
1070    return;
1071  }
1072
1073  if (mReadlen != item->RealSize()) {
1074    NS_ASSERTION(mReadlen == item->RealSize(), "nsZipCursor underflow");
1075    mReturnBuf = nsnull;
1076    return;
1077  }
1078}