Bug 1909986 - For sidebar revamp, only show chatbot entrypoints (context menu, shortc...
[gecko.git] / modules / libjar / zipwriter / nsZipHeader.cpp
blob7506670f1a4333bee5f10855472c59aa2f735d02
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 */
6 #include "StreamFunctions.h"
7 #include "nsZipHeader.h"
8 #include "prtime.h"
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
15 #define FLAGS_IS_UTF8 0x800
17 #define ZIP_EXTENDED_TIMESTAMP_FIELD 0x5455
18 #define ZIP_EXTENDED_TIMESTAMP_MODTIME 0x01
20 using namespace mozilla;
22 /**
23 * nsZipHeader represents an entry from a zip file.
25 NS_IMPL_ISUPPORTS(nsZipHeader, nsIZipEntry)
27 NS_IMETHODIMP nsZipHeader::GetCompression(uint16_t* aCompression) {
28 NS_ASSERTION(mInited, "Not initalised");
30 *aCompression = mMethod;
31 return NS_OK;
34 NS_IMETHODIMP nsZipHeader::GetSize(uint32_t* aSize) {
35 NS_ASSERTION(mInited, "Not initalised");
37 *aSize = mCSize;
38 return NS_OK;
41 NS_IMETHODIMP nsZipHeader::GetRealSize(uint32_t* aRealSize) {
42 NS_ASSERTION(mInited, "Not initalised");
44 *aRealSize = mUSize;
45 return NS_OK;
48 NS_IMETHODIMP nsZipHeader::GetCRC32(uint32_t* aCRC32) {
49 NS_ASSERTION(mInited, "Not initalised");
51 *aCRC32 = mCRC;
52 return NS_OK;
55 NS_IMETHODIMP nsZipHeader::GetIsDirectory(bool* aIsDirectory) {
56 NS_ASSERTION(mInited, "Not initalised");
58 if (mName.Last() == '/')
59 *aIsDirectory = true;
60 else
61 *aIsDirectory = false;
62 return NS_OK;
65 NS_IMETHODIMP nsZipHeader::GetLastModifiedTime(PRTime* aLastModifiedTime) {
66 NS_ASSERTION(mInited, "Not initalised");
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;
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;
87 time.tm_usec = 0;
89 time.tm_hour = (mTime >> 11) & 0x1F;
90 time.tm_min = (mTime >> 5) & 0x3F;
91 time.tm_sec = (mTime & 0x1F) * 2;
93 time.tm_year = (mDate >> 9) + 1980;
94 time.tm_month = ((mDate >> 5) & 0x0F) - 1;
95 time.tm_mday = mDate & 0x1F;
97 time.tm_params.tp_gmt_offset = 0;
98 time.tm_params.tp_dst_offset = 0;
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;
105 *aLastModifiedTime = PR_ImplodeTime(&time);
107 return NS_OK;
110 NS_IMETHODIMP nsZipHeader::GetIsSynthetic(bool* aIsSynthetic) {
111 NS_ASSERTION(mInited, "Not initalised");
113 *aIsSynthetic = false;
114 return NS_OK;
117 NS_IMETHODIMP nsZipHeader::GetPermissions(uint32_t* aPermissions) {
118 NS_ASSERTION(mInited, "Not initalised");
120 // Always give user read access at least, this matches nsIZipReader's
121 // behaviour
122 *aPermissions = ((mEAttr >> 16) & 0xfff) | 0x100;
123 return NS_OK;
126 nsresult nsZipHeader::Init(const nsACString& aPath, PRTime aDate,
127 uint32_t aAttr, uint32_t aOffset) {
128 NS_ASSERTION(!mInited, "Already initalised");
130 PRExplodedTime time;
131 PR_ExplodeTime(aDate, PR_LocalTimeParameters, &time);
132 if (time.tm_year < 1980) {
133 return NS_ERROR_INVALID_ARG;
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);
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);
153 // Fill local extra field
154 mLocalExtraField = MakeUnique<uint8_t[]>(mFieldLength);
155 if (mLocalExtraField) {
156 mLocalFieldLength = mFieldLength;
157 memcpy(mLocalExtraField.get(), mExtraField.get(), mLocalFieldLength);
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;
169 return NS_OK;
172 uint32_t nsZipHeader::GetFileHeaderLength() {
173 return ZIP_FILE_HEADER_SIZE + mName.Length() + mLocalFieldLength;
176 nsresult nsZipHeader::WriteFileHeader(nsIOutputStream* aStream) {
177 NS_ASSERTION(mInited, "Not initalised");
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);
193 nsresult rv = ZW_WriteData(aStream, (const char*)buf, pos);
194 NS_ENSURE_SUCCESS(rv, rv);
196 rv = ZW_WriteData(aStream, mName.get(), mName.Length());
197 NS_ENSURE_SUCCESS(rv, rv);
199 if (mLocalFieldLength) {
200 rv = ZW_WriteData(aStream, (const char*)mLocalExtraField.get(),
201 mLocalFieldLength);
202 NS_ENSURE_SUCCESS(rv, rv);
205 return NS_OK;
208 uint32_t nsZipHeader::GetCDSHeaderLength() {
209 return ZIP_CDS_HEADER_SIZE + mName.Length() + mComment.Length() +
210 mFieldLength;
213 nsresult nsZipHeader::WriteCDSHeader(nsIOutputStream* aStream) {
214 NS_ASSERTION(mInited, "Not initalised");
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);
236 nsresult rv = ZW_WriteData(aStream, (const char*)buf, pos);
237 NS_ENSURE_SUCCESS(rv, rv);
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);
245 return ZW_WriteData(aStream, mComment.get(), mComment.Length());
248 nsresult nsZipHeader::ReadCDSHeader(nsIInputStream* stream) {
249 NS_ASSERTION(!mInited, "Already initalised");
251 uint8_t buf[ZIP_CDS_HEADER_SIZE];
253 nsresult rv = ZW_ReadData(stream, (char*)buf, ZIP_CDS_HEADER_SIZE);
254 NS_ENSURE_SUCCESS(rv, rv);
256 uint32_t pos = 0;
257 uint32_t signature = READ32(buf, &pos);
258 if (signature != ZIP_CDS_HEADER_SIGNATURE) return NS_ERROR_FILE_CORRUPTED;
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);
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();
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);
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();
302 mInited = true;
303 return NS_OK;
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;
313 while (buf && (pos + 4) <= buflen) {
314 tag = READ16(buf, &pos);
315 blocksize = READ16(buf, &pos);
317 if (aTag == tag && (pos + blocksize) <= buflen) {
318 *aBlockSize = blocksize;
319 return buf + pos - 4;
322 pos += blocksize;
325 return nullptr;
329 * Pad extra field to align data starting position to specified size.
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;
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;
342 // Point to current starting data position.
343 aOffset += ZIP_FILE_HEADER_SIZE + mName.Length() + mLocalFieldLength;
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;
353 // Leave enough room(at least 4 bytes) for valid values in extra field.
354 while (pad_size < 4) {
355 pad_size += aAlignSize;
357 // Extra field length is 2 bytes.
358 if (mLocalFieldLength + pad_size > 65535) {
359 return NS_ERROR_FAILURE;
362 UniquePtr<uint8_t[]> field = std::move(mLocalExtraField);
363 uint32_t pos = mLocalFieldLength;
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;
375 return NS_OK;