Backout bug 449422.
[wine-gecko.git] / modules / libjar / nsZipArchive.cpp
blob4f786c1235039892ae07f22b394523f7291b057f
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>
30 * Alternatively, the contents of this file may be used under the terms of
31 * either the GNU General Public License Version 2 or later (the "GPL"), or
32 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
33 * in which case the provisions of the GPL or the LGPL are applicable instead
34 * of those above. If you wish to allow use of your version of this file only
35 * under the terms of either the GPL or the LGPL, and not to allow others to
36 * use your version of this file under the terms of the MPL, indicate your
37 * decision by deleting the provisions above and replace them with the notice
38 * and other provisions required by the GPL or the LGPL. If you do not delete
39 * the provisions above, a recipient may use your version of this file under
40 * the terms of any one of the MPL, the GPL or the LGPL.
42 * ***** END LICENSE BLOCK ***** */
45 * This module implements a simple archive extractor for the PKZIP format.
47 * The underlying nsZipArchive is NOT thread-safe. Do not pass references
48 * or pointers to it across thread boundaries.
52 #ifndef STANDALONE
54 #include "nsWildCard.h"
55 #include "nscore.h"
56 #include "prmem.h"
57 #include "prio.h"
58 #include "plstr.h"
59 #include "prlog.h"
60 #define ZFILE_CREATE PR_WRONLY | PR_CREATE_FILE
61 #define READTYPE PRInt32
62 #include "zlib.h"
63 #include "nsISupportsUtils.h"
64 #include "nsRecyclingAllocator.h"
66 /**
67 * Globals
69 * Global allocator used with zlib. Destroyed in module shutdown.
71 #define NBUCKETS 6
72 #define BY4ALLOC_ITEMS 320
73 nsRecyclingAllocator *gZlibAllocator = NULL;
75 // For placement new used for arena allocations of zip file list
76 #include NEW_H
77 #define ZIP_ARENABLOCKSIZE (1*1024)
79 #else /* STANDALONE */
81 #ifdef XP_WIN
82 #include "windows.h"
83 #endif
85 #undef MOZILLA_CLIENT // undoes prtypes damage in zlib.h
86 #define ZFILE_CREATE "wb"
87 #define READTYPE PRUint32
88 #include "zlib.h"
89 #undef PR_PUBLIC_API
90 #include "zipstub.h"
92 #ifdef XP_MAC
93 #include <string.h>
94 #include <stdlib.h>
96 char * strdup(const char *src);
97 char * strdup(const char *src)
99 long len = strlen(src);
100 char *dup = (char *)malloc(len+1 * sizeof(char));
101 memcpy(dup, src, len+1);
102 return dup;
104 #endif
106 #endif /* STANDALONE */
108 #ifdef XP_UNIX
109 #include <sys/types.h>
110 #include <sys/stat.h>
111 #include <limits.h>
112 #include <unistd.h>
113 #elif defined(XP_WIN) || defined(XP_OS2)
114 #include <io.h>
115 #elif defined(XP_BEOS)
116 #include <unistd.h>
117 #endif
119 #ifndef XP_UNIX /* we need some constants defined in limits.h and unistd.h */
120 # ifndef S_IFMT
121 # define S_IFMT 0170000
122 # endif
123 # ifndef S_IFLNK
124 # define S_IFLNK 0120000
125 # endif
126 # ifndef PATH_MAX
127 # define PATH_MAX 1024
128 # endif
129 #endif /* XP_UNIX */
131 #include "zipfile.h"
132 #include "zipstruct.h"
133 #include "nsZipArchive.h"
135 static PRUint16 xtoint(unsigned char *ii);
136 static PRUint32 xtolong(unsigned char *ll);
137 static PRUint16 ExtractMode(unsigned char *ll);
138 static PRUint32 HashName(const char* aName);
139 #if defined(XP_UNIX) || defined(XP_BEOS)
140 static PRBool IsSymlink(unsigned char *ll);
141 static nsresult ResolveSymlink(const char *path);
142 #endif
144 /*---------------------------------------------
145 * C API wrapper for nsZipArchive
146 *--------------------------------------------*/
148 #ifdef STANDALONE
151 * ZIP_OpenArchive
153 * opens the named zip/jar archive and returns a handle that
154 * represents the archive in other ZIP_ calls.
156 * @param zipname archive filename
157 * @param hZip receives handle if archive opened OK
158 * @return status code
160 PR_PUBLIC_API(PRInt32) ZIP_OpenArchive(const char * zipname, void** hZip)
162 PRInt32 status;
164 /*--- error check args ---*/
165 if (hZip == 0)
166 return ZIP_ERR_PARAM;
168 /*--- NULL output to prevent use by bozos who don't check errors ---*/
169 *hZip = 0;
171 /*--- create and open the archive ---*/
172 nsZipArchive* zip = new nsZipArchive();
173 if (zip == 0)
174 return ZIP_ERR_MEMORY;
176 PRFileDesc * fd = PR_Open(zipname, PR_RDONLY, 0400);
177 if (!fd) {
178 delete zip;
179 return ZIP_ERR_DISK;
182 status = zip->OpenArchive(fd);
183 if (status == ZIP_OK)
184 *hZip = static_cast<void*>(zip);
185 else {
186 delete zip;
187 PR_Close(fd);
190 return status;
196 * ZIP_TestArchive
198 * Tests the integrity of this open zip archive by extracting each
199 * item to memory and performing a CRC check.
201 * @param hZip handle obtained from ZIP_OpenArchive
202 * @return status code (success indicated by ZIP_OK)
204 PR_PUBLIC_API(PRInt32) ZIP_TestArchive(void *hZip)
206 /*--- error check args ---*/
207 if (hZip == 0)
208 return ZIP_ERR_PARAM;
210 nsZipArchive* zip = static_cast<nsZipArchive*>(hZip);
211 if (zip->kMagic != ZIP_MAGIC)
212 return ZIP_ERR_PARAM; /* whatever it is isn't one of ours! */
214 /*--- test the archive ---*/
215 return zip->Test(NULL);
220 * ZIP_CloseArchive
222 * closes zip archive and frees memory
223 * @param hZip handle obtained from ZIP_OpenArchive
224 * @return status code
226 PR_PUBLIC_API(PRInt32) ZIP_CloseArchive(void** hZip)
228 /*--- error check args ---*/
229 if (hZip == 0 || *hZip == 0)
230 return ZIP_ERR_PARAM;
232 nsZipArchive* zip = static_cast<nsZipArchive*>(*hZip);
233 if (zip->kMagic != ZIP_MAGIC)
234 return ZIP_ERR_PARAM; /* whatever it is isn't one of ours! */
236 /*--- close the archive ---*/
237 *hZip = 0;
238 delete zip;
240 return ZIP_OK;
246 * ZIP_ExtractFile
248 * extracts named file from an opened archive
250 * @param hZip handle obtained from ZIP_OpenArchive
251 * @param filename name of file in archive
252 * @param outname filename to extract to
254 PR_PUBLIC_API(PRInt32) ZIP_ExtractFile(void* hZip, const char * filename, const char * outname)
256 /*--- error check args ---*/
257 if (hZip == 0)
258 return ZIP_ERR_PARAM;
260 nsZipArchive* zip = static_cast<nsZipArchive*>(hZip);
261 if (zip->kMagic != ZIP_MAGIC)
262 return ZIP_ERR_PARAM; /* whatever it is isn't one of ours! */
264 //-- Find item in archive
265 nsZipItem* item = zip->GetItem(filename);
266 if (!item)
267 return ZIP_ERR_FNF;
269 // Can't extract a directory
270 if (item->isDirectory)
271 return ZIP_ERR_PARAM;
273 // delete any existing file so that we overwrite the file permissions
274 PR_Delete(outname);
276 PRFileDesc* fOut = PR_Open(outname, ZFILE_CREATE, item->mode);
277 if (!fOut)
278 return ZIP_ERR_DISK;
280 #if defined(XP_UNIX) && defined(STANDALONE)
281 // When STANDALONE is defined, PR_Open ignores its 3d argument.
282 mode_t msk = umask(0);
283 umask(msk);
284 chmod(outname, (item->mode | S_IRUSR) & ~msk);
285 #endif
287 // ExtractFile also closes the fOut handle and resolves the symlink if needed
288 return zip->ExtractFile(item, outname, fOut);
294 * ZIP_FindInit
296 * Initializes an enumeration of files in the archive
298 * @param hZip handle obtained from ZIP_OpenArchive
299 * @param pattern regexp to match files in archive, the usual shell expressions.
300 * NULL pattern also matches all files, faster than "*"
302 PR_PUBLIC_API(void*) ZIP_FindInit(void* hZip, const char * pattern)
304 /*--- error check args ---*/
305 if (hZip == 0)
306 return 0;
308 nsZipArchive* zip = static_cast<nsZipArchive*>(hZip);
309 if (zip->kMagic != ZIP_MAGIC)
310 return 0; /* whatever it is isn't one of ours! */
312 /*--- initialize the pattern search ---*/
313 nsZipFind* find;
314 PRInt32 rv = zip->FindInit(pattern, &find);
315 if (rv != ZIP_OK)
316 find = NULL;
318 return find;
324 * ZIP_FindNext
326 * Puts the next name in the passed buffer. Returns ZIP_ERR_SMALLBUF when
327 * the name is too large for the buffer, and ZIP_ERR_FNF when there are no
328 * more files that match the pattern
330 * @param hFind handle obtained from ZIP_FindInit
331 * @param outbuf buffer to receive next filename
332 * @param bufsize size of allocated buffer
334 PR_PUBLIC_API(PRInt32) ZIP_FindNext(void* hFind, char * outbuf, PRUint16 bufsize)
336 PRInt32 status;
338 /*--- error check args ---*/
339 if (hFind == 0)
340 return ZIP_ERR_PARAM;
342 nsZipFind* find = static_cast<nsZipFind*>(hFind);
343 if (find->kMagic != ZIPFIND_MAGIC)
344 return ZIP_ERR_PARAM; /* whatever it is isn't one of ours! */
346 /*--- return next filename file ---*/
347 const char* itemName;
348 status = find->FindNext(&itemName);
349 if (status == ZIP_OK)
351 PRUint16 namelen = (PRUint16)PL_strlen(itemName);
353 if (bufsize > namelen)
355 PL_strcpy(outbuf, itemName);
357 else
358 status = ZIP_ERR_SMALLBUF;
361 return status;
367 * ZIP_FindFree
369 * Releases allocated memory associated with the find token
371 * @param hFind handle obtained from ZIP_FindInit
373 PR_PUBLIC_API(PRInt32) ZIP_FindFree(void* hFind)
375 /*--- error check args ---*/
376 if (hFind == 0)
377 return ZIP_ERR_PARAM;
379 nsZipFind* find = static_cast<nsZipFind*>(hFind);
380 if (find->kMagic != ZIPFIND_MAGIC)
381 return ZIP_ERR_PARAM; /* whatever it is isn't one of ours! */
383 /* free the find structure */
384 delete find;
385 return ZIP_OK;
388 #if defined XP_WIN
389 void ProcessWindowsMessages()
391 MSG msg;
393 while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
395 TranslateMessage(&msg);
396 DispatchMessage(&msg);
399 #endif /* XP_WIN */
401 #else /* STANDALONE */
403 //***********************************************************
404 // Allocators for use with zlib
406 // These are allocators that are performance tuned for
407 // use with zlib. Our use of zlib for every file we read from
408 // the jar file when running navigator, we do these allocation.
409 // alloc 24
410 // alloc 64
411 // alloc 11520
412 // alloc 32768
413 // alloc 1216 [304x4] max
414 // alloc 76 [19x4]
415 // free 76 [19x4]
416 // alloc 1152 [288x4]
417 // free 1152 [288x4]
418 // free 1216 [304x4]
419 // alloc 28
420 // free 28
421 // free 32768
422 // free 11520
423 // free 64
424 // free 24
426 // The pool will allocate these as:
428 // 32,768
429 // 11,520
430 // 1,280 [320x4] - shared by first x4 alloc, 28
431 // 1,280 [320x4] - shared by second and third x4 alloc
432 // 64
433 // 24
434 // ------
435 // 46,936
437 // And almost all of the file reads happen serially. Hence this
438 // allocator tries to keep one set of memory needed for one file around
439 // and reused the same blocks for other file reads.
441 // The interesting question is when should be free this ?
442 // - memory pressure should be one.
443 // - after startup of navigator
444 // - after startup of mail
445 // In general, this allocator should be enabled before
446 // we startup and disabled after we startup if memory is a concern.
447 //***********************************************************
449 PR_STATIC_CALLBACK(void *)
450 zlibAlloc(void *opaque, uInt items, uInt size)
452 nsRecyclingAllocator *zallocator = (nsRecyclingAllocator *)opaque;
453 if (zallocator) {
454 // Bump up x4 allocations
455 PRUint32 realitems = items;
456 if (size == 4 && items < BY4ALLOC_ITEMS)
457 realitems = BY4ALLOC_ITEMS;
458 return zallocator->Calloc(realitems, size);
460 else
461 return calloc(items, size);
464 PR_STATIC_CALLBACK(void)
465 zlibFree(void *opaque, void *ptr)
467 nsRecyclingAllocator *zallocator = (nsRecyclingAllocator *)opaque;
468 if (zallocator)
469 zallocator->Free(ptr);
470 else
471 free(ptr);
472 return;
474 #endif /* STANDALONE */
476 nsresult gZlibInit(z_stream *zs)
478 memset(zs, 0, sizeof(z_stream));
479 #ifndef STANDALONE
480 //-- ensure we have our zlib allocator for better performance
481 if (!gZlibAllocator) {
482 gZlibAllocator = new nsRecyclingAllocator(NBUCKETS, NS_DEFAULT_RECYCLE_TIMEOUT, "libjar");
484 if (gZlibAllocator) {
485 zs->zalloc = zlibAlloc;
486 zs->zfree = zlibFree;
487 zs->opaque = gZlibAllocator;
489 #endif /* STANDALONE */
490 int zerr = inflateInit2(zs, -MAX_WBITS);
491 if (zerr != Z_OK) return ZIP_ERR_MEMORY;
493 return ZIP_OK;
496 //***********************************************************
497 // nsZipArchive -- public methods
498 //***********************************************************
501 //---------------------------------------------
502 // nsZipArchive::OpenArchive
503 //---------------------------------------------
504 nsresult nsZipArchive::OpenArchive(PRFileDesc * fd)
506 if (!fd)
507 return ZIP_ERR_PARAM;
509 #ifndef STANDALONE
510 // Initialize our arena
511 PL_INIT_ARENA_POOL(&mArena, "ZipArena", ZIP_ARENABLOCKSIZE);
512 #endif
514 //-- Keep the filedescriptor for further reading...
515 mFd = fd;
517 //-- get table of contents for archive
518 return BuildFileList();
521 //---------------------------------------------
522 // nsZipArchive::Test
523 //---------------------------------------------
524 nsresult nsZipArchive::Test(const char *aEntryName)
526 nsZipItem* currItem;
528 if (aEntryName) // only test specified item
530 currItem = GetItem(aEntryName);
531 if (!currItem)
532 return ZIP_ERR_FNF;
533 //-- don't test (synthetic) directory items
534 if (currItem->isDirectory)
535 return ZIP_OK;
536 return ExtractFile(currItem, 0, 0);
539 // test all items in archive
540 for (int i = 0; i < ZIP_TABSIZE; i++) {
541 for (currItem = mFiles[i]; currItem; currItem = currItem->next) {
542 //-- don't test (synthetic) directory items
543 if (currItem->isDirectory)
544 continue;
545 nsresult rv = ExtractFile(currItem, 0, 0);
546 if (rv != ZIP_OK)
547 return rv;
548 #if defined STANDALONE && defined XP_WIN
549 ProcessWindowsMessages();
550 #endif
554 return ZIP_OK;
557 //---------------------------------------------
558 // nsZipArchive::CloseArchive
559 //---------------------------------------------
560 nsresult nsZipArchive::CloseArchive()
562 #ifndef STANDALONE
563 if (mFd) {
564 PL_FinishArenaPool(&mArena);
567 // CAUTION:
568 // We don't need to delete each of the nsZipItem as the memory for
569 // the zip item and the filename it holds are both allocated from the Arena.
570 // Hence, destroying the Arena is like destroying all the memory
571 // for all the nsZipItem in one shot. But if the ~nsZipItem is doing
572 // anything more than cleaning up memory, we should start calling it.
573 // Let us also cleanup the mFiles table for re-use on the next 'open' call
574 for (int i = 0; i < ZIP_TABSIZE; i++) {
575 mFiles[i] = 0;
577 #else
578 // delete nsZipItems in table
579 nsZipItem* pItem;
580 for (int i = 0; i < ZIP_TABSIZE; ++i)
582 pItem = mFiles[i];
583 while (pItem != 0)
585 mFiles[i] = pItem->next;
586 free(pItem);
587 pItem = mFiles[i];
589 mFiles[i] = 0; // make sure we don't double-delete
591 #endif
593 if (mFd) {
594 PR_Close(mFd);
595 mFd = 0;
597 mBuiltSynthetics = PR_FALSE;
598 return ZIP_OK;
601 //---------------------------------------------
602 // nsZipArchive::GetItem
603 //---------------------------------------------
604 nsZipItem* nsZipArchive::GetItem(const char * aEntryName)
606 if (aEntryName) {
607 //-- If the request is for a directory, make sure that synthetic entries
608 //-- are created for the directories without their own entry.
609 if (!mBuiltSynthetics) {
610 PRUint32 len = strlen(aEntryName);
611 if ((len > 0) && (aEntryName[len-1] == '/')) {
612 if (BuildSynthetics() != ZIP_OK)
613 return 0;
617 nsZipItem* item = mFiles[ HashName(aEntryName) ];
618 while (item) {
619 if (!strcmp(aEntryName, item->name))
620 return item; //-- found it
621 item = item->next;
624 return 0;
627 //---------------------------------------------
628 // nsZipArchive::ExtractFile
629 // This extracts the item to the filehandle provided.
630 // If 'aFd' is null, it only tests the extraction.
631 // On extraction error(s) it removes the file.
632 // When needed, it also resolves the symlink.
633 //---------------------------------------------
634 nsresult nsZipArchive::ExtractFile(nsZipItem *item, const char *outname,
635 PRFileDesc* aFd)
637 if (!item)
638 return ZIP_ERR_PARAM;
639 if (!mFd)
640 return ZIP_ERR_GENERAL;
642 // Directory extraction is handled in nsJAR::Extract,
643 // so the item to be extracted should never be a directory
644 PR_ASSERT(!item->isDirectory);
646 //-- move to the start of file's data
647 if (SeekToItem(item, mFd) != ZIP_OK)
648 return ZIP_ERR_CORRUPT;
650 nsresult rv;
652 //-- extract the file using the appropriate method
653 switch(item->compression)
655 case STORED:
656 rv = CopyItemToDisk(item->size, item->crc32, aFd);
657 break;
659 case DEFLATED:
660 rv = InflateItem(item, aFd);
661 break;
663 default:
664 //-- unsupported compression type
665 rv = ZIP_ERR_UNSUPPORTED;
668 //-- delete the file on errors, or resolve symlink if needed
669 if (aFd) {
670 PR_Close(aFd);
671 if (rv != ZIP_OK)
672 PR_Delete(outname);
673 #if defined(XP_UNIX) || defined(XP_BEOS)
674 else if (item->isSymlink)
675 rv = ResolveSymlink(outname);
676 #endif
679 return rv;
682 //---------------------------------------------
683 // nsZipArchive::FindInit
684 //---------------------------------------------
685 PRInt32
686 nsZipArchive::FindInit(const char * aPattern, nsZipFind **aFind)
688 if (!aFind)
689 return ZIP_ERR_PARAM;
691 // null out param in case an error happens
692 *aFind = NULL;
694 PRBool regExp = PR_FALSE;
695 char* pattern = 0;
697 // Create synthetic directory entries on demand
698 nsresult rv = BuildSynthetics();
699 if (rv != ZIP_OK)
700 return rv;
702 // validate the pattern
703 if (aPattern)
705 switch (NS_WildCardValid((char*)aPattern))
707 case INVALID_SXP:
708 return ZIP_ERR_PARAM;
710 case NON_SXP:
711 regExp = PR_FALSE;
712 break;
714 case VALID_SXP:
715 regExp = PR_TRUE;
716 break;
718 default:
719 // undocumented return value from RegExpValid!
720 PR_ASSERT(PR_FALSE);
721 return ZIP_ERR_PARAM;
724 pattern = PL_strdup(aPattern);
725 if (!pattern)
726 return ZIP_ERR_MEMORY;
729 *aFind = new nsZipFind(this, pattern, regExp);
730 if (!*aFind) {
731 PR_FREEIF(pattern);
732 return ZIP_ERR_MEMORY;
735 return ZIP_OK;
740 //---------------------------------------------
741 // nsZipFind::FindNext
742 //---------------------------------------------
743 nsresult nsZipFind::FindNext(const char ** aResult)
745 if (!mArchive || !aResult)
746 return ZIP_ERR_PARAM;
748 *aResult = 0;
750 // we start from last match, look for next
751 while (mSlot < ZIP_TABSIZE)
753 // move to next in current chain, or move to new slot
754 mItem = mItem ? mItem->next : mArchive->mFiles[mSlot];
756 PRBool found = PR_FALSE;
757 if (!mItem)
758 ++mSlot; // no more in this chain, move to next slot
759 else if (!mPattern)
760 found = PR_TRUE; // always match
761 else if (mRegExp)
762 found = (NS_WildCardMatch(mItem->name, mPattern, PR_FALSE) == MATCH);
763 else
764 #if defined(STANDALONE) && defined(XP_MAC)
765 // simulate <regexp>* matches
766 found = (strncmp(mItem->name, mPattern, strlen(mPattern)) == 0);
767 #else
768 found = (PL_strcmp(mItem->name, mPattern) == 0);
769 #endif
771 if (found) {
772 *aResult = mItem->name;
773 return ZIP_OK;
777 return ZIP_ERR_FNF;
780 #if defined(XP_UNIX) || defined(XP_BEOS)
781 //---------------------------------------------
782 // ResolveSymlink
783 //---------------------------------------------
784 static nsresult ResolveSymlink(const char *path)
786 PRFileDesc * fIn = PR_Open(path, PR_RDONLY, 0000);
787 if (!fIn)
788 return ZIP_ERR_DISK;
790 char buf[PATH_MAX+1];
791 PRInt32 length = PR_Read(fIn, (void*)buf, PATH_MAX);
792 PR_Close(fIn);
794 if ( (length <= 0)
795 || ((buf[length] = 0, PR_Delete(path)) != 0)
796 || (symlink(buf, path) != 0))
798 return ZIP_ERR_DISK;
800 return ZIP_OK;
802 #endif
804 //***********************************************************
805 // nsZipArchive -- private implementation
806 //***********************************************************
808 #define BR_BUF_SIZE 1024 /* backward read buffer size */
810 //---------------------------------------------
811 // nsZipArchive::CreateZipItem
812 //---------------------------------------------
813 nsZipItem* nsZipArchive::CreateZipItem(PRUint16 namelen)
815 // sizeof(nsZipItem) includes space for name's null byte
816 #ifndef STANDALONE
817 // Arena allocate the nsZipItem
818 void *mem;
819 PL_ARENA_ALLOCATE(mem, &mArena, sizeof(nsZipItem)+namelen);
820 return (nsZipItem*)mem;
821 #else
822 return (nsZipItem*)malloc(sizeof(nsZipItem)+namelen);
823 #endif
826 //---------------------------------------------
827 // nsZipArchive::BuildFileList
828 //---------------------------------------------
829 nsresult nsZipArchive::BuildFileList()
831 PRUint8 buf[4*BR_BUF_SIZE];
833 //-----------------------------------------------------------------------
834 // locate the central directory via the End record
835 //-----------------------------------------------------------------------
837 //-- get archive size using end pos
838 PRInt32 pos = PR_Seek(mFd, 0, PR_SEEK_END);
839 #ifndef STANDALONE
840 if (pos <= 0)
841 #else
842 if (pos || ((pos = ftell(mFd)) <= 0))
843 #endif
844 return ZIP_ERR_CORRUPT;
846 PRBool bEndsigFound = PR_FALSE;
847 while (!bEndsigFound)
849 //-- read backwards in 1K-sized chunks (unless file is less than 1K)
850 PRInt32 bufsize = pos > BR_BUF_SIZE ? BR_BUF_SIZE : pos;
851 pos -= bufsize;
853 if (!ZIP_Seek(mFd, pos, PR_SEEK_SET))
854 return ZIP_ERR_CORRUPT;
856 if (PR_Read(mFd, buf, bufsize) != (READTYPE)bufsize)
857 return ZIP_ERR_CORRUPT;
859 //-- scan for ENDSIG
860 PRUint8 *endp = buf + bufsize;
861 for (endp -= ZIPEND_SIZE; endp >= buf; endp--)
863 if (xtolong(endp) == ENDSIG)
865 //-- Seek to start of central directory
866 PRInt32 central = xtolong(((ZipEnd *) endp)->offset_central_dir);
867 if (!ZIP_Seek(mFd, central, PR_SEEK_SET))
868 return ZIP_ERR_CORRUPT;
870 bEndsigFound = PR_TRUE;
871 break;
875 if (bEndsigFound)
876 break;
878 if (pos <= 0)
879 //-- We're at the beginning of the file, and still no sign
880 //-- of the end signature. File must be corrupted!
881 return ZIP_ERR_CORRUPT;
883 //-- backward read must overlap ZipEnd length
884 pos += ZIPEND_SIZE;
886 } /* while looking for end signature */
889 //-------------------------------------------------------
890 // read the central directory headers
891 //-------------------------------------------------------
892 PRInt32 byteCount = PR_Read(mFd, &buf, sizeof(buf));
893 pos = 0;
894 PRUint32 sig = xtolong(buf);
895 while (sig == CENTRALSIG) {
896 //-- make sure we've read enough
897 if (byteCount - pos < ZIPCENTRAL_SIZE)
898 return ZIP_ERR_CORRUPT;
900 //-------------------------------------------------------
901 // read the fixed-size data
902 //-------------------------------------------------------
903 ZipCentral* central = (ZipCentral*)(buf+pos);
905 PRUint16 namelen = xtoint(central->filename_len);
906 PRUint16 extralen = xtoint(central->extrafield_len);
907 PRUint16 commentlen = xtoint(central->commentfield_len);
909 //-- sanity check variable sizes and refuse to deal with
910 //-- anything too big: it's likely a corrupt archive
911 if (namelen > BR_BUF_SIZE || extralen > BR_BUF_SIZE || commentlen > 2*BR_BUF_SIZE)
912 return ZIP_ERR_CORRUPT;
914 nsZipItem* item = CreateZipItem(namelen);
915 if (!item)
916 return ZIP_ERR_MEMORY;
918 item->headerOffset = xtolong(central->localhdr_offset);
919 item->dataOffset = 0;
920 item->size = xtolong(central->size);
921 item->realsize = xtolong(central->orglen);
922 item->crc32 = xtolong(central->crc32);
923 item->time = xtoint(central->time);
924 item->date = xtoint(central->date);
925 item->isSynthetic = PR_FALSE;
926 item->hasDataOffset = PR_FALSE;
928 PRUint16 compression = xtoint(central->method);
929 item->compression = (compression < UNSUPPORTED) ? (PRUint8)compression
930 : UNSUPPORTED;
932 item->mode = ExtractMode(central->external_attributes);
933 #if defined(XP_UNIX) || defined(XP_BEOS)
934 // Check if item is a symlink
935 item->isSymlink = IsSymlink(central->external_attributes);
936 #endif
938 pos += ZIPCENTRAL_SIZE;
940 //-------------------------------------------------------
941 // Make sure that remainder of this record (name, comments, extra)
942 // and the next ZipCentral is all in the buffer
943 //-------------------------------------------------------
944 PRInt32 leftover = byteCount - pos;
945 if (leftover < (namelen + extralen + commentlen + ZIPCENTRAL_SIZE)) {
946 //-- not enough data left to process at top of loop.
947 //-- move leftover and read more
948 memcpy(buf, buf+pos, leftover);
949 byteCount = leftover + PR_Read(mFd, buf+leftover, sizeof(buf)-leftover);
950 pos = 0;
952 if (byteCount < (namelen + extralen + commentlen + sizeof(sig))) {
953 // truncated file
954 return ZIP_ERR_CORRUPT;
958 //-------------------------------------------------------
959 // get the item name
960 //-------------------------------------------------------
961 memcpy(item->name, buf+pos, namelen);
962 item->name[namelen] = 0;
964 //-- an item whose name ends with '/' is a directory
965 item->isDirectory = ('/' == item->name[namelen - 1]);
967 //-- add item to file table
968 //-- note that an explicit entry for a directory will override
969 //-- a fake entry created for that directory (as in the case
970 //-- of processing foo/bar.txt and then foo/) -- this will
971 //-- preserve an explicit directory's metadata at the cost of
972 //-- an extra nsZipItem (and that only happens if we process a
973 //-- file inside that directory before processing the directory
974 //-- entry itself)
975 PRUint32 hash = HashName(item->name);
976 item->next = mFiles[hash];
977 mFiles[hash] = item;
979 //-------------------------------------------------------
980 // set up to process the next item at the top of loop
981 //-------------------------------------------------------
982 pos += namelen + extralen + commentlen;
983 sig = xtolong(buf+pos);
984 } /* while reading central directory records */
986 if (sig != ENDSIG)
987 return ZIP_ERR_CORRUPT;
989 return ZIP_OK;
992 //---------------------------------------------
993 // nsZipArchive::BuildSynthetics
994 //---------------------------------------------
995 nsresult nsZipArchive::BuildSynthetics()
997 if (mBuiltSynthetics)
998 return ZIP_OK;
999 mBuiltSynthetics = PR_TRUE;
1001 // Create synthetic entries for any missing directories.
1002 // Do this when all ziptable has scanned to prevent double entries.
1003 for (int i = 0; i < ZIP_TABSIZE; ++i)
1005 for (nsZipItem* item = mFiles[i]; item != 0; item = item->next)
1007 if (item->isSynthetic)
1008 continue;
1010 //-- add entries for directories in the current item's path
1011 //-- go from end to beginning, because then we can stop trying
1012 //-- to create diritems if we find that the diritem we want to
1013 //-- create already exists
1014 //-- start just before the last char so as to not add the item
1015 //-- twice if it's a directory
1016 PRUint16 namelen = strlen(item->name);
1017 for (char* p = item->name + namelen - 2; p >= item->name; p--)
1019 if ('/' != *p)
1020 continue;
1022 // See whether we need to create any more implicit directories,
1023 // because if we don't we can avoid a lot of work.
1024 // We can even avoid (de)allocating space for a bogus dirname with
1025 // a little trickery -- save the char at item->name[dirnamelen],
1026 // set it to 0, compare the strings, and restore the saved
1027 // char when done
1028 const PRUint32 dirnamelen = p + 1 - item->name;
1029 const char savedChar = item->name[dirnamelen];
1030 item->name[dirnamelen] = 0;
1032 // Is the directory in the file table?
1033 PRUint32 hash = HashName(item->name);
1034 PRBool found = PR_FALSE;
1035 for (nsZipItem* zi = mFiles[hash]; zi != NULL; zi = zi->next)
1037 if (0 == strcmp(item->name, zi->name))
1039 // we've already added this dir and all its parents
1040 found = PR_TRUE;
1041 break;
1045 // restore the char immediately
1046 item->name[dirnamelen] = savedChar;
1048 // if the directory was found, break out of the directory
1049 // creation loop now that we know all implicit directories
1050 // are there -- otherwise, start creating the zip item
1051 if (found)
1052 break;
1054 nsZipItem* diritem = CreateZipItem(dirnamelen);
1055 if (!diritem)
1056 return ZIP_ERR_MEMORY;
1058 memcpy(diritem->name, item->name, dirnamelen);
1059 diritem->name[dirnamelen] = 0;
1061 diritem->isDirectory = PR_TRUE;
1062 diritem->isSynthetic = PR_TRUE;
1063 diritem->compression = STORED;
1064 diritem->size = diritem->realsize = 0;
1065 diritem->crc32 = 0;
1066 diritem->mode = 0755;
1068 // Set an obviously wrong last-modified date/time, because
1069 // finding something more accurate like the most recent
1070 // last-modified date/time of the dir's contents is a lot
1071 // of effort. The date/time corresponds to 1980-01-01 00:00.
1072 diritem->time = 0;
1073 diritem->date = 1 + (1 << 5) + (0 << 9);
1075 // add diritem to the file table
1076 diritem->next = mFiles[hash];
1077 mFiles[hash] = diritem;
1078 } /* end processing of dirs in item's name */
1081 return ZIP_OK;
1085 //---------------------------------------------
1086 // nsZipArchive::SeekToItem
1087 //---------------------------------------------
1088 nsresult nsZipArchive::SeekToItem(nsZipItem* aItem, PRFileDesc* aFd)
1090 PR_ASSERT (aItem);
1092 //-- the first time an item is used we need to calculate its offset
1093 if (!aItem->hasDataOffset)
1095 //-- read local header to get variable length values and calculate
1096 //-- the real data offset
1097 //--
1098 //-- NOTE: extralen is different in central header and local header
1099 //-- for archives created using the Unix "zip" utility. To set
1100 //-- the offset accurately we need the _local_ extralen.
1101 if (!ZIP_Seek(aFd, aItem->headerOffset, PR_SEEK_SET))
1102 return ZIP_ERR_CORRUPT;
1104 ZipLocal Local;
1105 if ((PR_Read(aFd, (char*)&Local, ZIPLOCAL_SIZE) != (READTYPE) ZIPLOCAL_SIZE) ||
1106 (xtolong(Local.signature) != LOCALSIG))
1108 //-- read error or local header not found
1109 return ZIP_ERR_CORRUPT;
1112 aItem->dataOffset = aItem->headerOffset +
1113 ZIPLOCAL_SIZE +
1114 xtoint(Local.filename_len) +
1115 xtoint(Local.extrafield_len);
1116 aItem->hasDataOffset = PR_TRUE;
1119 //-- move to start of file in archive
1120 if (!ZIP_Seek(aFd, aItem->dataOffset, PR_SEEK_SET))
1121 return ZIP_ERR_CORRUPT;
1123 return ZIP_OK;
1126 //---------------------------------------------
1127 // nsZipArchive::CopyItemToDisk
1128 //---------------------------------------------
1129 nsresult
1130 nsZipArchive::CopyItemToDisk(PRUint32 itemSize, PRUint32 itemCrc, PRFileDesc* outFD)
1132 * This function copies an archive item to disk, to the
1133 * file specified by outFD. If outFD is zero, the extracted data is
1134 * not written, only checked for CRC, so this is in effect same as 'Test'.
1137 PRUint32 chunk, pos, crc;
1138 char buf[ZIP_BUFLEN];
1140 //-- initialize crc
1141 crc = crc32(0L, Z_NULL, 0);
1143 //-- copy chunks until file is done
1144 for (pos = 0; pos < itemSize; pos += chunk)
1146 chunk = (itemSize - pos < ZIP_BUFLEN) ? (itemSize - pos) : ZIP_BUFLEN;
1148 if (PR_Read(mFd, buf, chunk) != (READTYPE)chunk)
1150 //-- unexpected end of data in archive
1151 return ZIP_ERR_CORRUPT;
1154 //-- incrementally update crc32
1155 crc = crc32(crc, (const unsigned char*)buf, chunk);
1157 if (outFD && PR_Write(outFD, buf, chunk) < (READTYPE)chunk)
1159 //-- Couldn't write all the data (disk full?)
1160 return ZIP_ERR_DISK;
1164 //-- verify crc32
1165 if (crc != itemCrc)
1166 return ZIP_ERR_CORRUPT;
1168 return ZIP_OK;
1172 //---------------------------------------------
1173 // nsZipArchive::InflateItem
1174 //---------------------------------------------
1175 nsresult nsZipArchive::InflateItem(const nsZipItem* aItem, PRFileDesc* outFD)
1177 * This function inflates an archive item to disk, to the
1178 * file specified by outFD. If outFD is zero, the extracted data is
1179 * not written, only checked for CRC, so this is in effect same as 'Test'.
1182 PR_ASSERT(aItem);
1184 //-- allocate deflation buffers
1185 Bytef inbuf[ZIP_BUFLEN];
1186 Bytef outbuf[ZIP_BUFLEN];
1188 //-- set up the inflate
1189 z_stream zs;
1190 nsresult status = gZlibInit(&zs);
1191 if (status != ZIP_OK)
1192 return ZIP_ERR_GENERAL;
1194 //-- inflate loop
1195 zs.next_out = outbuf;
1196 zs.avail_out = ZIP_BUFLEN;
1198 PRUint32 size = aItem->size;
1199 PRUint32 outpos = 0;
1200 PRUint32 crc = crc32(0L, Z_NULL, 0);
1201 int zerr = Z_OK;
1202 while (zerr == Z_OK)
1204 PRBool bRead = PR_FALSE;
1205 PRBool bWrote= PR_FALSE;
1207 if (zs.avail_in == 0 && zs.total_in < size)
1209 //-- no data to inflate yet still more in file:
1210 //-- read another chunk of compressed data
1211 PRUint32 chunk = (size-zs.total_in < ZIP_BUFLEN) ? size-zs.total_in : ZIP_BUFLEN;
1213 if (PR_Read(mFd, inbuf, chunk) != (READTYPE)chunk)
1215 //-- unexpected end of data
1216 status = ZIP_ERR_CORRUPT;
1217 break;
1220 zs.next_in = inbuf;
1221 zs.avail_in = chunk;
1222 bRead = PR_TRUE;
1225 if (zs.avail_out == 0)
1227 //-- write inflated buffer to disk and make space
1228 if (outFD && PR_Write(outFD, outbuf, ZIP_BUFLEN) < ZIP_BUFLEN)
1230 //-- Couldn't write all the data (disk full?)
1231 status = ZIP_ERR_DISK;
1232 break;
1235 outpos = zs.total_out;
1236 zs.next_out = outbuf;
1237 zs.avail_out = ZIP_BUFLEN;
1238 bWrote = PR_TRUE;
1241 if(bRead || bWrote)
1243 Bytef* old_next_out = zs.next_out;
1245 zerr = inflate(&zs, Z_PARTIAL_FLUSH);
1247 //-- incrementally update crc32
1248 crc = crc32(crc, (const unsigned char*)old_next_out, zs.next_out - old_next_out);
1250 else
1251 zerr = Z_STREAM_END;
1253 #if defined STANDALONE && defined XP_WIN
1254 ProcessWindowsMessages();
1255 #endif
1256 } // while
1258 //-- verify crc32
1259 if ((status == ZIP_OK) && (crc != aItem->crc32))
1261 status = ZIP_ERR_CORRUPT;
1262 goto cleanup;
1265 //-- write last inflated bit to disk
1266 if (zerr == Z_STREAM_END && outpos < zs.total_out)
1268 PRUint32 chunk = zs.total_out - outpos;
1269 if (outFD && PR_Write(outFD, outbuf, chunk) < (READTYPE)chunk)
1270 status = ZIP_ERR_DISK;
1273 //-- convert zlib error to return value
1274 if (status == ZIP_OK && zerr != Z_OK && zerr != Z_STREAM_END)
1276 status = (zerr == Z_MEM_ERROR) ? ZIP_ERR_MEMORY : ZIP_ERR_CORRUPT;
1279 //-- if found no errors make sure we've converted the whole thing
1280 PR_ASSERT(status != ZIP_OK || zs.total_in == aItem->size);
1281 PR_ASSERT(status != ZIP_OK || zs.total_out == aItem->realsize);
1283 cleanup:
1284 //-- free zlib internal state
1285 inflateEnd(&zs);
1287 return status;
1290 //------------------------------------------
1291 // nsZipArchive constructor and destructor
1292 //------------------------------------------
1294 nsZipArchive::nsZipArchive() :
1295 #ifdef STANDALONE
1296 kMagic(ZIP_MAGIC),
1297 #endif
1298 mFd(0),
1299 mBuiltSynthetics(PR_FALSE)
1301 MOZ_COUNT_CTOR(nsZipArchive);
1303 // initialize the table to NULL
1304 memset(mFiles, 0, sizeof mFiles);
1307 nsZipArchive::~nsZipArchive()
1309 CloseArchive();
1311 MOZ_COUNT_DTOR(nsZipArchive);
1315 //------------------------------------------
1316 // nsZipFind constructor and destructor
1317 //------------------------------------------
1319 nsZipFind::nsZipFind(nsZipArchive* aZip, char* aPattern, PRBool aRegExp) :
1320 #ifdef STANDALONE
1321 kMagic(ZIPFIND_MAGIC),
1322 #endif
1323 mArchive(aZip),
1324 mPattern(aPattern),
1325 mItem(0),
1326 mSlot(0),
1327 mRegExp(aRegExp)
1329 MOZ_COUNT_CTOR(nsZipFind);
1332 nsZipFind::~nsZipFind()
1334 PR_FREEIF(mPattern);
1336 MOZ_COUNT_DTOR(nsZipFind);
1339 //------------------------------------------
1340 // helper functions
1341 //------------------------------------------
1344 * HashName
1346 * returns a hash key for the entry name
1348 static PRUint32 HashName(const char* aName)
1350 PR_ASSERT(aName != 0);
1352 PRUint32 val = 0;
1353 for (PRUint8* c = (PRUint8*)aName; *c != 0; c++) {
1354 val = val*37 + *c;
1357 return (val % ZIP_TABSIZE);
1361 * x t o i n t
1363 * Converts a two byte ugly endianed integer
1364 * to our platform's integer.
1366 static PRUint16 xtoint (unsigned char *ii)
1368 return (PRUint16) ((ii [0]) | (ii [1] << 8));
1372 * x t o l o n g
1374 * Converts a four byte ugly endianed integer
1375 * to our platform's integer.
1377 static PRUint32 xtolong (unsigned char *ll)
1379 return (PRUint32)( (ll [0] << 0) |
1380 (ll [1] << 8) |
1381 (ll [2] << 16) |
1382 (ll [3] << 24) );
1386 * ExtractMode
1388 * Extracts bits 17-24 from a 32-bit unsigned long
1389 * representation of the external attributes field.
1390 * Subsequently it tacks on the implicit user-read
1391 * bit.
1393 static PRUint16 ExtractMode(unsigned char *ll)
1395 return ((PRUint16)(ll[2])) | 0x0100;
1398 #if defined(XP_UNIX) || defined(XP_BEOS)
1401 * Return true if the attributes are for a symbolic link
1405 static PRBool IsSymlink(unsigned char *ll)
1407 return ((xtoint(ll+2) & S_IFMT) == S_IFLNK);
1409 #endif