Bug 562144: Make DTrace hooks pretty. (r=sayrer)
[mozilla-central.git] / modules / libjar / nsZipArchive.cpp
blob6be3cff30bd8ec70729617921695557a38c7730c
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
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/
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.
15 * The Original Code is Mozilla Communicator client code, released
16 * March 31, 1998.
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.
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>
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.
43 * ***** END LICENSE BLOCK ***** */
46 * This module implements a simple archive extractor for the PKZIP format.
48 * The underlying nsZipArchive is NOT thread-safe. Do not pass references
49 * or pointers to it across thread boundaries.
52 #define READTYPE PRInt32
53 #include "zlib.h"
54 #include "nsISupportsUtils.h"
55 #include "nsRecyclingAllocator.h"
56 #include "prio.h"
57 #include "plstr.h"
58 #include "prlog.h"
59 #include "stdlib.h"
60 #include "nsWildCard.h"
61 #include "nsZipArchive.h"
63 /**
64 * Global allocator used with zlib. Destroyed in module shutdown.
66 #define NBUCKETS 6
67 nsRecyclingAllocator *gZlibAllocator = NULL;
69 // For placement new used for arena allocations of zip file list
70 #include NEW_H
71 #define ZIP_ARENABLOCKSIZE (1*1024)
73 #ifdef XP_UNIX
74 #include <sys/types.h>
75 #include <sys/stat.h>
76 #include <limits.h>
77 #include <unistd.h>
78 #elif defined(XP_WIN) || defined(XP_OS2)
79 #include <io.h>
80 #elif defined(XP_BEOS)
81 #include <unistd.h>
82 #endif
84 #ifdef __SYMBIAN32__
85 #include <sys/syslimits.h>
86 #endif /*__SYMBIAN32__*/
89 #ifndef XP_UNIX /* we need some constants defined in limits.h and unistd.h */
90 # ifndef S_IFMT
91 # define S_IFMT 0170000
92 # endif
93 # ifndef S_IFLNK
94 # define S_IFLNK 0120000
95 # endif
96 # ifndef PATH_MAX
97 # define PATH_MAX 1024
98 # endif
99 #endif /* XP_UNIX */
102 static const PRUint32 kMaxNameLength = PATH_MAX; /* Maximum name length */
103 // For synthetic zip entries. Date/time corresponds to 1980-01-01 00:00.
104 static const PRUint16 kSyntheticTime = 0;
105 static const PRUint16 kSyntheticDate = (1 + (1 << 5) + (0 << 9));
107 static PRUint16 xtoint(const PRUint8 *ii);
108 static PRUint32 xtolong(const PRUint8 *ll);
109 static PRUint32 HashName(const char* aName, PRUint16 nameLen);
110 #if defined(XP_UNIX) || defined(XP_BEOS)
111 static nsresult ResolveSymlink(const char *path);
112 #endif
114 //***********************************************************
115 // Allocators for use with zlib
117 // Use a recycling allocator, for re-use of of the zlib buffers.
118 // For every inflation the following allocations are done:
119 // zlibAlloc(1, 9520)
120 // zlibAlloc(32768, 1)
121 //***********************************************************
123 static void *
124 zlibAlloc(void *opaque, uInt items, uInt size)
126 nsRecyclingAllocator *zallocator = (nsRecyclingAllocator *)opaque;
127 if (zallocator) {
128 return gZlibAllocator->Malloc(items * size);
130 return malloc(items * size);
133 static void
134 zlibFree(void *opaque, void *ptr)
136 nsRecyclingAllocator *zallocator = (nsRecyclingAllocator *)opaque;
137 if (zallocator)
138 zallocator->Free(ptr);
139 else
140 free(ptr);
143 nsresult gZlibInit(z_stream *zs)
145 memset(zs, 0, sizeof(z_stream));
146 //-- ensure we have our zlib allocator for better performance
147 if (!gZlibAllocator) {
148 gZlibAllocator = new nsRecyclingAllocator(NBUCKETS, NS_DEFAULT_RECYCLE_TIMEOUT, "libjar");
150 if (gZlibAllocator) {
151 zs->zalloc = zlibAlloc;
152 zs->zfree = zlibFree;
153 zs->opaque = gZlibAllocator;
155 int zerr = inflateInit2(zs, -MAX_WBITS);
156 if (zerr != Z_OK) return NS_ERROR_OUT_OF_MEMORY;
158 return NS_OK;
161 nsZipHandle::nsZipHandle()
162 : mFileData(nsnull)
163 , mLen(0)
164 , mMap(nsnull)
165 , mRefCnt(0)
167 MOZ_COUNT_CTOR(nsZipHandle);
170 NS_IMPL_THREADSAFE_ADDREF(nsZipHandle)
171 NS_IMPL_THREADSAFE_RELEASE(nsZipHandle)
173 nsresult nsZipHandle::Init(PRFileDesc *fd, nsZipHandle **ret)
175 PRInt64 size = PR_Available64(fd);
176 if (size >= PR_INT32_MAX)
177 return NS_ERROR_FILE_TOO_BIG;
179 PRFileMap *map = PR_CreateFileMap(fd, size, PR_PROT_READONLY);
180 if (!map)
181 return NS_ERROR_FAILURE;
183 PRUint8 *buf = (PRUint8*) PR_MemMap(map, 0, (PRUint32) size);
184 // Bug 525755: PR_MemMap fails when fd points at something other than a normal file.
185 if (!buf) {
186 PR_CloseFileMap(map);
187 return NS_ERROR_FAILURE;
190 nsZipHandle *handle = new nsZipHandle();
191 if (!handle) {
192 PR_MemUnmap(buf, size);
193 PR_CloseFileMap(map);
194 return NS_ERROR_OUT_OF_MEMORY;
197 handle->mMap = map;
198 handle->mLen = (PRUint32) size;
199 handle->mFileData = buf;
200 handle->AddRef();
201 *ret = handle;
202 return NS_OK;
205 nsZipHandle::~nsZipHandle()
207 if (mFileData) {
208 PR_MemUnmap(mFileData, mLen);
209 PR_CloseFileMap(mMap);
210 mFileData = nsnull;
211 mMap = nsnull;
213 MOZ_COUNT_DTOR(nsZipHandle);
216 //***********************************************************
217 // nsZipArchive -- public methods
218 //***********************************************************
221 //---------------------------------------------
222 // nsZipArchive::OpenArchive
223 //---------------------------------------------
224 nsresult nsZipArchive::OpenArchive(nsIFile *aZipFile)
226 nsresult rv;
227 nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(aZipFile, &rv);
228 if (NS_FAILED(rv)) return rv;
230 PRFileDesc* fd;
231 rv = localFile->OpenNSPRFileDesc(PR_RDONLY, 0000, &fd);
232 if (NS_FAILED(rv)) return rv;
234 rv = nsZipHandle::Init(fd, getter_AddRefs(mFd));
235 PR_Close(fd);
236 if (NS_FAILED(rv))
237 return rv;
239 // Initialize our arena
240 PL_INIT_ARENA_POOL(&mArena, "ZipArena", ZIP_ARENABLOCKSIZE);
242 //-- get table of contents for archive
243 return BuildFileList();
246 //---------------------------------------------
247 // nsZipArchive::Test
248 //---------------------------------------------
249 nsresult nsZipArchive::Test(const char *aEntryName)
251 nsZipItem* currItem;
253 if (aEntryName) // only test specified item
255 currItem = GetItem(aEntryName);
256 if (!currItem)
257 return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
258 //-- don't test (synthetic) directory items
259 if (currItem->IsDirectory())
260 return NS_OK;
261 return ExtractFile(currItem, 0, 0);
264 // test all items in archive
265 for (int i = 0; i < ZIP_TABSIZE; i++) {
266 for (currItem = mFiles[i]; currItem; currItem = currItem->next) {
267 //-- don't test (synthetic) directory items
268 if (currItem->IsDirectory())
269 continue;
270 nsresult rv = ExtractFile(currItem, 0, 0);
271 if (rv != NS_OK)
272 return rv;
276 return NS_OK;
279 //---------------------------------------------
280 // nsZipArchive::CloseArchive
281 //---------------------------------------------
282 nsresult nsZipArchive::CloseArchive()
284 if (mFd) {
285 PL_FinishArenaPool(&mArena);
286 mFd = NULL;
289 // CAUTION:
290 // We don't need to delete each of the nsZipItem as the memory for
291 // the zip item and the filename it holds are both allocated from the Arena.
292 // Hence, destroying the Arena is like destroying all the memory
293 // for all the nsZipItem in one shot. But if the ~nsZipItem is doing
294 // anything more than cleaning up memory, we should start calling it.
295 // Let us also cleanup the mFiles table for re-use on the next 'open' call
296 memset(mFiles, 0, sizeof(mFiles));
297 mBuiltSynthetics = false;
298 return NS_OK;
301 //---------------------------------------------
302 // nsZipArchive::GetItem
303 //---------------------------------------------
304 nsZipItem* nsZipArchive::GetItem(const char * aEntryName)
306 if (aEntryName) {
307 PRUint32 len = strlen(aEntryName);
308 //-- If the request is for a directory, make sure that synthetic entries
309 //-- are created for the directories without their own entry.
310 if (!mBuiltSynthetics) {
311 if ((len > 0) && (aEntryName[len-1] == '/')) {
312 if (BuildSynthetics() != NS_OK)
313 return 0;
317 nsZipItem* item = mFiles[ HashName(aEntryName, len) ];
318 while (item) {
319 if ((len == item->nameLength) &&
320 (!memcmp(aEntryName, item->Name(), len)))
321 return item; //-- found it
322 item = item->next;
325 return 0;
328 //---------------------------------------------
329 // nsZipArchive::ExtractFile
330 // This extracts the item to the filehandle provided.
331 // If 'aFd' is null, it only tests the extraction.
332 // On extraction error(s) it removes the file.
333 // When needed, it also resolves the symlink.
334 //---------------------------------------------
335 nsresult nsZipArchive::ExtractFile(nsZipItem *item, const char *outname,
336 PRFileDesc* aFd)
338 if (!item)
339 return NS_ERROR_ILLEGAL_VALUE;
340 if (!mFd)
341 return NS_ERROR_FAILURE;
343 // Directory extraction is handled in nsJAR::Extract,
344 // so the item to be extracted should never be a directory
345 PR_ASSERT(!item->IsDirectory());
347 nsresult rv;
349 //-- extract the file using the appropriate method
350 switch(item->Compression())
352 case STORED:
353 rv = CopyItemToDisk(item, aFd);
354 break;
356 case DEFLATED:
357 rv = InflateItem(item, aFd);
358 break;
360 default:
361 //-- unsupported compression type
362 rv = NS_ERROR_NOT_IMPLEMENTED;
365 //-- delete the file on errors, or resolve symlink if needed
366 if (aFd) {
367 PR_Close(aFd);
368 if (rv != NS_OK)
369 PR_Delete(outname);
370 #if defined(XP_UNIX) || defined(XP_BEOS)
371 else if (item->IsSymlink())
372 rv = ResolveSymlink(outname);
373 #endif
376 return rv;
379 //---------------------------------------------
380 // nsZipArchive::FindInit
381 //---------------------------------------------
382 PRInt32
383 nsZipArchive::FindInit(const char * aPattern, nsZipFind **aFind)
385 if (!aFind)
386 return NS_ERROR_ILLEGAL_VALUE;
388 // null out param in case an error happens
389 *aFind = NULL;
391 PRBool regExp = PR_FALSE;
392 char* pattern = 0;
394 // Create synthetic directory entries on demand
395 nsresult rv = BuildSynthetics();
396 if (rv != NS_OK)
397 return rv;
399 // validate the pattern
400 if (aPattern)
402 switch (NS_WildCardValid((char*)aPattern))
404 case INVALID_SXP:
405 return NS_ERROR_ILLEGAL_VALUE;
407 case NON_SXP:
408 regExp = PR_FALSE;
409 break;
411 case VALID_SXP:
412 regExp = PR_TRUE;
413 break;
415 default:
416 // undocumented return value from RegExpValid!
417 PR_ASSERT(PR_FALSE);
418 return NS_ERROR_ILLEGAL_VALUE;
421 pattern = PL_strdup(aPattern);
422 if (!pattern)
423 return NS_ERROR_OUT_OF_MEMORY;
426 *aFind = new nsZipFind(this, pattern, regExp);
427 if (!*aFind) {
428 PL_strfree(pattern);
429 return NS_ERROR_OUT_OF_MEMORY;
432 return NS_OK;
437 //---------------------------------------------
438 // nsZipFind::FindNext
439 //---------------------------------------------
440 nsresult nsZipFind::FindNext(const char ** aResult, PRUint16 *aNameLen)
442 if (!mArchive || !aResult || !aNameLen)
443 return NS_ERROR_ILLEGAL_VALUE;
445 *aResult = 0;
446 *aNameLen = 0;
448 // we start from last match, look for next
449 while (mSlot < ZIP_TABSIZE)
451 // move to next in current chain, or move to new slot
452 mItem = mItem ? mItem->next : mArchive->mFiles[mSlot];
454 PRBool found = PR_FALSE;
455 if (!mItem)
456 ++mSlot; // no more in this chain, move to next slot
457 else if (!mPattern)
458 found = PR_TRUE; // always match
459 else if (mRegExp)
461 char buf[kMaxNameLength+1];
462 memcpy(buf, mItem->Name(), mItem->nameLength);
463 buf[mItem->nameLength]='\0';
464 found = (NS_WildCardMatch(buf, mPattern, PR_FALSE) == MATCH);
466 else
467 found = ((mItem->nameLength == strlen(mPattern)) &&
468 (memcmp(mItem->Name(), mPattern, mItem->nameLength) == 0));
469 if (found) {
470 // Need also to return the name length, as it is NOT zero-terminatdd...
471 *aResult = mItem->Name();
472 *aNameLen = mItem->nameLength;
473 return NS_OK;
477 return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
480 #if defined(XP_UNIX) || defined(XP_BEOS)
481 //---------------------------------------------
482 // ResolveSymlink
483 //---------------------------------------------
484 static nsresult ResolveSymlink(const char *path)
486 PRFileDesc * fIn = PR_Open(path, PR_RDONLY, 0000);
487 if (!fIn)
488 return NS_ERROR_FILE_DISK_FULL;
490 char buf[PATH_MAX+1];
491 PRInt32 length = PR_Read(fIn, (void*)buf, PATH_MAX);
492 PR_Close(fIn);
494 if ( (length <= 0)
495 || ((buf[length] = 0, PR_Delete(path)) != 0)
496 || (symlink(buf, path) != 0))
498 return NS_ERROR_FILE_DISK_FULL;
500 return NS_OK;
502 #endif
504 //***********************************************************
505 // nsZipArchive -- private implementation
506 //***********************************************************
508 //---------------------------------------------
509 // nsZipArchive::CreateZipItem
510 //---------------------------------------------
511 nsZipItem* nsZipArchive::CreateZipItem()
513 // Arena allocate the nsZipItem
514 void *mem;
515 PL_ARENA_ALLOCATE(mem, &mArena, sizeof(nsZipItem));
516 return (nsZipItem*)mem;
519 //---------------------------------------------
520 // nsZipArchive::BuildFileList
521 //---------------------------------------------
522 nsresult nsZipArchive::BuildFileList()
524 // Get archive size using end pos
525 PRUint8* buf;
526 PRUint8* startp = mFd->mFileData;
527 PRUint8* endp = startp + mFd->mLen;
529 PRUint32 centralOffset = 0;
530 for (buf = endp - ZIPEND_SIZE; buf > startp; buf--)
532 if (xtolong(buf) == ENDSIG) {
533 centralOffset = xtolong(((ZipEnd *)buf)->offset_central_dir);
534 break;
538 if (!centralOffset)
539 return NS_ERROR_FILE_CORRUPTED;
541 //-- Read the central directory headers
542 buf = startp + centralOffset;
543 if (endp - buf < PRInt32(sizeof(PRUint32)))
544 return NS_ERROR_FILE_CORRUPTED;
545 PRUint32 sig = xtolong(buf);
546 while (sig == CENTRALSIG) {
547 // Make sure there is enough data available.
548 if (endp - buf < ZIPCENTRAL_SIZE)
549 return NS_ERROR_FILE_CORRUPTED;
551 // Read the fixed-size data.
552 ZipCentral* central = (ZipCentral*)buf;
554 PRUint16 namelen = xtoint(central->filename_len);
555 PRUint16 extralen = xtoint(central->extrafield_len);
556 PRUint16 commentlen = xtoint(central->commentfield_len);
558 // Point to the next item at the top of loop
559 buf += ZIPCENTRAL_SIZE + namelen + extralen + commentlen;
561 // Sanity check variable sizes and refuse to deal with
562 // anything too big: it's likely a corrupt archive.
563 if (namelen > kMaxNameLength || buf >= endp)
564 return NS_ERROR_FILE_CORRUPTED;
566 nsZipItem* item = CreateZipItem();
567 if (!item)
568 return NS_ERROR_OUT_OF_MEMORY;
570 item->central = central;
571 item->nameLength = namelen;
572 item->isSynthetic = false;
574 // Add item to file table
575 PRUint32 hash = HashName(item->Name(), namelen);
576 item->next = mFiles[hash];
577 mFiles[hash] = item;
579 sig = xtolong(buf);
580 } /* while reading central directory records */
582 if (sig != ENDSIG)
583 return NS_ERROR_FILE_CORRUPTED;
584 return NS_OK;
587 //---------------------------------------------
588 // nsZipArchive::BuildSynthetics
589 //---------------------------------------------
590 nsresult nsZipArchive::BuildSynthetics()
592 if (mBuiltSynthetics)
593 return NS_OK;
594 mBuiltSynthetics = true;
596 // Create synthetic entries for any missing directories.
597 // Do this when all ziptable has scanned to prevent double entries.
598 for (int i = 0; i < ZIP_TABSIZE; ++i)
600 for (nsZipItem* item = mFiles[i]; item != 0; item = item->next)
602 if (item->isSynthetic)
603 continue;
605 //-- add entries for directories in the current item's path
606 //-- go from end to beginning, because then we can stop trying
607 //-- to create diritems if we find that the diritem we want to
608 //-- create already exists
609 //-- start just before the last char so as to not add the item
610 //-- twice if it's a directory
611 PRUint16 namelen = item->nameLength;
612 const char *name = item->Name();
613 for (PRUint16 dirlen = namelen - 1; dirlen > 0; dirlen--)
615 if (name[dirlen-1] != '/')
616 continue;
618 // Is the directory already in the file table?
619 PRUint32 hash = HashName(item->Name(), dirlen);
620 PRBool found = PR_FALSE;
621 for (nsZipItem* zi = mFiles[hash]; zi != NULL; zi = zi->next)
623 if ((dirlen == zi->nameLength) &&
624 (0 == memcmp(item->Name(), zi->Name(), dirlen)))
626 // we've already added this dir and all its parents
627 found = PR_TRUE;
628 break;
631 // if the directory was found, break out of the directory
632 // creation loop now that we know all implicit directories
633 // are there -- otherwise, start creating the zip item
634 if (found)
635 break;
637 nsZipItem* diritem = CreateZipItem();
638 if (!diritem)
639 return NS_ERROR_OUT_OF_MEMORY;
641 // Point to the central record of the original item for the name part.
642 diritem->central = item->central;
643 diritem->nameLength = dirlen;
644 diritem->isSynthetic = true;
646 // add diritem to the file table
647 diritem->next = mFiles[hash];
648 mFiles[hash] = diritem;
649 } /* end processing of dirs in item's name */
652 return NS_OK;
655 nsZipHandle* nsZipArchive::GetFD()
657 if (!mFd)
658 return NULL;
659 return mFd.get();
662 //---------------------------------------------
663 // nsZipArchive::GetData
664 //---------------------------------------------
665 PRUint8* nsZipArchive::GetData(nsZipItem* aItem)
667 PR_ASSERT (aItem);
669 //-- read local header to get variable length values and calculate
670 //-- the real data offset
671 PRUint32 len = mFd->mLen;
672 PRUint8* data = mFd->mFileData;
673 PRUint32 offset = aItem->LocalOffset();
674 if (offset + ZIPLOCAL_SIZE > len)
675 return nsnull;
677 // -- check signature before using the structure, in case the zip file is corrupt
678 ZipLocal* Local = (ZipLocal*)(data + offset);
679 if ((xtolong(Local->signature) != LOCALSIG))
680 return nsnull;
682 //-- NOTE: extralen is different in central header and local header
683 //-- for archives created using the Unix "zip" utility. To set
684 //-- the offset accurately we need the _local_ extralen.
685 offset += ZIPLOCAL_SIZE +
686 xtoint(Local->filename_len) +
687 xtoint(Local->extrafield_len);
689 // -- check if there is enough source data in the file
690 if (offset + aItem->Size() > len)
691 return nsnull;
693 return data + offset;
696 //---------------------------------------------
697 // nsZipArchive::CopyItemToDisk
698 //---------------------------------------------
699 nsresult
700 nsZipArchive::CopyItemToDisk(nsZipItem *item, PRFileDesc* outFD)
702 PR_ASSERT(item);
704 //-- get to the start of file's data
705 const PRUint8* itemData = GetData(item);
706 if (!itemData)
707 return NS_ERROR_FILE_CORRUPTED;
709 if (outFD && PR_Write(outFD, itemData, item->Size()) < (READTYPE)item->Size())
711 //-- Couldn't write all the data (disk full?)
712 return NS_ERROR_FILE_DISK_FULL;
715 //-- Calculate crc
716 PRUint32 crc = crc32(0L, (const unsigned char*)itemData, item->Size());
717 //-- verify crc32
718 if (crc != item->CRC32())
719 return NS_ERROR_FILE_CORRUPTED;
721 return NS_OK;
725 //---------------------------------------------
726 // nsZipArchive::InflateItem
727 //---------------------------------------------
728 nsresult nsZipArchive::InflateItem(nsZipItem * item, PRFileDesc* outFD)
730 * This function inflates an archive item to disk, to the
731 * file specified by outFD. If outFD is zero, the extracted data is
732 * not written, only checked for CRC, so this is in effect same as 'Test'.
735 PR_ASSERT(item);
736 //-- allocate deflation buffers
737 Bytef outbuf[ZIP_BUFLEN];
739 //-- set up the inflate
740 z_stream zs;
741 nsresult status = gZlibInit(&zs);
742 if (status != NS_OK)
743 return NS_ERROR_FAILURE;
745 //-- inflate loop
746 zs.avail_in = item->Size();
747 zs.next_in = (Bytef*)GetData(item);
748 if (!zs.next_in)
749 return NS_ERROR_FILE_CORRUPTED;
751 PRUint32 crc = crc32(0L, Z_NULL, 0);
752 int zerr = Z_OK;
753 while (zerr == Z_OK)
755 zs.next_out = outbuf;
756 zs.avail_out = ZIP_BUFLEN;
758 zerr = inflate(&zs, Z_PARTIAL_FLUSH);
759 if (zerr != Z_OK && zerr != Z_STREAM_END)
761 status = (zerr == Z_MEM_ERROR) ? NS_ERROR_OUT_OF_MEMORY : NS_ERROR_FILE_CORRUPTED;
762 break;
764 PRUint32 count = zs.next_out - outbuf;
766 //-- incrementally update crc32
767 crc = crc32(crc, (const unsigned char*)outbuf, count);
769 if (outFD && PR_Write(outFD, outbuf, count) < (READTYPE)count)
771 status = NS_ERROR_FILE_DISK_FULL;
772 break;
774 } // while
776 //-- free zlib internal state
777 inflateEnd(&zs);
779 //-- verify crc32
780 if ((status == NS_OK) && (crc != item->CRC32()))
782 status = NS_ERROR_FILE_CORRUPTED;
784 return status;
787 //------------------------------------------
788 // nsZipArchive constructor and destructor
789 //------------------------------------------
791 nsZipArchive::nsZipArchive() :
792 mBuiltSynthetics(false)
794 MOZ_COUNT_CTOR(nsZipArchive);
796 // initialize the table to NULL
797 memset(mFiles, 0, sizeof(mFiles));
800 nsZipArchive::~nsZipArchive()
802 CloseArchive();
804 MOZ_COUNT_DTOR(nsZipArchive);
808 //------------------------------------------
809 // nsZipFind constructor and destructor
810 //------------------------------------------
812 nsZipFind::nsZipFind(nsZipArchive* aZip, char* aPattern, PRBool aRegExp) :
813 mArchive(aZip),
814 mPattern(aPattern),
815 mItem(0),
816 mSlot(0),
817 mRegExp(aRegExp)
819 MOZ_COUNT_CTOR(nsZipFind);
822 nsZipFind::~nsZipFind()
824 PL_strfree(mPattern);
826 MOZ_COUNT_DTOR(nsZipFind);
829 //------------------------------------------
830 // helper functions
831 //------------------------------------------
834 * HashName
836 * returns a hash key for the entry name
838 static PRUint32 HashName(const char* aName, PRUint16 len)
840 PR_ASSERT(aName != 0);
842 const PRUint8* p = (const PRUint8*)aName;
843 const PRUint8* endp = p + len;
844 PRUint32 val = 0;
845 while (p != endp) {
846 val = val*37 + *p++;
849 return (val % ZIP_TABSIZE);
853 * x t o i n t
855 * Converts a two byte ugly endianed integer
856 * to our platform's integer.
858 static PRUint16 xtoint (const PRUint8 *ii)
860 return (PRUint16) ((ii [0]) | (ii [1] << 8));
864 * x t o l o n g
866 * Converts a four byte ugly endianed integer
867 * to our platform's integer.
869 static PRUint32 xtolong (const PRUint8 *ll)
871 return (PRUint32)( (ll [0] << 0) |
872 (ll [1] << 8) |
873 (ll [2] << 16) |
874 (ll [3] << 24) );
878 * GetModTime
880 * returns last modification time in microseconds
882 static PRTime GetModTime(PRUint16 aDate, PRUint16 aTime)
884 // Note that on DST shift we can't handle correctly the hour that is valid
885 // in both DST zones
886 PRExplodedTime time;
888 time.tm_usec = 0;
890 time.tm_hour = (aTime >> 11) & 0x1F;
891 time.tm_min = (aTime >> 5) & 0x3F;
892 time.tm_sec = (aTime & 0x1F) * 2;
894 time.tm_year = (aDate >> 9) + 1980;
895 time.tm_month = ((aDate >> 5) & 0x0F) - 1;
896 time.tm_mday = aDate & 0x1F;
898 time.tm_params.tp_gmt_offset = 0;
899 time.tm_params.tp_dst_offset = 0;
901 PR_NormalizeTime(&time, PR_GMTParameters);
902 time.tm_params.tp_gmt_offset = PR_LocalTimeParameters(&time).tp_gmt_offset;
903 PR_NormalizeTime(&time, PR_GMTParameters);
904 time.tm_params.tp_dst_offset = PR_LocalTimeParameters(&time).tp_dst_offset;
906 return PR_ImplodeTime(&time);
909 PRUint32 nsZipItem::LocalOffset()
911 return xtolong(central->localhdr_offset);
914 PRUint32 nsZipItem::Size()
916 return isSynthetic ? 0 : xtolong(central->size);
919 PRUint32 nsZipItem::RealSize()
921 return isSynthetic ? 0 : xtolong(central->orglen);
924 PRUint32 nsZipItem::CRC32()
926 return isSynthetic ? 0 : xtolong(central->crc32);
929 PRUint16 nsZipItem::Date()
931 return isSynthetic ? kSyntheticDate : xtoint(central->date);
934 PRUint16 nsZipItem::Time()
936 return isSynthetic ? kSyntheticTime : xtoint(central->time);
939 PRUint16 nsZipItem::Compression()
941 return isSynthetic ? STORED : xtoint(central->method);
944 bool nsZipItem::IsDirectory()
946 return isSynthetic || ((nameLength > 0) && ('/' == Name()[nameLength - 1]));
949 PRUint16 nsZipItem::Mode()
951 if (isSynthetic) return 0755;
952 return ((PRUint16)(central->external_attributes[2]) | 0x100);
955 const PRUint8 * nsZipItem::GetExtraField(PRUint16 aTag, PRUint16 *aBlockSize)
957 if (isSynthetic) return NULL;
959 const unsigned char *buf = ((const unsigned char*)central) + ZIPCENTRAL_SIZE +
960 nameLength;
961 PRUint32 buflen = (PRUint32)xtoint(central->extrafield_len);
962 PRUint32 pos = 0;
963 PRUint16 tag, blocksize;
965 while (buf && (pos + 4) <= buflen) {
966 tag = xtoint(buf + pos);
967 blocksize = xtoint(buf + pos + 2);
969 if (aTag == tag && (pos + 4 + blocksize) <= buflen) {
970 *aBlockSize = blocksize;
971 return buf + pos;
974 pos += blocksize + 4;
977 return NULL;
981 PRTime nsZipItem::LastModTime()
983 if (isSynthetic) return GetModTime(kSyntheticDate, kSyntheticTime);
985 // Try to read timestamp from extra field
986 PRUint16 blocksize;
987 const PRUint8 *tsField = GetExtraField(EXTENDED_TIMESTAMP_FIELD, &blocksize);
988 if (tsField && blocksize >= 5 && tsField[4] & EXTENDED_TIMESTAMP_MODTIME) {
989 return (PRTime)(xtolong(tsField + 5)) * PR_USEC_PER_SEC;
992 return GetModTime(Date(), Time());
995 #if defined(XP_UNIX) || defined(XP_BEOS)
996 bool nsZipItem::IsSymlink()
998 if (isSynthetic) return false;
999 return (xtoint(central->external_attributes+2) & S_IFMT) == S_IFLNK;
1001 #endif