modules/libjar/zipwriter/src/nsZipHeader.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
michael@0 4 */
michael@0 5
michael@0 6 #include "StreamFunctions.h"
michael@0 7 #include "nsZipHeader.h"
michael@0 8 #include "nsMemory.h"
michael@0 9 #include "prtime.h"
michael@0 10
michael@0 11 #define ZIP_FILE_HEADER_SIGNATURE 0x04034b50
michael@0 12 #define ZIP_FILE_HEADER_SIZE 30
michael@0 13 #define ZIP_CDS_HEADER_SIGNATURE 0x02014b50
michael@0 14 #define ZIP_CDS_HEADER_SIZE 46
michael@0 15
michael@0 16 #define FLAGS_IS_UTF8 0x800
michael@0 17
michael@0 18 #define ZIP_EXTENDED_TIMESTAMP_FIELD 0x5455
michael@0 19 #define ZIP_EXTENDED_TIMESTAMP_MODTIME 0x01
michael@0 20
michael@0 21 /**
michael@0 22 * nsZipHeader represents an entry from a zip file.
michael@0 23 */
michael@0 24 NS_IMPL_ISUPPORTS(nsZipHeader, nsIZipEntry)
michael@0 25
michael@0 26 /* readonly attribute unsigned short compression; */
michael@0 27 NS_IMETHODIMP nsZipHeader::GetCompression(uint16_t *aCompression)
michael@0 28 {
michael@0 29 NS_ASSERTION(mInited, "Not initalised");
michael@0 30
michael@0 31 *aCompression = mMethod;
michael@0 32 return NS_OK;
michael@0 33 }
michael@0 34
michael@0 35 /* readonly attribute unsigned long size; */
michael@0 36 NS_IMETHODIMP nsZipHeader::GetSize(uint32_t *aSize)
michael@0 37 {
michael@0 38 NS_ASSERTION(mInited, "Not initalised");
michael@0 39
michael@0 40 *aSize = mCSize;
michael@0 41 return NS_OK;
michael@0 42 }
michael@0 43
michael@0 44 /* readonly attribute unsigned long realSize; */
michael@0 45 NS_IMETHODIMP nsZipHeader::GetRealSize(uint32_t *aRealSize)
michael@0 46 {
michael@0 47 NS_ASSERTION(mInited, "Not initalised");
michael@0 48
michael@0 49 *aRealSize = mUSize;
michael@0 50 return NS_OK;
michael@0 51 }
michael@0 52
michael@0 53 /* readonly attribute unsigned long CRC32; */
michael@0 54 NS_IMETHODIMP nsZipHeader::GetCRC32(uint32_t *aCRC32)
michael@0 55 {
michael@0 56 NS_ASSERTION(mInited, "Not initalised");
michael@0 57
michael@0 58 *aCRC32 = mCRC;
michael@0 59 return NS_OK;
michael@0 60 }
michael@0 61
michael@0 62 /* readonly attribute boolean isDirectory; */
michael@0 63 NS_IMETHODIMP nsZipHeader::GetIsDirectory(bool *aIsDirectory)
michael@0 64 {
michael@0 65 NS_ASSERTION(mInited, "Not initalised");
michael@0 66
michael@0 67 if (mName.Last() == '/')
michael@0 68 *aIsDirectory = true;
michael@0 69 else
michael@0 70 *aIsDirectory = false;
michael@0 71 return NS_OK;
michael@0 72 }
michael@0 73
michael@0 74 /* readonly attribute PRTime lastModifiedTime; */
michael@0 75 NS_IMETHODIMP nsZipHeader::GetLastModifiedTime(PRTime *aLastModifiedTime)
michael@0 76 {
michael@0 77 NS_ASSERTION(mInited, "Not initalised");
michael@0 78
michael@0 79 // Try to read timestamp from extra field
michael@0 80 uint16_t blocksize;
michael@0 81 const uint8_t *tsField = GetExtraField(ZIP_EXTENDED_TIMESTAMP_FIELD, false, &blocksize);
michael@0 82 if (tsField && blocksize >= 5) {
michael@0 83 uint32_t pos = 4;
michael@0 84 uint8_t flags;
michael@0 85 flags = READ8(tsField, &pos);
michael@0 86 if (flags & ZIP_EXTENDED_TIMESTAMP_MODTIME) {
michael@0 87 *aLastModifiedTime = (PRTime)(READ32(tsField, &pos))
michael@0 88 * PR_USEC_PER_SEC;
michael@0 89 return NS_OK;
michael@0 90 }
michael@0 91 }
michael@0 92
michael@0 93 // Use DOS date/time fields
michael@0 94 // Note that on DST shift we can't handle correctly the hour that is valid
michael@0 95 // in both DST zones
michael@0 96 PRExplodedTime time;
michael@0 97
michael@0 98 time.tm_usec = 0;
michael@0 99
michael@0 100 time.tm_hour = (mTime >> 11) & 0x1F;
michael@0 101 time.tm_min = (mTime >> 5) & 0x3F;
michael@0 102 time.tm_sec = (mTime & 0x1F) * 2;
michael@0 103
michael@0 104 time.tm_year = (mDate >> 9) + 1980;
michael@0 105 time.tm_month = ((mDate >> 5) & 0x0F) - 1;
michael@0 106 time.tm_mday = mDate & 0x1F;
michael@0 107
michael@0 108 time.tm_params.tp_gmt_offset = 0;
michael@0 109 time.tm_params.tp_dst_offset = 0;
michael@0 110
michael@0 111 PR_NormalizeTime(&time, PR_GMTParameters);
michael@0 112 time.tm_params.tp_gmt_offset = PR_LocalTimeParameters(&time).tp_gmt_offset;
michael@0 113 PR_NormalizeTime(&time, PR_GMTParameters);
michael@0 114 time.tm_params.tp_dst_offset = PR_LocalTimeParameters(&time).tp_dst_offset;
michael@0 115
michael@0 116 *aLastModifiedTime = PR_ImplodeTime(&time);
michael@0 117
michael@0 118 return NS_OK;
michael@0 119 }
michael@0 120
michael@0 121 /* readonly attribute boolean isSynthetic; */
michael@0 122 NS_IMETHODIMP nsZipHeader::GetIsSynthetic(bool *aIsSynthetic)
michael@0 123 {
michael@0 124 NS_ASSERTION(mInited, "Not initalised");
michael@0 125
michael@0 126 *aIsSynthetic = false;
michael@0 127 return NS_OK;
michael@0 128 }
michael@0 129
michael@0 130 /* readonly attribute unsigned long permissions; */
michael@0 131 NS_IMETHODIMP nsZipHeader::GetPermissions(uint32_t *aPermissions)
michael@0 132 {
michael@0 133 NS_ASSERTION(mInited, "Not initalised");
michael@0 134
michael@0 135 // Always give user read access at least, this matches nsIZipReader's behaviour
michael@0 136 *aPermissions = ((mEAttr >> 16) & 0xfff) | 0x100;
michael@0 137 return NS_OK;
michael@0 138 }
michael@0 139
michael@0 140 void nsZipHeader::Init(const nsACString & aPath, PRTime aDate, uint32_t aAttr,
michael@0 141 uint32_t aOffset)
michael@0 142 {
michael@0 143 NS_ASSERTION(!mInited, "Already initalised");
michael@0 144
michael@0 145 PRExplodedTime time;
michael@0 146 PR_ExplodeTime(aDate, PR_LocalTimeParameters, &time);
michael@0 147
michael@0 148 mTime = time.tm_sec / 2 + (time.tm_min << 5) + (time.tm_hour << 11);
michael@0 149 mDate = time.tm_mday + ((time.tm_month + 1) << 5) +
michael@0 150 ((time.tm_year - 1980) << 9);
michael@0 151
michael@0 152 // Store modification timestamp as extra field
michael@0 153 // First fill CDS extra field
michael@0 154 mFieldLength = 9;
michael@0 155 mExtraField = new uint8_t[mFieldLength];
michael@0 156 if (!mExtraField) {
michael@0 157 mFieldLength = 0;
michael@0 158 } else {
michael@0 159 uint32_t pos = 0;
michael@0 160 WRITE16(mExtraField.get(), &pos, ZIP_EXTENDED_TIMESTAMP_FIELD);
michael@0 161 WRITE16(mExtraField.get(), &pos, 5);
michael@0 162 WRITE8(mExtraField.get(), &pos, ZIP_EXTENDED_TIMESTAMP_MODTIME);
michael@0 163 WRITE32(mExtraField.get(), &pos, aDate / PR_USEC_PER_SEC);
michael@0 164
michael@0 165 // Fill local extra field
michael@0 166 mLocalExtraField = new uint8_t[mFieldLength];
michael@0 167 if (mLocalExtraField) {
michael@0 168 mLocalFieldLength = mFieldLength;
michael@0 169 memcpy(mLocalExtraField.get(), mExtraField.get(), mLocalFieldLength);
michael@0 170 }
michael@0 171 }
michael@0 172
michael@0 173 mEAttr = aAttr;
michael@0 174 mOffset = aOffset;
michael@0 175 mName = aPath;
michael@0 176 mComment = NS_LITERAL_CSTRING("");
michael@0 177 // Claim a UTF-8 path in case it needs it.
michael@0 178 mFlags |= FLAGS_IS_UTF8;
michael@0 179 mInited = true;
michael@0 180 }
michael@0 181
michael@0 182 uint32_t nsZipHeader::GetFileHeaderLength()
michael@0 183 {
michael@0 184 return ZIP_FILE_HEADER_SIZE + mName.Length() + mLocalFieldLength;
michael@0 185 }
michael@0 186
michael@0 187 nsresult nsZipHeader::WriteFileHeader(nsIOutputStream *aStream)
michael@0 188 {
michael@0 189 NS_ASSERTION(mInited, "Not initalised");
michael@0 190
michael@0 191 uint8_t buf[ZIP_FILE_HEADER_SIZE];
michael@0 192 uint32_t pos = 0;
michael@0 193 WRITE32(buf, &pos, ZIP_FILE_HEADER_SIGNATURE);
michael@0 194 WRITE16(buf, &pos, mVersionNeeded);
michael@0 195 WRITE16(buf, &pos, mFlags);
michael@0 196 WRITE16(buf, &pos, mMethod);
michael@0 197 WRITE16(buf, &pos, mTime);
michael@0 198 WRITE16(buf, &pos, mDate);
michael@0 199 WRITE32(buf, &pos, mCRC);
michael@0 200 WRITE32(buf, &pos, mCSize);
michael@0 201 WRITE32(buf, &pos, mUSize);
michael@0 202 WRITE16(buf, &pos, mName.Length());
michael@0 203 WRITE16(buf, &pos, mLocalFieldLength);
michael@0 204
michael@0 205 nsresult rv = ZW_WriteData(aStream, (const char *)buf, pos);
michael@0 206 NS_ENSURE_SUCCESS(rv, rv);
michael@0 207
michael@0 208 rv = ZW_WriteData(aStream, mName.get(), mName.Length());
michael@0 209 NS_ENSURE_SUCCESS(rv, rv);
michael@0 210
michael@0 211 if (mLocalFieldLength)
michael@0 212 {
michael@0 213 rv = ZW_WriteData(aStream, (const char *)mLocalExtraField.get(), mLocalFieldLength);
michael@0 214 NS_ENSURE_SUCCESS(rv, rv);
michael@0 215 }
michael@0 216
michael@0 217 return NS_OK;
michael@0 218 }
michael@0 219
michael@0 220 uint32_t nsZipHeader::GetCDSHeaderLength()
michael@0 221 {
michael@0 222 return ZIP_CDS_HEADER_SIZE + mName.Length() + mComment.Length() +
michael@0 223 mFieldLength;
michael@0 224 }
michael@0 225
michael@0 226 nsresult nsZipHeader::WriteCDSHeader(nsIOutputStream *aStream)
michael@0 227 {
michael@0 228 NS_ASSERTION(mInited, "Not initalised");
michael@0 229
michael@0 230 uint8_t buf[ZIP_CDS_HEADER_SIZE];
michael@0 231 uint32_t pos = 0;
michael@0 232 WRITE32(buf, &pos, ZIP_CDS_HEADER_SIGNATURE);
michael@0 233 WRITE16(buf, &pos, mVersionMade);
michael@0 234 WRITE16(buf, &pos, mVersionNeeded);
michael@0 235 WRITE16(buf, &pos, mFlags);
michael@0 236 WRITE16(buf, &pos, mMethod);
michael@0 237 WRITE16(buf, &pos, mTime);
michael@0 238 WRITE16(buf, &pos, mDate);
michael@0 239 WRITE32(buf, &pos, mCRC);
michael@0 240 WRITE32(buf, &pos, mCSize);
michael@0 241 WRITE32(buf, &pos, mUSize);
michael@0 242 WRITE16(buf, &pos, mName.Length());
michael@0 243 WRITE16(buf, &pos, mFieldLength);
michael@0 244 WRITE16(buf, &pos, mComment.Length());
michael@0 245 WRITE16(buf, &pos, mDisk);
michael@0 246 WRITE16(buf, &pos, mIAttr);
michael@0 247 WRITE32(buf, &pos, mEAttr);
michael@0 248 WRITE32(buf, &pos, mOffset);
michael@0 249
michael@0 250 nsresult rv = ZW_WriteData(aStream, (const char *)buf, pos);
michael@0 251 NS_ENSURE_SUCCESS(rv, rv);
michael@0 252
michael@0 253 rv = ZW_WriteData(aStream, mName.get(), mName.Length());
michael@0 254 NS_ENSURE_SUCCESS(rv, rv);
michael@0 255 if (mExtraField) {
michael@0 256 rv = ZW_WriteData(aStream, (const char *)mExtraField.get(), mFieldLength);
michael@0 257 NS_ENSURE_SUCCESS(rv, rv);
michael@0 258 }
michael@0 259 return ZW_WriteData(aStream, mComment.get(), mComment.Length());
michael@0 260 }
michael@0 261
michael@0 262 nsresult nsZipHeader::ReadCDSHeader(nsIInputStream *stream)
michael@0 263 {
michael@0 264 NS_ASSERTION(!mInited, "Already initalised");
michael@0 265
michael@0 266 uint8_t buf[ZIP_CDS_HEADER_SIZE];
michael@0 267
michael@0 268 nsresult rv = ZW_ReadData(stream, (char *)buf, ZIP_CDS_HEADER_SIZE);
michael@0 269 NS_ENSURE_SUCCESS(rv, rv);
michael@0 270
michael@0 271 uint32_t pos = 0;
michael@0 272 uint32_t signature = READ32(buf, &pos);
michael@0 273 if (signature != ZIP_CDS_HEADER_SIGNATURE)
michael@0 274 return NS_ERROR_FILE_CORRUPTED;
michael@0 275
michael@0 276 mVersionMade = READ16(buf, &pos);
michael@0 277 mVersionNeeded = READ16(buf, &pos);
michael@0 278 mFlags = READ16(buf, &pos);
michael@0 279 mMethod = READ16(buf, &pos);
michael@0 280 mTime = READ16(buf, &pos);
michael@0 281 mDate = READ16(buf, &pos);
michael@0 282 mCRC = READ32(buf, &pos);
michael@0 283 mCSize = READ32(buf, &pos);
michael@0 284 mUSize = READ32(buf, &pos);
michael@0 285 uint16_t namelength = READ16(buf, &pos);
michael@0 286 mFieldLength = READ16(buf, &pos);
michael@0 287 uint16_t commentlength = READ16(buf, &pos);
michael@0 288 mDisk = READ16(buf, &pos);
michael@0 289 mIAttr = READ16(buf, &pos);
michael@0 290 mEAttr = READ32(buf, &pos);
michael@0 291 mOffset = READ32(buf, &pos);
michael@0 292
michael@0 293 if (namelength > 0) {
michael@0 294 nsAutoArrayPtr<char> field(new char[namelength]);
michael@0 295 NS_ENSURE_TRUE(field, NS_ERROR_OUT_OF_MEMORY);
michael@0 296 rv = ZW_ReadData(stream, field.get(), namelength);
michael@0 297 NS_ENSURE_SUCCESS(rv, rv);
michael@0 298 mName.Assign(field, namelength);
michael@0 299 }
michael@0 300 else
michael@0 301 mName = NS_LITERAL_CSTRING("");
michael@0 302
michael@0 303 if (mFieldLength > 0) {
michael@0 304 mExtraField = new uint8_t[mFieldLength];
michael@0 305 NS_ENSURE_TRUE(mExtraField, NS_ERROR_OUT_OF_MEMORY);
michael@0 306 rv = ZW_ReadData(stream, (char *)mExtraField.get(), mFieldLength);
michael@0 307 NS_ENSURE_SUCCESS(rv, rv);
michael@0 308 }
michael@0 309
michael@0 310 if (commentlength > 0) {
michael@0 311 nsAutoArrayPtr<char> field(new char[commentlength]);
michael@0 312 NS_ENSURE_TRUE(field, NS_ERROR_OUT_OF_MEMORY);
michael@0 313 rv = ZW_ReadData(stream, field.get(), commentlength);
michael@0 314 NS_ENSURE_SUCCESS(rv, rv);
michael@0 315 mComment.Assign(field, commentlength);
michael@0 316 }
michael@0 317 else
michael@0 318 mComment = NS_LITERAL_CSTRING("");
michael@0 319
michael@0 320 mInited = true;
michael@0 321 return NS_OK;
michael@0 322 }
michael@0 323
michael@0 324 const uint8_t * nsZipHeader::GetExtraField(uint16_t aTag, bool aLocal, uint16_t *aBlockSize)
michael@0 325 {
michael@0 326 const uint8_t *buf = aLocal ? mLocalExtraField : mExtraField;
michael@0 327 uint32_t buflen = aLocal ? mLocalFieldLength : mFieldLength;
michael@0 328 uint32_t pos = 0;
michael@0 329 uint16_t tag, blocksize;
michael@0 330
michael@0 331 while (buf && (pos + 4) <= buflen) {
michael@0 332 tag = READ16(buf, &pos);
michael@0 333 blocksize = READ16(buf, &pos);
michael@0 334
michael@0 335 if (aTag == tag && (pos + blocksize) <= buflen) {
michael@0 336 *aBlockSize = blocksize;
michael@0 337 return buf + pos - 4;
michael@0 338 }
michael@0 339
michael@0 340 pos += blocksize;
michael@0 341 }
michael@0 342
michael@0 343 return nullptr;
michael@0 344 }
michael@0 345
michael@0 346 /*
michael@0 347 * Pad extra field to align data starting position to specified size.
michael@0 348 */
michael@0 349 nsresult nsZipHeader::PadExtraField(uint32_t aOffset, uint16_t aAlignSize)
michael@0 350 {
michael@0 351 uint32_t pad_size;
michael@0 352 uint32_t pa_offset;
michael@0 353 uint32_t pa_end;
michael@0 354
michael@0 355 // Check for range and power of 2.
michael@0 356 if (aAlignSize < 2 || aAlignSize > 32768 ||
michael@0 357 (aAlignSize & (aAlignSize - 1)) != 0) {
michael@0 358 return NS_ERROR_INVALID_ARG;
michael@0 359 }
michael@0 360
michael@0 361 // Point to current starting data position.
michael@0 362 aOffset += ZIP_FILE_HEADER_SIZE + mName.Length() + mLocalFieldLength;
michael@0 363
michael@0 364 // Calculate aligned offset.
michael@0 365 pa_offset = aOffset & ~(aAlignSize - 1);
michael@0 366 pa_end = pa_offset + aAlignSize;
michael@0 367 pad_size = pa_end - aOffset;
michael@0 368 if (pad_size == 0) {
michael@0 369 return NS_OK;
michael@0 370 }
michael@0 371
michael@0 372 // Leave enough room(at least 4 bytes) for valid values in extra field.
michael@0 373 while (pad_size < 4) {
michael@0 374 pad_size += aAlignSize;
michael@0 375 }
michael@0 376 // Extra field length is 2 bytes.
michael@0 377 if (mLocalFieldLength + pad_size > 65535) {
michael@0 378 return NS_ERROR_FAILURE;
michael@0 379 }
michael@0 380
michael@0 381 nsAutoArrayPtr<uint8_t> field = mLocalExtraField;
michael@0 382 uint32_t pos = mLocalFieldLength;
michael@0 383
michael@0 384 mLocalExtraField = new uint8_t[mLocalFieldLength + pad_size];
michael@0 385 memcpy(mLocalExtraField.get(), field, mLocalFieldLength);
michael@0 386 // Use 0xFFFF as tag ID to avoid conflict with other IDs.
michael@0 387 // For more information, please read "Extensible data fields" section in:
michael@0 388 // http://www.pkware.com/documents/casestudies/APPNOTE.TXT
michael@0 389 WRITE16(mLocalExtraField.get(), &pos, 0xFFFF);
michael@0 390 WRITE16(mLocalExtraField.get(), &pos, pad_size - 4);
michael@0 391 memset(mLocalExtraField.get() + pos, 0, pad_size - 4);
michael@0 392 mLocalFieldLength += pad_size;
michael@0 393
michael@0 394 return NS_OK;
michael@0 395 }

mercurial