--- a/modules/libjar/nsJAR.cpp
+++ b/modules/libjar/nsJAR.cpp
@@ -254,24 +254,19 @@ nsJAR::Extract(const char *zipEntry, nsI
nsCAutoString path;
rv = outFile->GetNativePath(path);
if (NS_FAILED(rv)) return rv;
rv = mZip.ExtractFile(item, path.get(), fd);
}
if (NS_FAILED(rv)) return rv;
- PRTime prtime = GetModTime(item->Date(), item->Time());
// nsIFile needs milliseconds, while prtime is in microseconds.
- PRTime conversion = LL_ZERO;
- PRTime newTime = LL_ZERO;
- LL_I2L(conversion, PR_USEC_PER_MSEC);
- LL_DIV(newTime, prtime, conversion);
// non-fatal if this fails, ignore errors
- outFile->SetLastModifiedTime(newTime);
+ outFile->SetLastModifiedTime(item->LastModTime() / PR_USEC_PER_MSEC);
return NS_OK;
}
NS_IMETHODIMP
nsJAR::GetEntry(const char *aEntryName, nsIZipEntry* *result)
{
nsZipItem* zipItem = mZip.GetItem(aEntryName);
@@ -928,18 +923,17 @@ nsJAREnumerator::GetNext(nsACString& aRe
NS_IMPL_THREADSAFE_ISUPPORTS1(nsJARItem, nsIZipEntry)
nsJARItem::nsJARItem(nsZipItem* aZipItem)
: mSize(aZipItem->Size()),
mRealsize(aZipItem->RealSize()),
mCrc32(aZipItem->CRC32()),
- mDate(aZipItem->Date()),
- mTime(aZipItem->Time()),
+ mLastModTime(aZipItem->LastModTime()),
mCompression(aZipItem->Compression()),
mIsDirectory(aZipItem->IsDirectory()),
mIsSynthetic(aZipItem->isSynthetic)
{
}
//------------------------------------------
// nsJARItem::GetCompression
@@ -1016,17 +1010,17 @@ nsJARItem::GetIsSynthetic(PRBool *aIsSyn
//------------------------------------------
// nsJARItem::GetLastModifiedTime
//------------------------------------------
NS_IMETHODIMP
nsJARItem::GetLastModifiedTime(PRTime* aLastModTime)
{
NS_ENSURE_ARG_POINTER(aLastModTime);
- *aLastModTime = GetModTime(mDate, mTime);
+ *aLastModTime = mLastModTime;
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
// nsIZipReaderCache
NS_IMPL_THREADSAFE_ISUPPORTS3(nsZipReaderCache, nsIZipReaderCache, nsIObserver, nsISupportsWeakReference)
@@ -1262,32 +1256,9 @@ nsZipReaderCache::Observe(nsISupports *a
}
else if (strcmp(aTopic, "chrome-flush-caches") == 0) {
mZips.Enumerate(DropZipReaderCache, nsnull);
mZips.Reset();
}
return NS_OK;
}
-PRTime GetModTime(PRUint16 aDate, PRUint16 aTime)
-{
- PRExplodedTime time;
-
- time.tm_usec = 0;
-
- time.tm_hour = (aTime >> 11) & 0x1F;
- time.tm_min = (aTime >> 5) & 0x3F;
- time.tm_sec = (aTime & 0x1F) * 2;
-
- time.tm_year = (aDate >> 9) + 1980;
- time.tm_month = ((aDate >> 5) & 0x0F)-1;
- time.tm_mday = aDate & 0x1F;
-
- time.tm_params.tp_gmt_offset = 0;
- time.tm_params.tp_dst_offset = 0;
-
- PR_NormalizeTime(&time, PR_GMTParameters);
- time.tm_params = PR_LocalTimeParameters(&time);
-
- return PR_ImplodeTime(&time);
-}
-
////////////////////////////////////////////////////////////////////////////////
--- a/modules/libjar/nsJAR.h
+++ b/modules/libjar/nsJAR.h
@@ -80,18 +80,16 @@ typedef enum
JAR_INVALID_SIG = 2,
JAR_INVALID_UNKNOWN_CA = 3,
JAR_INVALID_MANIFEST = 4,
JAR_INVALID_ENTRY = 5,
JAR_NO_MANIFEST = 6,
JAR_NOT_SIGNED = 7
} JARManifestStatusType;
-PRTime GetModTime(PRUint16 aDate, PRUint16 aTime);
-
/*-------------------------------------------------------------------------
* Class nsJAR declaration.
* nsJAR serves as an XPCOM wrapper for nsZipArchive with the addition of
* JAR manifest file parsing.
*------------------------------------------------------------------------*/
class nsJAR : public nsIZipReader, public nsIJAR
{
// Allows nsJARInputStream to call the verification functions
@@ -179,18 +177,17 @@ public:
nsJARItem(nsZipItem* aZipItem);
virtual ~nsJARItem() {}
private:
PRUint32 mSize; /* size in original file */
PRUint32 mRealsize; /* inflated size */
PRUint32 mCrc32;
- PRUint16 mDate;
- PRUint16 mTime;
+ PRTime mLastModTime;
PRUint16 mCompression;
PRPackedBool mIsDirectory;
PRPackedBool mIsSynthetic;
};
/**
* nsJAREnumerator
*
--- a/modules/libjar/nsJARInputStream.cpp
+++ b/modules/libjar/nsJARInputStream.cpp
@@ -341,17 +341,17 @@ nsJARInputStream::ReadDirectory(char* aB
const char * entryName = mArray[mArrPos].get();
PRUint32 entryNameLen = mArray[mArrPos].Length();
nsZipItem* ze = mJar->mZip.GetItem(entryName);
NS_ENSURE_TRUE(ze, NS_ERROR_FILE_TARGET_DOES_NOT_EXIST);
// Last Modified Time
PRExplodedTime tm;
- PR_ExplodeTime(GetModTime(ze->Date(), ze->Time()), PR_GMTParameters, &tm);
+ PR_ExplodeTime(ze->LastModTime(), PR_GMTParameters, &tm);
char itemLastModTime[65];
PR_FormatTimeUSEnglish(itemLastModTime,
sizeof(itemLastModTime),
" %a,%%20%d%%20%b%%20%Y%%20%H:%M:%S%%20GMT ",
&tm);
// write a 201: line to the buffer for this item
// 200: filename content-length last-modified file-type
--- a/modules/libjar/nsZipArchive.cpp
+++ b/modules/libjar/nsZipArchive.cpp
@@ -99,18 +99,18 @@ nsRecyclingAllocator *gZlibAllocator = N
#endif /* XP_UNIX */
static const PRUint32 kMaxNameLength = PATH_MAX; /* Maximum name length */
// For synthetic zip entries. Date/time corresponds to 1980-01-01 00:00.
static const PRUint16 kSyntheticTime = 0;
static const PRUint16 kSyntheticDate = (1 + (1 << 5) + (0 << 9));
-static PRUint16 xtoint(const unsigned char *ii);
-static PRUint32 xtolong(const unsigned char *ll);
+static PRUint16 xtoint(const PRUint8 *ii);
+static PRUint32 xtolong(const PRUint8 *ll);
static PRUint32 HashName(const char* aName, PRUint16 nameLen);
#if defined(XP_UNIX) || defined(XP_BEOS)
static nsresult ResolveSymlink(const char *path);
#endif
//***********************************************************
// Allocators for use with zlib
//
@@ -843,80 +843,153 @@ static PRUint32 HashName(const char* aNa
}
/*
* x t o i n t
*
* Converts a two byte ugly endianed integer
* to our platform's integer.
*/
-static PRUint16 xtoint (const unsigned char *ii)
+static PRUint16 xtoint (const PRUint8 *ii)
{
return (PRUint16) ((ii [0]) | (ii [1] << 8));
}
/*
* x t o l o n g
*
* Converts a four byte ugly endianed integer
* to our platform's integer.
*/
-static PRUint32 xtolong (const unsigned char *ll)
+static PRUint32 xtolong (const PRUint8 *ll)
{
return (PRUint32)( (ll [0] << 0) |
(ll [1] << 8) |
(ll [2] << 16) |
(ll [3] << 24) );
}
-PRUint32 const nsZipItem::LocalOffset()
+/*
+ * GetModTime
+ *
+ * returns last modification time in microseconds
+ */
+static PRTime GetModTime(PRUint16 aDate, PRUint16 aTime)
+{
+ // Note that on DST shift we can't handle correctly the hour that is valid
+ // in both DST zones
+ PRExplodedTime time;
+
+ time.tm_usec = 0;
+
+ time.tm_hour = (aTime >> 11) & 0x1F;
+ time.tm_min = (aTime >> 5) & 0x3F;
+ time.tm_sec = (aTime & 0x1F) * 2;
+
+ time.tm_year = (aDate >> 9) + 1980;
+ time.tm_month = ((aDate >> 5) & 0x0F) - 1;
+ time.tm_mday = aDate & 0x1F;
+
+ time.tm_params.tp_gmt_offset = 0;
+ time.tm_params.tp_dst_offset = 0;
+
+ PR_NormalizeTime(&time, PR_GMTParameters);
+ time.tm_params.tp_gmt_offset = PR_LocalTimeParameters(&time).tp_gmt_offset;
+ PR_NormalizeTime(&time, PR_GMTParameters);
+ time.tm_params.tp_dst_offset = PR_LocalTimeParameters(&time).tp_dst_offset;
+
+ return PR_ImplodeTime(&time);
+}
+
+PRUint32 nsZipItem::LocalOffset()
{
return xtolong(central->localhdr_offset);
}
-PRUint32 const nsZipItem::Size()
+PRUint32 nsZipItem::Size()
{
return isSynthetic ? 0 : xtolong(central->size);
}
-PRUint32 const nsZipItem::RealSize()
+PRUint32 nsZipItem::RealSize()
{
return isSynthetic ? 0 : xtolong(central->orglen);
}
-PRUint32 const nsZipItem::CRC32()
+PRUint32 nsZipItem::CRC32()
{
return isSynthetic ? 0 : xtolong(central->crc32);
}
-PRUint16 const nsZipItem::Date()
+PRUint16 nsZipItem::Date()
{
return isSynthetic ? kSyntheticDate : xtoint(central->date);
}
-PRUint16 const nsZipItem::Time()
+PRUint16 nsZipItem::Time()
{
return isSynthetic ? kSyntheticTime : xtoint(central->time);
}
-PRUint16 const nsZipItem::Compression()
+PRUint16 nsZipItem::Compression()
{
return isSynthetic ? STORED : xtoint(central->method);
}
-bool const nsZipItem::IsDirectory()
+bool nsZipItem::IsDirectory()
{
return isSynthetic || ((nameLength > 0) && ('/' == Name()[nameLength - 1]));
}
-PRUint16 const nsZipItem::Mode()
+PRUint16 nsZipItem::Mode()
{
if (isSynthetic) return 0755;
return ((PRUint16)(central->external_attributes[2]) | 0x100);
}
+const PRUint8 * nsZipItem::GetExtraField(PRUint16 aTag, PRUint16 *aBlockSize)
+{
+ if (isSynthetic) return NULL;
+
+ const unsigned char *buf = ((const unsigned char*)central) + ZIPCENTRAL_SIZE +
+ nameLength;
+ PRUint32 buflen = (PRUint32)xtoint(central->extrafield_len);
+ PRUint32 pos = 0;
+ PRUint16 tag, blocksize;
+
+ while (buf && (pos + 4) <= buflen) {
+ tag = xtoint(buf + pos);
+ blocksize = xtoint(buf + pos + 2);
+
+ if (aTag == tag && (pos + 4 + blocksize) <= buflen) {
+ *aBlockSize = blocksize;
+ return buf + pos;
+ }
+
+ pos += blocksize + 4;
+ }
+
+ return NULL;
+}
+
+
+PRTime nsZipItem::LastModTime()
+{
+ if (isSynthetic) return GetModTime(kSyntheticDate, kSyntheticTime);
+
+ // Try to read timestamp from extra field
+ PRUint16 blocksize;
+ const PRUint8 *tsField = GetExtraField(EXTENDED_TIMESTAMP_FIELD, &blocksize);
+ if (tsField && blocksize >= 5 && tsField[4] & EXTENDED_TIMESTAMP_MODTIME) {
+ return (PRTime)(xtolong(tsField + 5)) * PR_USEC_PER_SEC;
+ }
+
+ return GetModTime(Date(), Time());
+}
+
#if defined(XP_UNIX) || defined(XP_BEOS)
-bool const nsZipItem::IsSymlink()
+bool nsZipItem::IsSymlink()
{
if (isSynthetic) return false;
return (xtoint(central->external_attributes+2) & S_IFMT) == S_IFLNK;
}
#endif
+
--- a/modules/libjar/nsZipArchive.h
+++ b/modules/libjar/nsZipArchive.h
@@ -78,28 +78,30 @@ struct PRFileDesc;
* each nsZipItem represents one file in the archive and all the
* information needed to manipulate it.
*/
class nsZipItem
{
public:
const char* Name() { return ((const char*)central) + ZIPCENTRAL_SIZE; }
- PRUint32 const LocalOffset();
- PRUint32 const Size();
- PRUint32 const RealSize();
- PRUint32 const CRC32();
- PRUint16 const Date();
- PRUint16 const Time();
- PRUint16 const Compression();
- bool const IsDirectory();
- PRUint16 const Mode();
+ PRUint32 LocalOffset();
+ PRUint32 Size();
+ PRUint32 RealSize();
+ PRUint32 CRC32();
+ PRUint16 Date();
+ PRUint16 Time();
+ PRUint16 Compression();
+ bool IsDirectory();
+ PRUint16 Mode();
+ const PRUint8* GetExtraField(PRUint16 aTag, PRUint16 *aBlockSize);
+ PRTime LastModTime();
#if defined(XP_UNIX) || defined(XP_BEOS)
- bool const IsSymlink();
+ bool IsSymlink();
#endif
nsZipItem* next;
const ZipCentral* central;
PRUint16 nameLength;
bool isSynthetic;
};
--- a/modules/libjar/test/unit/test_bug379841.js
+++ b/modules/libjar/test/unit/test_bug379841.js
@@ -2,18 +2,18 @@
const Cc = Components.classes;
const Ci = Components.interfaces;
const path = "data/test_bug379841.zip";
// Retrieved time should be within 2 seconds of original file's time.
const MAX_TIME_DIFF = 2000000;
var ENTRY_NAME = "test";
-// Actual time of file was 07 May 2007 14:35:49
-var ENTRY_TIME = new Date(2007, 4, 7, 14, 35, 49, 0);
+// Actual time of file was 07 May 2007 13:35:49 UTC
+var ENTRY_TIME = new Date(Date.UTC(2007, 4, 7, 13, 35, 49, 0));
function run_test() {
var file = do_get_file(path);
var zipReader = Cc["@mozilla.org/libjar/zip-reader;1"].
createInstance(Ci.nsIZipReader);
zipReader.open(file);
var entry = zipReader.getEntry(ENTRY_NAME);
var diff = Math.abs(entry.lastModifiedTime - ENTRY_TIME.getTime()*1000);
--- a/modules/libjar/zipstruct.h
+++ b/modules/libjar/zipstruct.h
@@ -117,16 +117,20 @@ typedef struct ZipEnd_
*/
#define ZIPEND_SIZE (4+2+2+2+2+4+4+2)
/* signatures */
#define LOCALSIG 0x04034B50l
#define CENTRALSIG 0x02014B50l
#define ENDSIG 0x06054B50l
+/* extra fields */
+#define EXTENDED_TIMESTAMP_FIELD 0x5455
+#define EXTENDED_TIMESTAMP_MODTIME 0x01
+
/* compression methods */
#define STORED 0
#define SHRUNK 1
#define REDUCED1 2
#define REDUCED2 3
#define REDUCED3 4
#define REDUCED4 5
#define IMPLODED 6
--- a/modules/libjar/zipwriter/src/StreamFunctions.h
+++ b/modules/libjar/zipwriter/src/StreamFunctions.h
@@ -45,57 +45,53 @@
#include "nsIOutputStream.h"
/*
* ZIP file data is stored little-endian. These are helper functions to read and
* write little endian data to/from a char buffer.
* The off argument, where present, is incremented according to the number of
* bytes consumed from the buffer.
*/
-inline NS_HIDDEN_(void) WRITE8(char* buf, PRUint32* off, PRUint8 val)
+inline NS_HIDDEN_(void) WRITE8(PRUint8* buf, PRUint32* off, PRUint8 val)
{
- buf[(*off)++] = val & 0xff;
+ buf[(*off)++] = val;
}
-inline NS_HIDDEN_(void) WRITE16(char* buf, PRUint32* off, PRUint16 val)
+inline NS_HIDDEN_(void) WRITE16(PRUint8* buf, PRUint32* off, PRUint16 val)
{
- buf[(*off)++] = val & 0xff;
- buf[(*off)++] = (val >> 8) & 0xff;
+ WRITE8(buf, off, val & 0xff);
+ WRITE8(buf, off, (val >> 8) & 0xff);
}
-inline NS_HIDDEN_(void) WRITE32(char* buf, PRUint32* off, PRUint32 val)
+inline NS_HIDDEN_(void) WRITE32(PRUint8* buf, PRUint32* off, PRUint32 val)
{
- buf[(*off)++] = val & 0xff;
- buf[(*off)++] = (val >> 8) & 0xff;
- buf[(*off)++] = (val >> 16) & 0xff;
- buf[(*off)++] = (val >> 24) & 0xff;
+ WRITE16(buf, off, val & 0xffff);
+ WRITE16(buf, off, (val >> 16) & 0xffff);
}
-inline NS_HIDDEN_(PRUint8) READ8(char* buf, PRUint32* off)
+inline NS_HIDDEN_(PRUint8) READ8(const PRUint8* buf, PRUint32* off)
{
- return (PRUint8)buf[(*off)++];
+ return buf[(*off)++];
}
-inline NS_HIDDEN_(PRUint16) READ16(char* buf, PRUint32* off)
+inline NS_HIDDEN_(PRUint16) READ16(const PRUint8* buf, PRUint32* off)
{
- PRUint16 val = (PRUint16)buf[(*off)++] & 0xff;
- val |= ((PRUint16)buf[(*off)++] & 0xff) << 8;
+ PRUint16 val = READ8(buf, off);
+ val |= READ8(buf, off) << 8;
return val;
}
-inline NS_HIDDEN_(PRUint32) READ32(char* buf, PRUint32* off)
+inline NS_HIDDEN_(PRUint32) READ32(const PRUint8* buf, PRUint32* off)
{
- PRUint32 val = (PRUint32)buf[(*off)++] & 0xff;
- val |= ((PRUint32)buf[(*off)++] & 0xff) << 8;
- val |= ((PRUint32)buf[(*off)++] & 0xff) << 16;
- val |= ((PRUint32)buf[(*off)++] & 0xff) << 24;
+ PRUint32 val = READ16(buf, off);
+ val |= READ16(buf, off) << 16;
return val;
}
-inline NS_HIDDEN_(PRUint32) PEEK32(unsigned char *buf)
+inline NS_HIDDEN_(PRUint32) PEEK32(const PRUint8* buf)
{
return (PRUint32)( (buf [0] ) |
(buf [1] << 8) |
(buf [2] << 16) |
(buf [3] << 24) );
}
NS_HIDDEN_(nsresult) ZW_ReadData(nsIInputStream *aStream, char *aBuffer, PRUint32 aCount);
--- a/modules/libjar/zipwriter/src/nsZipHeader.cpp
+++ b/modules/libjar/zipwriter/src/nsZipHeader.cpp
@@ -42,16 +42,19 @@
#define ZIP_FILE_HEADER_SIGNATURE 0x04034b50
#define ZIP_FILE_HEADER_SIZE 30
#define ZIP_CDS_HEADER_SIGNATURE 0x02014b50
#define ZIP_CDS_HEADER_SIZE 46
#define FLAGS_IS_UTF8 0x800
+#define ZIP_EXTENDED_TIMESTAMP_FIELD 0x5455
+#define ZIP_EXTENDED_TIMESTAMP_MODTIME 0x01
+
/**
* nsZipHeader represents an entry from a zip file.
*/
NS_IMPL_ISUPPORTS1(nsZipHeader, nsIZipEntry)
/* readonly attribute unsigned short compression; */
NS_IMETHODIMP nsZipHeader::GetCompression(PRUint16 *aCompression)
{
@@ -100,33 +103,52 @@ NS_IMETHODIMP nsZipHeader::GetIsDirector
return NS_OK;
}
/* readonly attribute PRTime lastModifiedTime; */
NS_IMETHODIMP nsZipHeader::GetLastModifiedTime(PRTime *aLastModifiedTime)
{
NS_ASSERTION(mInited, "Not initalised");
+ // Try to read timestamp from extra field
+ PRUint16 blocksize;
+ const PRUint8 *tsField = GetExtraField(ZIP_EXTENDED_TIMESTAMP_FIELD, PR_FALSE, &blocksize);
+ if (tsField && blocksize >= 5) {
+ PRUint32 pos = 4;
+ PRUint8 flags;
+ flags = READ8(tsField, &pos);
+ if (flags & ZIP_EXTENDED_TIMESTAMP_MODTIME) {
+ *aLastModifiedTime = (PRTime)(READ32(tsField, &pos))
+ * PR_USEC_PER_SEC;
+ return NS_OK;
+ }
+ }
+
+ // Use DOS date/time fields
+ // Note that on DST shift we can't handle correctly the hour that is valid
+ // in both DST zones
PRExplodedTime time;
time.tm_usec = 0;
- time.tm_hour = mTime >> 11;
+ time.tm_hour = (mTime >> 11) & 0x1F;
time.tm_min = (mTime >> 5) & 0x3F;
time.tm_sec = (mTime & 0x1F) * 2;
time.tm_year = (mDate >> 9) + 1980;
time.tm_month = ((mDate >> 5) & 0x0F) - 1;
time.tm_mday = mDate & 0x1F;
time.tm_params.tp_gmt_offset = 0;
time.tm_params.tp_dst_offset = 0;
PR_NormalizeTime(&time, PR_GMTParameters);
- time.tm_params = PR_LocalTimeParameters(&time);
+ time.tm_params.tp_gmt_offset = PR_LocalTimeParameters(&time).tp_gmt_offset;
+ PR_NormalizeTime(&time, PR_GMTParameters);
+ time.tm_params.tp_dst_offset = PR_LocalTimeParameters(&time).tp_dst_offset;
*aLastModifiedTime = PR_ImplodeTime(&time);
return NS_OK;
}
/* readonly attribute boolean isSynthetic; */
NS_IMETHODIMP nsZipHeader::GetIsSynthetic(PRBool *aIsSynthetic)
@@ -144,65 +166,95 @@ void nsZipHeader::Init(const nsACString
PRExplodedTime time;
PR_ExplodeTime(aDate, PR_LocalTimeParameters, &time);
mTime = time.tm_sec / 2 + (time.tm_min << 5) + (time.tm_hour << 11);
mDate = time.tm_mday + ((time.tm_month + 1) << 5) +
((time.tm_year - 1980) << 9);
+ // Store modification timestamp as extra field
+ // First fill CDS extra field
+ mFieldLength = 9;
+ mExtraField = new PRUint8[mFieldLength];
+ if (!mExtraField) {
+ mFieldLength = 0;
+ } else {
+ PRUint32 pos = 0;
+ WRITE16(mExtraField.get(), &pos, ZIP_EXTENDED_TIMESTAMP_FIELD);
+ WRITE16(mExtraField.get(), &pos, 5);
+ WRITE8(mExtraField.get(), &pos, ZIP_EXTENDED_TIMESTAMP_MODTIME);
+ WRITE32(mExtraField.get(), &pos, aDate / PR_USEC_PER_SEC);
+
+ // Fill local extra field
+ mLocalExtraField = new PRUint8[mFieldLength];
+ if (mLocalExtraField) {
+ mLocalFieldLength = mFieldLength;
+ memcpy(mLocalExtraField.get(), mExtraField.get(), mLocalFieldLength);
+ }
+ }
+
mEAttr = aAttr;
mOffset = aOffset;
mName = aPath;
mComment = NS_LITERAL_CSTRING("");
// Claim a UTF-8 path in case it needs it.
mFlags |= FLAGS_IS_UTF8;
mInited = PR_TRUE;
}
PRUint32 nsZipHeader::GetFileHeaderLength()
{
- return ZIP_FILE_HEADER_SIZE + mName.Length();
+ return ZIP_FILE_HEADER_SIZE + mName.Length() + mLocalFieldLength;
}
nsresult nsZipHeader::WriteFileHeader(nsIOutputStream *aStream)
{
NS_ASSERTION(mInited, "Not initalised");
- char buf[ZIP_FILE_HEADER_SIZE];
+ PRUint8 buf[ZIP_FILE_HEADER_SIZE];
PRUint32 pos = 0;
WRITE32(buf, &pos, ZIP_FILE_HEADER_SIGNATURE);
WRITE16(buf, &pos, mVersionNeeded);
WRITE16(buf, &pos, mFlags);
WRITE16(buf, &pos, mMethod);
WRITE16(buf, &pos, mTime);
WRITE16(buf, &pos, mDate);
WRITE32(buf, &pos, mCRC);
WRITE32(buf, &pos, mCSize);
WRITE32(buf, &pos, mUSize);
WRITE16(buf, &pos, mName.Length());
- WRITE16(buf, &pos, 0);
+ WRITE16(buf, &pos, mLocalFieldLength);
- nsresult rv = ZW_WriteData(aStream, buf, pos);
+ nsresult rv = ZW_WriteData(aStream, (const char *)buf, pos);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = ZW_WriteData(aStream, mName.get(), mName.Length());
NS_ENSURE_SUCCESS(rv, rv);
- return ZW_WriteData(aStream, mName.get(), mName.Length());
+ if (mLocalFieldLength)
+ {
+ rv = ZW_WriteData(aStream, (const char *)mLocalExtraField.get(), mLocalFieldLength);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
}
PRUint32 nsZipHeader::GetCDSHeaderLength()
{
return ZIP_CDS_HEADER_SIZE + mName.Length() + mComment.Length() +
mFieldLength;
}
nsresult nsZipHeader::WriteCDSHeader(nsIOutputStream *aStream)
{
NS_ASSERTION(mInited, "Not initalised");
- char buf[ZIP_CDS_HEADER_SIZE];
+ PRUint8 buf[ZIP_CDS_HEADER_SIZE];
PRUint32 pos = 0;
WRITE32(buf, &pos, ZIP_CDS_HEADER_SIGNATURE);
WRITE16(buf, &pos, mVersionMade);
WRITE16(buf, &pos, mVersionNeeded);
WRITE16(buf, &pos, mFlags);
WRITE16(buf, &pos, mMethod);
WRITE16(buf, &pos, mTime);
WRITE16(buf, &pos, mDate);
@@ -212,35 +264,35 @@ nsresult nsZipHeader::WriteCDSHeader(nsI
WRITE16(buf, &pos, mName.Length());
WRITE16(buf, &pos, mFieldLength);
WRITE16(buf, &pos, mComment.Length());
WRITE16(buf, &pos, mDisk);
WRITE16(buf, &pos, mIAttr);
WRITE32(buf, &pos, mEAttr);
WRITE32(buf, &pos, mOffset);
- nsresult rv = ZW_WriteData(aStream, buf, pos);
+ nsresult rv = ZW_WriteData(aStream, (const char *)buf, pos);
NS_ENSURE_SUCCESS(rv, rv);
rv = ZW_WriteData(aStream, mName.get(), mName.Length());
NS_ENSURE_SUCCESS(rv, rv);
if (mExtraField) {
- rv = ZW_WriteData(aStream, mExtraField, mFieldLength);
+ rv = ZW_WriteData(aStream, (const char *)mExtraField.get(), mFieldLength);
NS_ENSURE_SUCCESS(rv, rv);
}
return ZW_WriteData(aStream, mComment.get(), mComment.Length());
}
nsresult nsZipHeader::ReadCDSHeader(nsIInputStream *stream)
{
NS_ASSERTION(!mInited, "Already initalised");
- char buf[ZIP_CDS_HEADER_SIZE];
+ PRUint8 buf[ZIP_CDS_HEADER_SIZE];
- nsresult rv = ZW_ReadData(stream, buf, ZIP_CDS_HEADER_SIZE);
+ nsresult rv = ZW_ReadData(stream, (char *)buf, ZIP_CDS_HEADER_SIZE);
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 pos = 0;
PRUint32 signature = READ32(buf, &pos);
if (signature != ZIP_CDS_HEADER_SIGNATURE)
return NS_ERROR_FILE_CORRUPTED;
mVersionMade = READ16(buf, &pos);
@@ -266,27 +318,49 @@ nsresult nsZipHeader::ReadCDSHeader(nsII
rv = ZW_ReadData(stream, field.get(), namelength);
NS_ENSURE_SUCCESS(rv, rv);
mName.Assign(field, namelength);
}
else
mName = NS_LITERAL_CSTRING("");
if (mFieldLength > 0) {
- mExtraField = new char[mFieldLength];
+ mExtraField = new PRUint8[mFieldLength];
NS_ENSURE_TRUE(mExtraField, NS_ERROR_OUT_OF_MEMORY);
- rv = ZW_ReadData(stream, mExtraField.get(), mFieldLength);
+ rv = ZW_ReadData(stream, (char *)mExtraField.get(), mFieldLength);
NS_ENSURE_SUCCESS(rv, rv);
}
if (commentlength > 0) {
nsAutoArrayPtr<char> field(new char[commentlength]);
NS_ENSURE_TRUE(field, NS_ERROR_OUT_OF_MEMORY);
rv = ZW_ReadData(stream, field.get(), commentlength);
NS_ENSURE_SUCCESS(rv, rv);
mComment.Assign(field, commentlength);
}
else
mComment = NS_LITERAL_CSTRING("");
mInited = PR_TRUE;
return NS_OK;
}
+
+const PRUint8 * nsZipHeader::GetExtraField(PRUint16 aTag, PRBool aLocal, PRUint16 *aBlockSize)
+{
+ const PRUint8 *buf = aLocal ? mLocalExtraField : mExtraField;
+ PRUint32 buflen = aLocal ? mLocalFieldLength : mFieldLength;
+ PRUint32 pos = 0;
+ PRUint16 tag, blocksize;
+
+ while (buf && (pos + 4) <= buflen) {
+ tag = READ16(buf, &pos);
+ blocksize = READ16(buf, &pos);
+
+ if (aTag == tag && (pos + blocksize) <= buflen) {
+ *aBlockSize = blocksize;
+ return buf + pos - 4;
+ }
+
+ pos += blocksize;
+ }
+
+ return NULL;
+}
--- a/modules/libjar/zipwriter/src/nsZipHeader.h
+++ b/modules/libjar/zipwriter/src/nsZipHeader.h
@@ -63,55 +63,61 @@ public:
nsZipHeader() :
mCRC(0),
mCSize(0),
mUSize(0),
mEAttr(0),
mOffset(0),
mFieldLength(0),
+ mLocalFieldLength(0),
mVersionMade(0x0300 + 23), // Generated on Unix by v2.3 (matches infozip)
mVersionNeeded(20), // Requires v2.0 to extract
mFlags(0),
mMethod(0),
mTime(0),
mDate(0),
mDisk(0),
mIAttr(0),
mInited(PR_FALSE),
- mExtraField(NULL)
+ mExtraField(NULL),
+ mLocalExtraField(NULL)
{
}
~nsZipHeader()
{
mExtraField = NULL;
+ mLocalExtraField = NULL;
}
PRUint32 mCRC;
PRUint32 mCSize;
PRUint32 mUSize;
PRUint32 mEAttr;
PRUint32 mOffset;
PRUint32 mFieldLength;
+ PRUint32 mLocalFieldLength;
PRUint16 mVersionMade;
PRUint16 mVersionNeeded;
PRUint16 mFlags;
PRUint16 mMethod;
PRUint16 mTime;
PRUint16 mDate;
PRUint16 mDisk;
PRUint16 mIAttr;
PRPackedBool mInited;
nsCString mName;
nsCString mComment;
- nsAutoArrayPtr<char> mExtraField;
+ nsAutoArrayPtr<PRUint8> mExtraField;
+ nsAutoArrayPtr<PRUint8> mLocalExtraField;
void Init(const nsACString & aPath, PRTime aDate, PRUint32 aAttr,
PRUint32 aOffset);
PRUint32 GetFileHeaderLength();
nsresult WriteFileHeader(nsIOutputStream *aStream);
PRUint32 GetCDSHeaderLength();
nsresult WriteCDSHeader(nsIOutputStream *aStream);
nsresult ReadCDSHeader(nsIInputStream *aStream);
+ const PRUint8 * GetExtraField(PRUint16 aTag, PRBool aLocal, PRUint16 *aBlockSize);
};
#endif
--- a/modules/libjar/zipwriter/src/nsZipWriter.cpp
+++ b/modules/libjar/zipwriter/src/nsZipWriter.cpp
@@ -142,60 +142,60 @@ nsresult nsZipWriter::ReadFile(nsIFile *
// If the file is too short, it cannot be a valid archive, thus we fail
// without even attempting to open it
NS_ENSURE_TRUE(size > ZIP_EOCDR_HEADER_SIZE, NS_ERROR_FILE_CORRUPTED);
nsCOMPtr<nsIInputStream> inputStream;
rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), aFile);
NS_ENSURE_SUCCESS(rv, rv);
- char buf[1024];
+ PRUint8 buf[1024];
PRInt64 seek = size - 1024;
PRUint32 length = 1024;
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(inputStream);
while (true) {
if (seek < 0) {
length += (PRInt32)seek;
seek = 0;
}
rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, seek);
if (NS_FAILED(rv)) {
inputStream->Close();
return rv;
}
- rv = ZW_ReadData(inputStream, buf, length);
+ rv = ZW_ReadData(inputStream, (char *)buf, length);
if (NS_FAILED(rv)) {
inputStream->Close();
return rv;
}
/*
* We have to backtrack from the end of the file until we find the
* CDS signature
*/
// We know it's at least this far from the end
for (PRUint32 pos = length - ZIP_EOCDR_HEADER_SIZE;
(PRInt32)pos >= 0; pos--) {
- PRUint32 sig = PEEK32((unsigned char *)buf + pos);
+ PRUint32 sig = PEEK32(buf + pos);
if (sig == ZIP_EOCDR_HEADER_SIGNATURE) {
// Skip down to entry count
pos += 10;
PRUint32 entries = READ16(buf, &pos);
// Skip past CDS size
pos += 4;
mCDSOffset = READ32(buf, &pos);
PRUint32 commentlen = READ16(buf, &pos);
if (commentlen == 0)
mComment.Truncate();
else if (pos + commentlen <= length)
- mComment.Assign(buf + pos, commentlen);
+ mComment.Assign((const char *)buf + pos, commentlen);
else {
if ((seek + pos + commentlen) > size) {
inputStream->Close();
return NS_ERROR_FILE_CORRUPTED;
}
nsAutoArrayPtr<char> field(new char[commentlen]);
NS_ENSURE_TRUE(field, NS_ERROR_OUT_OF_MEMORY);
rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
@@ -677,28 +677,28 @@ NS_IMETHODIMP nsZipWriter::Close()
nsresult rv = mHeaders[i]->WriteCDSHeader(mStream);
if (NS_FAILED(rv)) {
Cleanup();
return rv;
}
size += mHeaders[i]->GetCDSHeaderLength();
}
- char buf[ZIP_EOCDR_HEADER_SIZE];
+ PRUint8 buf[ZIP_EOCDR_HEADER_SIZE];
PRUint32 pos = 0;
WRITE32(buf, &pos, ZIP_EOCDR_HEADER_SIGNATURE);
WRITE16(buf, &pos, 0);
WRITE16(buf, &pos, 0);
WRITE16(buf, &pos, mHeaders.Count());
WRITE16(buf, &pos, mHeaders.Count());
WRITE32(buf, &pos, size);
WRITE32(buf, &pos, mCDSOffset);
WRITE16(buf, &pos, mComment.Length());
- nsresult rv = ZW_WriteData(mStream, buf, pos);
+ nsresult rv = ZW_WriteData(mStream, (const char *)buf, pos);
if (NS_FAILED(rv)) {
Cleanup();
return rv;
}
rv = ZW_WriteData(mStream, mComment.get(), mComment.Length());
if (NS_FAILED(rv)) {
Cleanup();
--- a/modules/libjar/zipwriter/test/unit/head_zipwriter.js
+++ b/modules/libjar/zipwriter/test/unit/head_zipwriter.js
@@ -50,22 +50,20 @@ const PR_TRUNCATE = 0x20
const PR_SYNC = 0x40
const PR_EXCL = 0x80
const ZIP_EOCDR_HEADER_SIZE = 22;
const ZIP_FILE_HEADER_SIZE = 30;
const ZIP_CDS_HEADER_SIZE = 46;
const ZIP_METHOD_STORE = 0
const ZIP_METHOD_DEFLATE = 8
+const ZIP_EXTENDED_TIMESTAMP_SIZE = 9;
const PR_USEC_PER_MSEC = 1000;
-// ZIP times are stored at a 2 second resolution.
-const TIME_RESOLUTION = 2000;
-
const DATA_DIR = "data/";
var ZipWriter = Components.Constructor("@mozilla.org/zipwriter;1",
"nsIZipWriter");
var ZipReader = Components.Constructor("@mozilla.org/libjar/zip-reader;1",
"nsIZipReader", "open");
var dirSvc = Cc["@mozilla.org/file/directory_service;1"]
--- a/modules/libjar/zipwriter/test/unit/test_asyncadd.js
+++ b/modules/libjar/zipwriter/test/unit/test_asyncadd.js
@@ -72,21 +72,18 @@ var observer = {
for (var i = 0; i < TESTS.length; i++) {
do_check_true(zipR.hasEntry(TESTS[i].name));
var source = do_get_file(DATA_DIR + TESTS[i].name);
var entry = zipR.getEntry(TESTS[i].name);
do_check_eq(entry.realSize, TESTS[i].size);
do_check_eq(entry.size, TESTS[i].size);
do_check_eq(entry.CRC32, TESTS[i].crc);
-
- var diff = Math.abs((entry.lastModifiedTime/PR_USEC_PER_MSEC) -
- source.lastModifiedTime);
- if (diff > TIME_RESOLUTION)
- do_throw(diff);
+ do_check_eq(entry.lastModifiedTime / PR_USEC_PER_MSEC,
+ source.lastModifiedTime);
zipR.test(TESTS[i].name);
}
zipR.close();
do_test_finished();
}
};
@@ -95,14 +92,15 @@ function run_test()
{
zipW.open(tmpFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
for (var i = 0; i < TESTS.length; i++) {
var source = do_get_file(DATA_DIR+TESTS[i].name);
zipW.addEntryFile(TESTS[i].name, Ci.nsIZipWriter.COMPRESSION_NONE, source,
true);
size += ZIP_FILE_HEADER_SIZE + ZIP_CDS_HEADER_SIZE +
+ (ZIP_EXTENDED_TIMESTAMP_SIZE * 2) +
(TESTS[i].name.length*2) + TESTS[i].size;
}
do_test_pending();
zipW.processQueue(observer, null);
do_check_true(zipW.inQueue);
}
--- a/modules/libjar/zipwriter/test/unit/test_bug419769_1.js
+++ b/modules/libjar/zipwriter/test/unit/test_bug419769_1.js
@@ -50,19 +50,17 @@ function testpass(source)
var entry = source.getEntry(FILENAME);
do_check_neq(entry, null);
do_check_false(entry.isDirectory);
// Should be stored
do_check_eq(entry.compression, ZIP_METHOD_DEFLATE);
- var diff = Math.abs((entry.lastModifiedTime / PR_USEC_PER_MSEC) - time);
- if (diff > TIME_RESOLUTION)
- do_throw(diff);
+ do_check_eq(entry.lastModifiedTime / PR_USEC_PER_MSEC, time);
// File size should match our data size.
do_check_eq(entry.realSize, DATA.length);
// Check that the CRC is accurate
do_check_eq(entry.CRC32, CRC);
}
--- a/modules/libjar/zipwriter/test/unit/test_bug425768.js
+++ b/modules/libjar/zipwriter/test/unit/test_bug425768.js
@@ -52,11 +52,13 @@ function run_test()
zipW.close();
var zipR = new ZipReader(tmpFile);
do_check_true(zipR.hasEntry(DIRNAME));
zipR.close();
// Adding the directory would have added a fixed amount to the file size.
// Any difference suggests the CDS was written out incorrectly.
- var extra = ZIP_FILE_HEADER_SIZE + ZIP_CDS_HEADER_SIZE + (DIRNAME.length * 2);
+ var extra = ZIP_FILE_HEADER_SIZE + ZIP_CDS_HEADER_SIZE +
+ (DIRNAME.length * 2) + (ZIP_EXTENDED_TIMESTAMP_SIZE * 2);
+
do_check_eq(source.fileSize + extra, tmpFile.fileSize);
}
--- a/modules/libjar/zipwriter/test/unit/test_deflatedata.js
+++ b/modules/libjar/zipwriter/test/unit/test_deflatedata.js
@@ -60,19 +60,17 @@ function run_test()
var entry = zipW.getEntry(FILENAME);
do_check_true(entry != null);
// Check entry seems right.
do_check_eq(entry.compression, ZIP_METHOD_DEFLATE);
do_check_eq(entry.CRC32, CRC);
do_check_eq(entry.realSize, DATA.length);
- var diff = Math.abs((entry.lastModifiedTime/PR_USEC_PER_MSEC) - time);
- if (diff > TIME_RESOLUTION)
- do_throw(diff);
+ do_check_eq(entry.lastModifiedTime / PR_USEC_PER_MSEC, time);
zipW.close();
// Test the stored data with the zipreader
var zipR = new ZipReader(tmpFile);
do_check_true(zipR.hasEntry(FILENAME));
zipR.test(FILENAME);
--- a/modules/libjar/zipwriter/test/unit/test_editexisting.js
+++ b/modules/libjar/zipwriter/test/unit/test_editexisting.js
@@ -37,23 +37,23 @@
*/
// Values taken from using zipinfo to list the test.zip contents
var TESTS = [
{
name: "test.txt",
size: 232,
crc: 0x0373ac26,
- time: new Date(2007, 4, 1, 21, 44, 56)
+ time: Date.UTC(2007, 4, 1, 20, 44, 55)
},
{
name: "test.png",
size: 3402,
crc: 0x504a5c30,
- time: new Date(2007, 4, 1, 21, 49, 40)
+ time: Date.UTC(2007, 4, 1, 20, 49, 39)
}
];
var BADENTRY = "unknown.txt";
function run_test()
{
// Copy our test zip to the tmp dir so we can modify it
var testzip = do_get_file(DATA_DIR + "test.zip");
@@ -65,20 +65,17 @@ function run_test()
for (var i = 0; i < TESTS.length; i++) {
do_check_true(zipW.hasEntry(TESTS[i].name));
var entry = zipW.getEntry(TESTS[i].name);
do_check_true(entry != null);
do_check_eq(entry.realSize, TESTS[i].size);
do_check_eq(entry.CRC32, TESTS[i].crc);
- var diff = Math.abs(TESTS[i].time -
- (entry.lastModifiedTime / PR_USEC_PER_MSEC));
- if (diff > TIME_RESOLUTION)
- do_throw(diff);
+ do_check_eq(entry.lastModifiedTime / PR_USEC_PER_MSEC, TESTS[i].time);
}
try {
zipW.removeEntry(BADENTRY, false);
do_throw("shouldn't be able to remove an entry that doesn't exist");
}
catch (e) {
do_check_eq(e.result, Components.results.NS_ERROR_FILE_NOT_FOUND);
--- a/modules/libjar/zipwriter/test/unit/test_storedata.js
+++ b/modules/libjar/zipwriter/test/unit/test_storedata.js
@@ -51,19 +51,17 @@ function testpass(source)
var entry = source.getEntry(FILENAME);
do_check_neq(entry, null);
do_check_false(entry.isDirectory);
// Should be stored
do_check_eq(entry.compression, ZIP_METHOD_STORE);
- var diff = Math.abs((entry.lastModifiedTime / PR_USEC_PER_MSEC) - time);
- if (diff > TIME_RESOLUTION)
- do_throw(diff);
+ do_check_eq(entry.lastModifiedTime / PR_USEC_PER_MSEC, time);
// File size should match our data size.
do_check_eq(entry.realSize, DATA.length);
// When stored sizes should match.
do_check_eq(entry.size, entry.realSize);
// Check that the CRC is accurate
do_check_eq(entry.CRC32, CRC);
@@ -85,16 +83,17 @@ function run_test()
Ci.nsIZipWriter.COMPRESSION_NONE, stream, false);
// Check that zip state is right at this stage.
testpass(zipW);
zipW.close();
do_check_eq(tmpFile.fileSize,
DATA.length + ZIP_FILE_HEADER_SIZE + ZIP_CDS_HEADER_SIZE +
+ (ZIP_EXTENDED_TIMESTAMP_SIZE * 2) +
(FILENAME.length * 2) + ZIP_EOCDR_HEADER_SIZE);
// Check to see if we get the same results loading afresh.
zipW.open(tmpFile, PR_RDWR);
testpass(zipW);
zipW.close();
// Test the stored data with the zipreader
--- a/modules/libjar/zipwriter/test/unit/test_sync.js
+++ b/modules/libjar/zipwriter/test/unit/test_sync.js
@@ -55,16 +55,17 @@ function run_test()
zipW.open(tmpFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
var size = 0;
for (var i = 0; i < TESTS.length; i++) {
var source = do_get_file(DATA_DIR + TESTS[i].name);
zipW.addEntryFile(TESTS[i].name, Ci.nsIZipWriter.COMPRESSION_NONE, source,
false);
size += ZIP_FILE_HEADER_SIZE + ZIP_CDS_HEADER_SIZE +
+ (ZIP_EXTENDED_TIMESTAMP_SIZE * 2) +
(TESTS[i].name.length*2) + TESTS[i].size;
}
zipW.close();
size += ZIP_EOCDR_HEADER_SIZE;
do_check_eq(size, tmpFile.fileSize);
@@ -74,19 +75,16 @@ function run_test()
for (var i = 0; i < TESTS.length; i++) {
var source = do_get_file(DATA_DIR + TESTS[i].name);
do_check_true(zipR.hasEntry(TESTS[i].name));
var entry = zipR.getEntry(TESTS[i].name);
do_check_eq(entry.realSize, TESTS[i].size);
do_check_eq(entry.size, TESTS[i].size);
do_check_eq(entry.CRC32, TESTS[i].crc);
-
- var diff = Math.abs((entry.lastModifiedTime/PR_USEC_PER_MSEC) -
- source.lastModifiedTime);
- if (diff > TIME_RESOLUTION)
- do_throw(diff);
+ do_check_eq(entry.lastModifiedTime / PR_USEC_PER_MSEC,
+ source.lastModifiedTime);
zipR.test(TESTS[i].name);
}
zipR.close();
}