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