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
15 * The Original Code is Mozilla Communicator client code, released
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.
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
54 #include "nsISupportsUtils.h"
55 #include "nsRecyclingAllocator.h"
60 #include "nsWildCard.h"
61 #include "nsZipArchive.h"
64 * Global allocator used with zlib. Destroyed in module shutdown.
67 nsRecyclingAllocator
*gZlibAllocator
= NULL
;
69 // For placement new used for arena allocations of zip file list
71 #define ZIP_ARENABLOCKSIZE (1*1024)
74 #include <sys/types.h>
78 #elif defined(XP_WIN) || defined(XP_OS2)
80 #elif defined(XP_BEOS)
85 #include <sys/syslimits.h>
86 #endif /*__SYMBIAN32__*/
89 #ifndef XP_UNIX /* we need some constants defined in limits.h and unistd.h */
91 # define S_IFMT 0170000
94 # define S_IFLNK 0120000
97 # define PATH_MAX 1024
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
);
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 //***********************************************************
124 zlibAlloc(void *opaque
, uInt items
, uInt size
)
126 nsRecyclingAllocator
*zallocator
= (nsRecyclingAllocator
*)opaque
;
128 return gZlibAllocator
->Malloc(items
* size
);
130 return malloc(items
* size
);
134 zlibFree(void *opaque
, void *ptr
)
136 nsRecyclingAllocator
*zallocator
= (nsRecyclingAllocator
*)opaque
;
138 zallocator
->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
;
161 nsZipHandle::nsZipHandle()
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
);
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.
186 PR_CloseFileMap(map
);
187 return NS_ERROR_FAILURE
;
190 nsZipHandle
*handle
= new nsZipHandle();
192 PR_MemUnmap(buf
, size
);
193 PR_CloseFileMap(map
);
194 return NS_ERROR_OUT_OF_MEMORY
;
198 handle
->mLen
= (PRUint32
) size
;
199 handle
->mFileData
= buf
;
205 nsZipHandle::~nsZipHandle()
208 PR_MemUnmap(mFileData
, mLen
);
209 PR_CloseFileMap(mMap
);
213 MOZ_COUNT_DTOR(nsZipHandle
);
216 //***********************************************************
217 // nsZipArchive -- public methods
218 //***********************************************************
221 //---------------------------------------------
222 // nsZipArchive::OpenArchive
223 //---------------------------------------------
224 nsresult
nsZipArchive::OpenArchive(nsIFile
*aZipFile
)
227 nsCOMPtr
<nsILocalFile
> localFile
= do_QueryInterface(aZipFile
, &rv
);
228 if (NS_FAILED(rv
)) return rv
;
231 rv
= localFile
->OpenNSPRFileDesc(PR_RDONLY
, 0000, &fd
);
232 if (NS_FAILED(rv
)) return rv
;
234 rv
= nsZipHandle::Init(fd
, getter_AddRefs(mFd
));
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
)
253 if (aEntryName
) // only test specified item
255 currItem
= GetItem(aEntryName
);
257 return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST
;
258 //-- don't test (synthetic) directory items
259 if (currItem
->IsDirectory())
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())
270 nsresult rv
= ExtractFile(currItem
, 0, 0);
279 //---------------------------------------------
280 // nsZipArchive::CloseArchive
281 //---------------------------------------------
282 nsresult
nsZipArchive::CloseArchive()
285 PL_FinishArenaPool(&mArena
);
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;
301 //---------------------------------------------
302 // nsZipArchive::GetItem
303 //---------------------------------------------
304 nsZipItem
* nsZipArchive::GetItem(const char * 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
)
317 nsZipItem
* item
= mFiles
[ HashName(aEntryName
, len
) ];
319 if ((len
== item
->nameLength
) &&
320 (!memcmp(aEntryName
, item
->Name(), len
)))
321 return item
; //-- found it
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
,
339 return NS_ERROR_ILLEGAL_VALUE
;
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());
349 //-- extract the file using the appropriate method
350 switch(item
->Compression())
353 rv
= CopyItemToDisk(item
, aFd
);
357 rv
= InflateItem(item
, aFd
);
361 //-- unsupported compression type
362 rv
= NS_ERROR_NOT_IMPLEMENTED
;
365 //-- delete the file on errors, or resolve symlink if needed
370 #if defined(XP_UNIX) || defined(XP_BEOS)
371 else if (item
->IsSymlink())
372 rv
= ResolveSymlink(outname
);
379 //---------------------------------------------
380 // nsZipArchive::FindInit
381 //---------------------------------------------
383 nsZipArchive::FindInit(const char * aPattern
, nsZipFind
**aFind
)
386 return NS_ERROR_ILLEGAL_VALUE
;
388 // null out param in case an error happens
391 PRBool regExp
= PR_FALSE
;
394 // Create synthetic directory entries on demand
395 nsresult rv
= BuildSynthetics();
399 // validate the pattern
402 switch (NS_WildCardValid((char*)aPattern
))
405 return NS_ERROR_ILLEGAL_VALUE
;
416 // undocumented return value from RegExpValid!
418 return NS_ERROR_ILLEGAL_VALUE
;
421 pattern
= PL_strdup(aPattern
);
423 return NS_ERROR_OUT_OF_MEMORY
;
426 *aFind
= new nsZipFind(this, pattern
, regExp
);
429 return NS_ERROR_OUT_OF_MEMORY
;
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
;
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
;
456 ++mSlot
; // no more in this chain, move to next slot
458 found
= PR_TRUE
; // always match
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
);
467 found
= ((mItem
->nameLength
== strlen(mPattern
)) &&
468 (memcmp(mItem
->Name(), mPattern
, mItem
->nameLength
) == 0));
470 // Need also to return the name length, as it is NOT zero-terminatdd...
471 *aResult
= mItem
->Name();
472 *aNameLen
= mItem
->nameLength
;
477 return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST
;
480 #if defined(XP_UNIX) || defined(XP_BEOS)
481 //---------------------------------------------
483 //---------------------------------------------
484 static nsresult
ResolveSymlink(const char *path
)
486 PRFileDesc
* fIn
= PR_Open(path
, PR_RDONLY
, 0000);
488 return NS_ERROR_FILE_DISK_FULL
;
490 char buf
[PATH_MAX
+1];
491 PRInt32 length
= PR_Read(fIn
, (void*)buf
, PATH_MAX
);
495 || ((buf
[length
] = 0, PR_Delete(path
)) != 0)
496 || (symlink(buf
, path
) != 0))
498 return NS_ERROR_FILE_DISK_FULL
;
504 //***********************************************************
505 // nsZipArchive -- private implementation
506 //***********************************************************
508 //---------------------------------------------
509 // nsZipArchive::CreateZipItem
510 //---------------------------------------------
511 nsZipItem
* nsZipArchive::CreateZipItem()
513 // Arena allocate the nsZipItem
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
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
);
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();
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
];
580 } /* while reading central directory records */
583 return NS_ERROR_FILE_CORRUPTED
;
587 //---------------------------------------------
588 // nsZipArchive::BuildSynthetics
589 //---------------------------------------------
590 nsresult
nsZipArchive::BuildSynthetics()
592 if (mBuiltSynthetics
)
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
)
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] != '/')
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
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
637 nsZipItem
* diritem
= CreateZipItem();
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 */
655 nsZipHandle
* nsZipArchive::GetFD()
662 //---------------------------------------------
663 // nsZipArchive::GetData
664 //---------------------------------------------
665 PRUint8
* nsZipArchive::GetData(nsZipItem
* 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
)
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
))
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
)
693 return data
+ offset
;
696 //---------------------------------------------
697 // nsZipArchive::CopyItemToDisk
698 //---------------------------------------------
700 nsZipArchive::CopyItemToDisk(nsZipItem
*item
, PRFileDesc
* outFD
)
704 //-- get to the start of file's data
705 const PRUint8
* itemData
= GetData(item
);
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
;
716 PRUint32 crc
= crc32(0L, (const unsigned char*)itemData
, item
->Size());
718 if (crc
!= item
->CRC32())
719 return NS_ERROR_FILE_CORRUPTED
;
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'.
736 //-- allocate deflation buffers
737 Bytef outbuf
[ZIP_BUFLEN
];
739 //-- set up the inflate
741 nsresult status
= gZlibInit(&zs
);
743 return NS_ERROR_FAILURE
;
746 zs
.avail_in
= item
->Size();
747 zs
.next_in
= (Bytef
*)GetData(item
);
749 return NS_ERROR_FILE_CORRUPTED
;
751 PRUint32 crc
= crc32(0L, Z_NULL
, 0);
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
;
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
;
776 //-- free zlib internal state
780 if ((status
== NS_OK
) && (crc
!= item
->CRC32()))
782 status
= NS_ERROR_FILE_CORRUPTED
;
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()
804 MOZ_COUNT_DTOR(nsZipArchive
);
808 //------------------------------------------
809 // nsZipFind constructor and destructor
810 //------------------------------------------
812 nsZipFind::nsZipFind(nsZipArchive
* aZip
, char* aPattern
, PRBool aRegExp
) :
819 MOZ_COUNT_CTOR(nsZipFind
);
822 nsZipFind::~nsZipFind()
824 PL_strfree(mPattern
);
826 MOZ_COUNT_DTOR(nsZipFind
);
829 //------------------------------------------
831 //------------------------------------------
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
;
849 return (val
% ZIP_TABSIZE
);
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));
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) |
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
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
+
961 PRUint32 buflen
= (PRUint32
)xtoint(central
->extrafield_len
);
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
;
974 pos
+= blocksize
+ 4;
981 PRTime
nsZipItem::LastModTime()
983 if (isSynthetic
) return GetModTime(kSyntheticDate
, kSyntheticTime
);
985 // Try to read timestamp from extra field
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
;