1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/modules/libjar/zipwriter/src/nsZipHeader.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,395 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. 1.7 + */ 1.8 + 1.9 +#include "StreamFunctions.h" 1.10 +#include "nsZipHeader.h" 1.11 +#include "nsMemory.h" 1.12 +#include "prtime.h" 1.13 + 1.14 +#define ZIP_FILE_HEADER_SIGNATURE 0x04034b50 1.15 +#define ZIP_FILE_HEADER_SIZE 30 1.16 +#define ZIP_CDS_HEADER_SIGNATURE 0x02014b50 1.17 +#define ZIP_CDS_HEADER_SIZE 46 1.18 + 1.19 +#define FLAGS_IS_UTF8 0x800 1.20 + 1.21 +#define ZIP_EXTENDED_TIMESTAMP_FIELD 0x5455 1.22 +#define ZIP_EXTENDED_TIMESTAMP_MODTIME 0x01 1.23 + 1.24 +/** 1.25 + * nsZipHeader represents an entry from a zip file. 1.26 + */ 1.27 +NS_IMPL_ISUPPORTS(nsZipHeader, nsIZipEntry) 1.28 + 1.29 +/* readonly attribute unsigned short compression; */ 1.30 +NS_IMETHODIMP nsZipHeader::GetCompression(uint16_t *aCompression) 1.31 +{ 1.32 + NS_ASSERTION(mInited, "Not initalised"); 1.33 + 1.34 + *aCompression = mMethod; 1.35 + return NS_OK; 1.36 +} 1.37 + 1.38 +/* readonly attribute unsigned long size; */ 1.39 +NS_IMETHODIMP nsZipHeader::GetSize(uint32_t *aSize) 1.40 +{ 1.41 + NS_ASSERTION(mInited, "Not initalised"); 1.42 + 1.43 + *aSize = mCSize; 1.44 + return NS_OK; 1.45 +} 1.46 + 1.47 +/* readonly attribute unsigned long realSize; */ 1.48 +NS_IMETHODIMP nsZipHeader::GetRealSize(uint32_t *aRealSize) 1.49 +{ 1.50 + NS_ASSERTION(mInited, "Not initalised"); 1.51 + 1.52 + *aRealSize = mUSize; 1.53 + return NS_OK; 1.54 +} 1.55 + 1.56 +/* readonly attribute unsigned long CRC32; */ 1.57 +NS_IMETHODIMP nsZipHeader::GetCRC32(uint32_t *aCRC32) 1.58 +{ 1.59 + NS_ASSERTION(mInited, "Not initalised"); 1.60 + 1.61 + *aCRC32 = mCRC; 1.62 + return NS_OK; 1.63 +} 1.64 + 1.65 +/* readonly attribute boolean isDirectory; */ 1.66 +NS_IMETHODIMP nsZipHeader::GetIsDirectory(bool *aIsDirectory) 1.67 +{ 1.68 + NS_ASSERTION(mInited, "Not initalised"); 1.69 + 1.70 + if (mName.Last() == '/') 1.71 + *aIsDirectory = true; 1.72 + else 1.73 + *aIsDirectory = false; 1.74 + return NS_OK; 1.75 +} 1.76 + 1.77 +/* readonly attribute PRTime lastModifiedTime; */ 1.78 +NS_IMETHODIMP nsZipHeader::GetLastModifiedTime(PRTime *aLastModifiedTime) 1.79 +{ 1.80 + NS_ASSERTION(mInited, "Not initalised"); 1.81 + 1.82 + // Try to read timestamp from extra field 1.83 + uint16_t blocksize; 1.84 + const uint8_t *tsField = GetExtraField(ZIP_EXTENDED_TIMESTAMP_FIELD, false, &blocksize); 1.85 + if (tsField && blocksize >= 5) { 1.86 + uint32_t pos = 4; 1.87 + uint8_t flags; 1.88 + flags = READ8(tsField, &pos); 1.89 + if (flags & ZIP_EXTENDED_TIMESTAMP_MODTIME) { 1.90 + *aLastModifiedTime = (PRTime)(READ32(tsField, &pos)) 1.91 + * PR_USEC_PER_SEC; 1.92 + return NS_OK; 1.93 + } 1.94 + } 1.95 + 1.96 + // Use DOS date/time fields 1.97 + // Note that on DST shift we can't handle correctly the hour that is valid 1.98 + // in both DST zones 1.99 + PRExplodedTime time; 1.100 + 1.101 + time.tm_usec = 0; 1.102 + 1.103 + time.tm_hour = (mTime >> 11) & 0x1F; 1.104 + time.tm_min = (mTime >> 5) & 0x3F; 1.105 + time.tm_sec = (mTime & 0x1F) * 2; 1.106 + 1.107 + time.tm_year = (mDate >> 9) + 1980; 1.108 + time.tm_month = ((mDate >> 5) & 0x0F) - 1; 1.109 + time.tm_mday = mDate & 0x1F; 1.110 + 1.111 + time.tm_params.tp_gmt_offset = 0; 1.112 + time.tm_params.tp_dst_offset = 0; 1.113 + 1.114 + PR_NormalizeTime(&time, PR_GMTParameters); 1.115 + time.tm_params.tp_gmt_offset = PR_LocalTimeParameters(&time).tp_gmt_offset; 1.116 + PR_NormalizeTime(&time, PR_GMTParameters); 1.117 + time.tm_params.tp_dst_offset = PR_LocalTimeParameters(&time).tp_dst_offset; 1.118 + 1.119 + *aLastModifiedTime = PR_ImplodeTime(&time); 1.120 + 1.121 + return NS_OK; 1.122 +} 1.123 + 1.124 +/* readonly attribute boolean isSynthetic; */ 1.125 +NS_IMETHODIMP nsZipHeader::GetIsSynthetic(bool *aIsSynthetic) 1.126 +{ 1.127 + NS_ASSERTION(mInited, "Not initalised"); 1.128 + 1.129 + *aIsSynthetic = false; 1.130 + return NS_OK; 1.131 +} 1.132 + 1.133 +/* readonly attribute unsigned long permissions; */ 1.134 +NS_IMETHODIMP nsZipHeader::GetPermissions(uint32_t *aPermissions) 1.135 +{ 1.136 + NS_ASSERTION(mInited, "Not initalised"); 1.137 + 1.138 + // Always give user read access at least, this matches nsIZipReader's behaviour 1.139 + *aPermissions = ((mEAttr >> 16) & 0xfff) | 0x100; 1.140 + return NS_OK; 1.141 +} 1.142 + 1.143 +void nsZipHeader::Init(const nsACString & aPath, PRTime aDate, uint32_t aAttr, 1.144 + uint32_t aOffset) 1.145 +{ 1.146 + NS_ASSERTION(!mInited, "Already initalised"); 1.147 + 1.148 + PRExplodedTime time; 1.149 + PR_ExplodeTime(aDate, PR_LocalTimeParameters, &time); 1.150 + 1.151 + mTime = time.tm_sec / 2 + (time.tm_min << 5) + (time.tm_hour << 11); 1.152 + mDate = time.tm_mday + ((time.tm_month + 1) << 5) + 1.153 + ((time.tm_year - 1980) << 9); 1.154 + 1.155 + // Store modification timestamp as extra field 1.156 + // First fill CDS extra field 1.157 + mFieldLength = 9; 1.158 + mExtraField = new uint8_t[mFieldLength]; 1.159 + if (!mExtraField) { 1.160 + mFieldLength = 0; 1.161 + } else { 1.162 + uint32_t pos = 0; 1.163 + WRITE16(mExtraField.get(), &pos, ZIP_EXTENDED_TIMESTAMP_FIELD); 1.164 + WRITE16(mExtraField.get(), &pos, 5); 1.165 + WRITE8(mExtraField.get(), &pos, ZIP_EXTENDED_TIMESTAMP_MODTIME); 1.166 + WRITE32(mExtraField.get(), &pos, aDate / PR_USEC_PER_SEC); 1.167 + 1.168 + // Fill local extra field 1.169 + mLocalExtraField = new uint8_t[mFieldLength]; 1.170 + if (mLocalExtraField) { 1.171 + mLocalFieldLength = mFieldLength; 1.172 + memcpy(mLocalExtraField.get(), mExtraField.get(), mLocalFieldLength); 1.173 + } 1.174 + } 1.175 + 1.176 + mEAttr = aAttr; 1.177 + mOffset = aOffset; 1.178 + mName = aPath; 1.179 + mComment = NS_LITERAL_CSTRING(""); 1.180 + // Claim a UTF-8 path in case it needs it. 1.181 + mFlags |= FLAGS_IS_UTF8; 1.182 + mInited = true; 1.183 +} 1.184 + 1.185 +uint32_t nsZipHeader::GetFileHeaderLength() 1.186 +{ 1.187 + return ZIP_FILE_HEADER_SIZE + mName.Length() + mLocalFieldLength; 1.188 +} 1.189 + 1.190 +nsresult nsZipHeader::WriteFileHeader(nsIOutputStream *aStream) 1.191 +{ 1.192 + NS_ASSERTION(mInited, "Not initalised"); 1.193 + 1.194 + uint8_t buf[ZIP_FILE_HEADER_SIZE]; 1.195 + uint32_t pos = 0; 1.196 + WRITE32(buf, &pos, ZIP_FILE_HEADER_SIGNATURE); 1.197 + WRITE16(buf, &pos, mVersionNeeded); 1.198 + WRITE16(buf, &pos, mFlags); 1.199 + WRITE16(buf, &pos, mMethod); 1.200 + WRITE16(buf, &pos, mTime); 1.201 + WRITE16(buf, &pos, mDate); 1.202 + WRITE32(buf, &pos, mCRC); 1.203 + WRITE32(buf, &pos, mCSize); 1.204 + WRITE32(buf, &pos, mUSize); 1.205 + WRITE16(buf, &pos, mName.Length()); 1.206 + WRITE16(buf, &pos, mLocalFieldLength); 1.207 + 1.208 + nsresult rv = ZW_WriteData(aStream, (const char *)buf, pos); 1.209 + NS_ENSURE_SUCCESS(rv, rv); 1.210 + 1.211 + rv = ZW_WriteData(aStream, mName.get(), mName.Length()); 1.212 + NS_ENSURE_SUCCESS(rv, rv); 1.213 + 1.214 + if (mLocalFieldLength) 1.215 + { 1.216 + rv = ZW_WriteData(aStream, (const char *)mLocalExtraField.get(), mLocalFieldLength); 1.217 + NS_ENSURE_SUCCESS(rv, rv); 1.218 + } 1.219 + 1.220 + return NS_OK; 1.221 +} 1.222 + 1.223 +uint32_t nsZipHeader::GetCDSHeaderLength() 1.224 +{ 1.225 + return ZIP_CDS_HEADER_SIZE + mName.Length() + mComment.Length() + 1.226 + mFieldLength; 1.227 +} 1.228 + 1.229 +nsresult nsZipHeader::WriteCDSHeader(nsIOutputStream *aStream) 1.230 +{ 1.231 + NS_ASSERTION(mInited, "Not initalised"); 1.232 + 1.233 + uint8_t buf[ZIP_CDS_HEADER_SIZE]; 1.234 + uint32_t pos = 0; 1.235 + WRITE32(buf, &pos, ZIP_CDS_HEADER_SIGNATURE); 1.236 + WRITE16(buf, &pos, mVersionMade); 1.237 + WRITE16(buf, &pos, mVersionNeeded); 1.238 + WRITE16(buf, &pos, mFlags); 1.239 + WRITE16(buf, &pos, mMethod); 1.240 + WRITE16(buf, &pos, mTime); 1.241 + WRITE16(buf, &pos, mDate); 1.242 + WRITE32(buf, &pos, mCRC); 1.243 + WRITE32(buf, &pos, mCSize); 1.244 + WRITE32(buf, &pos, mUSize); 1.245 + WRITE16(buf, &pos, mName.Length()); 1.246 + WRITE16(buf, &pos, mFieldLength); 1.247 + WRITE16(buf, &pos, mComment.Length()); 1.248 + WRITE16(buf, &pos, mDisk); 1.249 + WRITE16(buf, &pos, mIAttr); 1.250 + WRITE32(buf, &pos, mEAttr); 1.251 + WRITE32(buf, &pos, mOffset); 1.252 + 1.253 + nsresult rv = ZW_WriteData(aStream, (const char *)buf, pos); 1.254 + NS_ENSURE_SUCCESS(rv, rv); 1.255 + 1.256 + rv = ZW_WriteData(aStream, mName.get(), mName.Length()); 1.257 + NS_ENSURE_SUCCESS(rv, rv); 1.258 + if (mExtraField) { 1.259 + rv = ZW_WriteData(aStream, (const char *)mExtraField.get(), mFieldLength); 1.260 + NS_ENSURE_SUCCESS(rv, rv); 1.261 + } 1.262 + return ZW_WriteData(aStream, mComment.get(), mComment.Length()); 1.263 +} 1.264 + 1.265 +nsresult nsZipHeader::ReadCDSHeader(nsIInputStream *stream) 1.266 +{ 1.267 + NS_ASSERTION(!mInited, "Already initalised"); 1.268 + 1.269 + uint8_t buf[ZIP_CDS_HEADER_SIZE]; 1.270 + 1.271 + nsresult rv = ZW_ReadData(stream, (char *)buf, ZIP_CDS_HEADER_SIZE); 1.272 + NS_ENSURE_SUCCESS(rv, rv); 1.273 + 1.274 + uint32_t pos = 0; 1.275 + uint32_t signature = READ32(buf, &pos); 1.276 + if (signature != ZIP_CDS_HEADER_SIGNATURE) 1.277 + return NS_ERROR_FILE_CORRUPTED; 1.278 + 1.279 + mVersionMade = READ16(buf, &pos); 1.280 + mVersionNeeded = READ16(buf, &pos); 1.281 + mFlags = READ16(buf, &pos); 1.282 + mMethod = READ16(buf, &pos); 1.283 + mTime = READ16(buf, &pos); 1.284 + mDate = READ16(buf, &pos); 1.285 + mCRC = READ32(buf, &pos); 1.286 + mCSize = READ32(buf, &pos); 1.287 + mUSize = READ32(buf, &pos); 1.288 + uint16_t namelength = READ16(buf, &pos); 1.289 + mFieldLength = READ16(buf, &pos); 1.290 + uint16_t commentlength = READ16(buf, &pos); 1.291 + mDisk = READ16(buf, &pos); 1.292 + mIAttr = READ16(buf, &pos); 1.293 + mEAttr = READ32(buf, &pos); 1.294 + mOffset = READ32(buf, &pos); 1.295 + 1.296 + if (namelength > 0) { 1.297 + nsAutoArrayPtr<char> field(new char[namelength]); 1.298 + NS_ENSURE_TRUE(field, NS_ERROR_OUT_OF_MEMORY); 1.299 + rv = ZW_ReadData(stream, field.get(), namelength); 1.300 + NS_ENSURE_SUCCESS(rv, rv); 1.301 + mName.Assign(field, namelength); 1.302 + } 1.303 + else 1.304 + mName = NS_LITERAL_CSTRING(""); 1.305 + 1.306 + if (mFieldLength > 0) { 1.307 + mExtraField = new uint8_t[mFieldLength]; 1.308 + NS_ENSURE_TRUE(mExtraField, NS_ERROR_OUT_OF_MEMORY); 1.309 + rv = ZW_ReadData(stream, (char *)mExtraField.get(), mFieldLength); 1.310 + NS_ENSURE_SUCCESS(rv, rv); 1.311 + } 1.312 + 1.313 + if (commentlength > 0) { 1.314 + nsAutoArrayPtr<char> field(new char[commentlength]); 1.315 + NS_ENSURE_TRUE(field, NS_ERROR_OUT_OF_MEMORY); 1.316 + rv = ZW_ReadData(stream, field.get(), commentlength); 1.317 + NS_ENSURE_SUCCESS(rv, rv); 1.318 + mComment.Assign(field, commentlength); 1.319 + } 1.320 + else 1.321 + mComment = NS_LITERAL_CSTRING(""); 1.322 + 1.323 + mInited = true; 1.324 + return NS_OK; 1.325 +} 1.326 + 1.327 +const uint8_t * nsZipHeader::GetExtraField(uint16_t aTag, bool aLocal, uint16_t *aBlockSize) 1.328 +{ 1.329 + const uint8_t *buf = aLocal ? mLocalExtraField : mExtraField; 1.330 + uint32_t buflen = aLocal ? mLocalFieldLength : mFieldLength; 1.331 + uint32_t pos = 0; 1.332 + uint16_t tag, blocksize; 1.333 + 1.334 + while (buf && (pos + 4) <= buflen) { 1.335 + tag = READ16(buf, &pos); 1.336 + blocksize = READ16(buf, &pos); 1.337 + 1.338 + if (aTag == tag && (pos + blocksize) <= buflen) { 1.339 + *aBlockSize = blocksize; 1.340 + return buf + pos - 4; 1.341 + } 1.342 + 1.343 + pos += blocksize; 1.344 + } 1.345 + 1.346 + return nullptr; 1.347 +} 1.348 + 1.349 +/* 1.350 + * Pad extra field to align data starting position to specified size. 1.351 + */ 1.352 +nsresult nsZipHeader::PadExtraField(uint32_t aOffset, uint16_t aAlignSize) 1.353 +{ 1.354 + uint32_t pad_size; 1.355 + uint32_t pa_offset; 1.356 + uint32_t pa_end; 1.357 + 1.358 + // Check for range and power of 2. 1.359 + if (aAlignSize < 2 || aAlignSize > 32768 || 1.360 + (aAlignSize & (aAlignSize - 1)) != 0) { 1.361 + return NS_ERROR_INVALID_ARG; 1.362 + } 1.363 + 1.364 + // Point to current starting data position. 1.365 + aOffset += ZIP_FILE_HEADER_SIZE + mName.Length() + mLocalFieldLength; 1.366 + 1.367 + // Calculate aligned offset. 1.368 + pa_offset = aOffset & ~(aAlignSize - 1); 1.369 + pa_end = pa_offset + aAlignSize; 1.370 + pad_size = pa_end - aOffset; 1.371 + if (pad_size == 0) { 1.372 + return NS_OK; 1.373 + } 1.374 + 1.375 + // Leave enough room(at least 4 bytes) for valid values in extra field. 1.376 + while (pad_size < 4) { 1.377 + pad_size += aAlignSize; 1.378 + } 1.379 + // Extra field length is 2 bytes. 1.380 + if (mLocalFieldLength + pad_size > 65535) { 1.381 + return NS_ERROR_FAILURE; 1.382 + } 1.383 + 1.384 + nsAutoArrayPtr<uint8_t> field = mLocalExtraField; 1.385 + uint32_t pos = mLocalFieldLength; 1.386 + 1.387 + mLocalExtraField = new uint8_t[mLocalFieldLength + pad_size]; 1.388 + memcpy(mLocalExtraField.get(), field, mLocalFieldLength); 1.389 + // Use 0xFFFF as tag ID to avoid conflict with other IDs. 1.390 + // For more information, please read "Extensible data fields" section in: 1.391 + // http://www.pkware.com/documents/casestudies/APPNOTE.TXT 1.392 + WRITE16(mLocalExtraField.get(), &pos, 0xFFFF); 1.393 + WRITE16(mLocalExtraField.get(), &pos, pad_size - 4); 1.394 + memset(mLocalExtraField.get() + pos, 0, pad_size - 4); 1.395 + mLocalFieldLength += pad_size; 1.396 + 1.397 + return NS_OK; 1.398 +}