@zip.js/zip.js 2.8.25 → 2.8.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/deno.json +1 -1
- package/dist/zip-core.js +1050 -569
- package/dist/zip-core.min.js +1 -1
- package/dist/zip-fs-core.js +1146 -617
- package/dist/zip-fs-core.min.js +1 -1
- package/dist/zip-fs-native.js +1207 -627
- package/dist/zip-fs-native.min.js +1 -1
- package/dist/zip-fs.js +1279 -681
- package/dist/zip-fs.min.js +1 -1
- package/dist/zip-legacy.js +1051 -570
- package/dist/zip-legacy.min.js +1 -1
- package/dist/zip-module.wasm +0 -0
- package/dist/zip-native.js +1053 -572
- package/dist/zip-native.min.js +1 -1
- package/dist/zip-web-worker-native.js +1 -1
- package/dist/zip-web-worker.js +1 -1
- package/dist/zip.js +1125 -626
- package/dist/zip.min.js +1 -1
- package/eslint.config.mjs +6 -1
- package/index-native.cjs +1207 -627
- package/index-native.min.js +1 -1
- package/index.cjs +1279 -681
- package/index.d.ts +2451 -2355
- package/index.min.js +1 -1
- package/lib/core/codec-pool.js +39 -1
- package/lib/core/codec-worker.js +120 -34
- package/lib/core/configuration.js +3 -0
- package/lib/core/constants.js +6 -0
- package/lib/core/io.js +55 -25
- package/lib/core/options.js +2 -0
- package/lib/core/streams/aes-crypto-stream.js +12 -12
- package/lib/core/streams/codec-stream.js +11 -5
- package/lib/core/streams/codecs/sjcl.js +1 -33
- package/lib/core/streams/common-crypto.js +4 -4
- package/lib/core/streams/zip-crypto-stream.js +25 -15
- package/lib/core/streams/zip-entry-stream.js +35 -1
- package/lib/core/streams/zlib-js/zlib-streams.min.js +1 -1
- package/lib/core/streams/zlib-wasm/zlib-streams.js +73 -55
- package/lib/core/streams/zlib-wasm/zlib-streams.wasm +0 -0
- package/lib/core/util/decode-cp437.js +1 -1
- package/lib/core/util/decode-text.js +2 -1
- package/lib/core/web-worker-base.js +12 -4
- package/lib/core/web-worker-inline-native.js +1 -1
- package/lib/core/web-worker-inline-wasm.js +1 -1
- package/lib/core/zip-fs.js +154 -55
- package/lib/core/zip-reader.js +185 -67
- package/lib/core/zip-writer.js +613 -376
- package/lib/core/zlib-streams-inline.js +1 -1
- package/lib/zip-core-reader.js +3 -1
- package/lib/zip-core-writer.js +1 -0
- package/package.json +20 -6
package/dist/zip-fs-core.js
CHANGED
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
const END_OF_CENTRAL_DIR_SIGNATURE = 0x06054b50;
|
|
50
50
|
const ZIP64_END_OF_CENTRAL_DIR_SIGNATURE = 0x06064b50;
|
|
51
51
|
const ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIGNATURE = 0x07064b50;
|
|
52
|
+
const CENTRAL_FILE_HEADER_LENGTH = 46;
|
|
52
53
|
const END_OF_CENTRAL_DIR_LENGTH = 22;
|
|
53
54
|
const ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH = 20;
|
|
54
55
|
const ZIP64_END_OF_CENTRAL_DIR_LENGTH = 56;
|
|
@@ -96,9 +97,11 @@
|
|
|
96
97
|
const DIRECTORY_SIGNATURE = "/";
|
|
97
98
|
|
|
98
99
|
const HEADER_SIZE = 30;
|
|
100
|
+
const HEADER_OFFSET_VERSION = 0;
|
|
99
101
|
const HEADER_OFFSET_SIGNATURE = 10;
|
|
100
102
|
const HEADER_OFFSET_COMPRESSED_SIZE = 14;
|
|
101
103
|
const HEADER_OFFSET_UNCOMPRESSED_SIZE = 18;
|
|
104
|
+
const LOCAL_HEADER_COMMON_OFFSET = 4;
|
|
102
105
|
|
|
103
106
|
const MAX_DATE = new Date(2107, 11, 31);
|
|
104
107
|
const MIN_DATE = new Date(1980, 0, 1);
|
|
@@ -153,6 +156,7 @@
|
|
|
153
156
|
chunkSize: 64 * 1024,
|
|
154
157
|
maxWorkers,
|
|
155
158
|
terminateWorkerTimeout: 5000,
|
|
159
|
+
workerStarvationTimeout: 5000,
|
|
156
160
|
useWebWorkers: true,
|
|
157
161
|
useCompressionStream: true,
|
|
158
162
|
CompressionStream: typeof CompressionStream != UNDEFINED_TYPE && CompressionStream,
|
|
@@ -205,6 +209,7 @@
|
|
|
205
209
|
|
|
206
210
|
const CONTENT_TYPE_TEXT_PLAIN = "text/plain";
|
|
207
211
|
const HTTP_HEADER_CONTENT_LENGTH = "Content-Length";
|
|
212
|
+
const HTTP_HEADER_CONTENT_ENCODING = "Content-Encoding";
|
|
208
213
|
const HTTP_HEADER_CONTENT_RANGE = "Content-Range";
|
|
209
214
|
const HTTP_HEADER_ACCEPT_RANGES = "Accept-Ranges";
|
|
210
215
|
const HTTP_HEADER_RANGE = "Range";
|
|
@@ -212,7 +217,7 @@
|
|
|
212
217
|
const HTTP_METHOD_HEAD = "HEAD";
|
|
213
218
|
const HTTP_METHOD_GET = "GET";
|
|
214
219
|
const HTTP_RANGE_UNIT = "bytes";
|
|
215
|
-
const DEFAULT_CHUNK_SIZE = 64 * 1024;
|
|
220
|
+
const DEFAULT_CHUNK_SIZE$1 = 64 * 1024;
|
|
216
221
|
const DEFAULT_BUFFER_SIZE = 256 * 1024;
|
|
217
222
|
|
|
218
223
|
const PROPERTY_NAME_WRITABLE = "writable";
|
|
@@ -232,7 +237,7 @@
|
|
|
232
237
|
|
|
233
238
|
get readable() {
|
|
234
239
|
const reader = this;
|
|
235
|
-
const { chunkSize = DEFAULT_CHUNK_SIZE } = reader;
|
|
240
|
+
const { chunkSize = DEFAULT_CHUNK_SIZE$1 } = reader;
|
|
236
241
|
const readable = new ReadableStream({
|
|
237
242
|
start() {
|
|
238
243
|
this.chunkOffset = 0;
|
|
@@ -329,7 +334,7 @@
|
|
|
329
334
|
|
|
330
335
|
writeUint8Array(array) {
|
|
331
336
|
const writer = this;
|
|
332
|
-
let indexArray
|
|
337
|
+
let indexArray;
|
|
333
338
|
let dataString = writer.pending;
|
|
334
339
|
const delta = writer.pending.length;
|
|
335
340
|
writer.pending = "";
|
|
@@ -339,12 +344,10 @@
|
|
|
339
344
|
for (; indexArray < array.length; indexArray++) {
|
|
340
345
|
writer.pending += String.fromCharCode(array[indexArray]);
|
|
341
346
|
}
|
|
342
|
-
if (dataString.length) {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
writer.pending += dataString;
|
|
347
|
-
}
|
|
347
|
+
if (dataString.length > 2) {
|
|
348
|
+
writer.data += btoa(dataString);
|
|
349
|
+
} else {
|
|
350
|
+
writer.pending = dataString + writer.pending;
|
|
348
351
|
}
|
|
349
352
|
}
|
|
350
353
|
|
|
@@ -391,6 +394,9 @@
|
|
|
391
394
|
}
|
|
392
395
|
});
|
|
393
396
|
writer.blob = new Response(transformStream.readable, { headers }).blob();
|
|
397
|
+
// mark early rejections (e.g. aborted pipe) as handled; awaiting getData() still
|
|
398
|
+
// surfaces them
|
|
399
|
+
writer.blob.catch(() => { });
|
|
394
400
|
}
|
|
395
401
|
|
|
396
402
|
getData() {
|
|
@@ -475,7 +481,8 @@
|
|
|
475
481
|
preventHeadRequest,
|
|
476
482
|
useRangeHeader,
|
|
477
483
|
forceRangeRequests,
|
|
478
|
-
combineSizeEocd
|
|
484
|
+
combineSizeEocd,
|
|
485
|
+
fetch
|
|
479
486
|
} = options;
|
|
480
487
|
options = Object.assign({}, options);
|
|
481
488
|
delete options.preventHeadRequest;
|
|
@@ -483,13 +490,15 @@
|
|
|
483
490
|
delete options.forceRangeRequests;
|
|
484
491
|
delete options.combineSizeEocd;
|
|
485
492
|
delete options.useXHR;
|
|
493
|
+
delete options.fetch;
|
|
486
494
|
Object.assign(httpReader, {
|
|
487
495
|
url,
|
|
488
496
|
options,
|
|
489
497
|
preventHeadRequest,
|
|
490
498
|
useRangeHeader,
|
|
491
499
|
forceRangeRequests,
|
|
492
|
-
combineSizeEocd
|
|
500
|
+
combineSizeEocd,
|
|
501
|
+
fetch
|
|
493
502
|
});
|
|
494
503
|
}
|
|
495
504
|
|
|
@@ -508,7 +517,10 @@
|
|
|
508
517
|
throw new Error(ERR_HTTP_RANGE);
|
|
509
518
|
} else {
|
|
510
519
|
if (combineSizeEocd) {
|
|
511
|
-
|
|
520
|
+
const eocdCache = new Uint8Array(await response.arrayBuffer());
|
|
521
|
+
if (response.status == 206 && eocdCache.length == END_OF_CENTRAL_DIR_LENGTH) {
|
|
522
|
+
httpReader.eocdCache = eocdCache;
|
|
523
|
+
}
|
|
512
524
|
}
|
|
513
525
|
let contentSize;
|
|
514
526
|
const contentRangeHeader = response.headers.get(HTTP_HEADER_CONTENT_RANGE);
|
|
@@ -554,7 +566,18 @@
|
|
|
554
566
|
if (response.status != 206) {
|
|
555
567
|
throw new Error(ERR_HTTP_RANGE);
|
|
556
568
|
}
|
|
557
|
-
|
|
569
|
+
const contentRangeHeader = response.headers.get(HTTP_HEADER_CONTENT_RANGE);
|
|
570
|
+
if (contentRangeHeader) {
|
|
571
|
+
const rangeStart = Number(contentRangeHeader.trim().split(/[\s-]+/)[1]);
|
|
572
|
+
if (!Number.isNaN(rangeStart) && rangeStart != index) {
|
|
573
|
+
throw new Error(ERR_HTTP_RANGE);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
const data = new Uint8Array(await response.arrayBuffer());
|
|
577
|
+
if (data.length != length) {
|
|
578
|
+
throw new Error(ERR_HTTP_RANGE);
|
|
579
|
+
}
|
|
580
|
+
return data;
|
|
558
581
|
}
|
|
559
582
|
} else {
|
|
560
583
|
const { data } = httpReader;
|
|
@@ -591,9 +614,9 @@
|
|
|
591
614
|
async function getRequestData(httpReader, sendRequest) {
|
|
592
615
|
const response = await sendRequest(HTTP_METHOD_GET, httpReader, getHeaders(httpReader));
|
|
593
616
|
httpReader.data = new Uint8Array(await response.arrayBuffer());
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
617
|
+
// the downloaded content is authoritative, e.g. the Content-Length response header
|
|
618
|
+
// of the HEAD request can be the encoded size when the content is compressed
|
|
619
|
+
httpReader.size = httpReader.data.length;
|
|
597
620
|
}
|
|
598
621
|
|
|
599
622
|
async function getContentLength(httpReader, sendRequest, getRequestData) {
|
|
@@ -602,7 +625,7 @@
|
|
|
602
625
|
} else {
|
|
603
626
|
const response = await sendRequest(HTTP_METHOD_HEAD, httpReader, getHeaders(httpReader));
|
|
604
627
|
const contentLength = response.headers.get(HTTP_HEADER_CONTENT_LENGTH);
|
|
605
|
-
if (contentLength) {
|
|
628
|
+
if (contentLength && !response.headers.get(HTTP_HEADER_CONTENT_ENCODING)) {
|
|
606
629
|
httpReader.size = Number(contentLength);
|
|
607
630
|
} else {
|
|
608
631
|
await getRequestData(httpReader, httpReader.options);
|
|
@@ -610,8 +633,8 @@
|
|
|
610
633
|
}
|
|
611
634
|
}
|
|
612
635
|
|
|
613
|
-
async function sendFetchRequest(method, { options, url }, headers) {
|
|
614
|
-
const response = await
|
|
636
|
+
async function sendFetchRequest(method, { fetch: fetchFunction = fetch, options, url }, headers) {
|
|
637
|
+
const response = await fetchFunction(url, Object.assign({}, options, { method, headers }));
|
|
615
638
|
if (response.status < 400) {
|
|
616
639
|
return response;
|
|
617
640
|
} else {
|
|
@@ -657,7 +680,8 @@
|
|
|
657
680
|
super();
|
|
658
681
|
Object.assign(this, {
|
|
659
682
|
url,
|
|
660
|
-
|
|
683
|
+
// an explicit fetch implementation takes precedence over useXHR
|
|
684
|
+
reader: options.useXHR && !options.fetch ? new XHRReader(url, options) : new FetchReader(url, options)
|
|
661
685
|
});
|
|
662
686
|
}
|
|
663
687
|
|
|
@@ -749,7 +773,7 @@
|
|
|
749
773
|
reader.lastDiskNumber = 0;
|
|
750
774
|
reader.lastDiskOffset = 0;
|
|
751
775
|
await Promise.all(readers.map(async (diskReader, indexDiskReader) => {
|
|
752
|
-
await diskReader
|
|
776
|
+
await initStream(diskReader);
|
|
753
777
|
if (indexDiskReader != readers.length - 1) {
|
|
754
778
|
reader.lastDiskOffset += diskReader.size;
|
|
755
779
|
}
|
|
@@ -833,14 +857,26 @@
|
|
|
833
857
|
writer.diskOffset += diskSourceWriter.size;
|
|
834
858
|
writer.diskNumber++;
|
|
835
859
|
diskWriter = null;
|
|
836
|
-
|
|
860
|
+
// the next disk is not opened yet but its space is available; leaving a
|
|
861
|
+
// stale value here would force a new disk for every subsequent write
|
|
862
|
+
writer.availableSize = writer.maxSize;
|
|
863
|
+
if (chunk.length > availableSize) {
|
|
864
|
+
await this.write(chunk.subarray(availableSize));
|
|
865
|
+
}
|
|
837
866
|
} else {
|
|
838
867
|
await writeChunk(chunk);
|
|
839
868
|
}
|
|
840
869
|
},
|
|
841
870
|
async close() {
|
|
842
|
-
|
|
843
|
-
|
|
871
|
+
if (diskWriter) {
|
|
872
|
+
await diskWriter.ready;
|
|
873
|
+
await closeDisk();
|
|
874
|
+
}
|
|
875
|
+
},
|
|
876
|
+
async abort(reason) {
|
|
877
|
+
if (diskWriter) {
|
|
878
|
+
await diskWriter.abort(reason);
|
|
879
|
+
}
|
|
844
880
|
}
|
|
845
881
|
});
|
|
846
882
|
Object.defineProperty(writer, PROPERTY_NAME_WRITABLE, {
|
|
@@ -855,7 +891,6 @@
|
|
|
855
891
|
await diskWriter.ready;
|
|
856
892
|
await diskWriter.write(chunk);
|
|
857
893
|
diskSourceWriter.size += chunkLength;
|
|
858
|
-
writer.size += chunkLength;
|
|
859
894
|
writer.availableSize -= chunkLength;
|
|
860
895
|
}
|
|
861
896
|
}
|
|
@@ -1672,37 +1707,6 @@
|
|
|
1672
1707
|
}
|
|
1673
1708
|
};
|
|
1674
1709
|
|
|
1675
|
-
/**
|
|
1676
|
-
* Random values
|
|
1677
|
-
* @namespace
|
|
1678
|
-
*/
|
|
1679
|
-
const random = {
|
|
1680
|
-
/**
|
|
1681
|
-
* Generate random words with pure js, cryptographically not as strong & safe as native implementation.
|
|
1682
|
-
* @param {TypedArray} typedArray The array to fill.
|
|
1683
|
-
* @return {TypedArray} The random values.
|
|
1684
|
-
*/
|
|
1685
|
-
getRandomValues(typedArray) {
|
|
1686
|
-
const words = new Uint32Array(typedArray.buffer);
|
|
1687
|
-
const r = (m_w) => {
|
|
1688
|
-
let m_z = 0x3ade68b1;
|
|
1689
|
-
const mask = 0xffffffff;
|
|
1690
|
-
return function () {
|
|
1691
|
-
m_z = (0x9069 * (m_z & 0xFFFF) + (m_z >> 0x10)) & mask;
|
|
1692
|
-
m_w = (0x4650 * (m_w & 0xFFFF) + (m_w >> 0x10)) & mask;
|
|
1693
|
-
const result = ((((m_z << 0x10) + m_w) & mask) / 0x100000000) + .5;
|
|
1694
|
-
return result * (Math.random() > .5 ? 1 : -1);
|
|
1695
|
-
};
|
|
1696
|
-
};
|
|
1697
|
-
for (let i = 0, rcache; i < typedArray.length; i += 4) {
|
|
1698
|
-
const _r = r((rcache || Math.random()) * 0x100000000);
|
|
1699
|
-
rcache = _r() * 0x3ade67b7;
|
|
1700
|
-
words[i / 4] = (_r() * 0x100000000) | 0;
|
|
1701
|
-
}
|
|
1702
|
-
return typedArray;
|
|
1703
|
-
}
|
|
1704
|
-
};
|
|
1705
|
-
|
|
1706
1710
|
/** @fileOverview CTR mode implementation.
|
|
1707
1711
|
*
|
|
1708
1712
|
* Special thanks to Roy Nicholson for pointing out a bug in our
|
|
@@ -1933,12 +1937,15 @@
|
|
|
1933
1937
|
const ERR_INVALID_PASSWORD = "Invalid password";
|
|
1934
1938
|
const ERR_INVALID_SIGNATURE = "Invalid signature";
|
|
1935
1939
|
const ERR_ABORT_CHECK_PASSWORD = "zipjs-abort-check-password";
|
|
1940
|
+
const ERR_UNSUPPORTED_CRYPTO_API = "Crypto API not supported";
|
|
1936
1941
|
|
|
1937
1942
|
function getRandomValues(array) {
|
|
1938
1943
|
if (GET_RANDOM_VALUES_SUPPORTED) {
|
|
1939
1944
|
return crypto.getRandomValues(array);
|
|
1940
1945
|
} else {
|
|
1941
|
-
|
|
1946
|
+
// all supported runtimes expose crypto.getRandomValues; failing hard beats
|
|
1947
|
+
// silently generating predictable encryption salts
|
|
1948
|
+
throw new Error(ERR_UNSUPPORTED_CRYPTO_API);
|
|
1942
1949
|
}
|
|
1943
1950
|
}
|
|
1944
1951
|
|
|
@@ -1997,13 +2004,12 @@
|
|
|
1997
2004
|
|
|
1998
2005
|
class AESDecryptionStream extends TransformStream {
|
|
1999
2006
|
|
|
2000
|
-
constructor({ password, rawPassword,
|
|
2007
|
+
constructor({ password, rawPassword, encryptionStrength, checkPasswordOnly }) {
|
|
2001
2008
|
super({
|
|
2002
2009
|
start() {
|
|
2003
2010
|
Object.assign(this, {
|
|
2004
2011
|
ready: new Promise(resolve => this.resolveReady = resolve),
|
|
2005
2012
|
password: encodePassword(password, rawPassword),
|
|
2006
|
-
signed,
|
|
2007
2013
|
strength: encryptionStrength - 1,
|
|
2008
2014
|
pending: new Uint8Array()
|
|
2009
2015
|
});
|
|
@@ -2032,7 +2038,6 @@
|
|
|
2032
2038
|
},
|
|
2033
2039
|
async flush(controller) {
|
|
2034
2040
|
const {
|
|
2035
|
-
signed,
|
|
2036
2041
|
ctr,
|
|
2037
2042
|
hmac,
|
|
2038
2043
|
pending,
|
|
@@ -2049,13 +2054,15 @@
|
|
|
2049
2054
|
const decryptedChunk = ctr.update(encryptedChunk);
|
|
2050
2055
|
decryptedChunkArray = fromBits(codecBytes, decryptedChunk);
|
|
2051
2056
|
}
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2057
|
+
const signature = subarray(fromBits(codecBytes, hmac.digest()), 0, SIGNATURE_LENGTH);
|
|
2058
|
+
// branch-free comparison: accumulate the differences instead of
|
|
2059
|
+
// short-circuiting on the first mismatching byte
|
|
2060
|
+
let invalidSignature = pending.length < SIGNATURE_LENGTH ? 1 : 0;
|
|
2061
|
+
for (let indexSignature = 0; indexSignature < SIGNATURE_LENGTH; indexSignature++) {
|
|
2062
|
+
invalidSignature |= signature[indexSignature] ^ originalSignature[indexSignature];
|
|
2063
|
+
}
|
|
2064
|
+
if (invalidSignature) {
|
|
2065
|
+
throw new Error(ERR_INVALID_SIGNATURE);
|
|
2059
2066
|
}
|
|
2060
2067
|
controller.enqueue(decryptedChunkArray);
|
|
2061
2068
|
}
|
|
@@ -2127,11 +2134,11 @@
|
|
|
2127
2134
|
hmac,
|
|
2128
2135
|
pending
|
|
2129
2136
|
} = aesCrypto;
|
|
2130
|
-
const inputLength = input.length - paddingEnd;
|
|
2131
2137
|
if (pending.length) {
|
|
2132
2138
|
input = concat(pending, input);
|
|
2133
|
-
output = expand(output, inputLength - (inputLength % BLOCK_LENGTH));
|
|
2134
2139
|
}
|
|
2140
|
+
const inputLength = input.length - paddingEnd;
|
|
2141
|
+
output = expand(output, paddingStart + (inputLength - (inputLength % BLOCK_LENGTH)));
|
|
2135
2142
|
let offset;
|
|
2136
2143
|
for (offset = 0; offset <= inputLength - BLOCK_LENGTH; offset += BLOCK_LENGTH) {
|
|
2137
2144
|
const inputChunk = toBits(codecBytes, subarray(input, offset, offset + BLOCK_LENGTH));
|
|
@@ -2279,21 +2286,24 @@
|
|
|
2279
2286
|
|
|
2280
2287
|
class ZipCryptoDecryptionStream extends TransformStream {
|
|
2281
2288
|
|
|
2282
|
-
constructor({ password, passwordVerification, checkPasswordOnly }) {
|
|
2289
|
+
constructor({ password, rawPassword, passwordVerification, checkPasswordOnly }) {
|
|
2283
2290
|
super({
|
|
2284
2291
|
start() {
|
|
2285
2292
|
Object.assign(this, {
|
|
2286
2293
|
password,
|
|
2294
|
+
rawPassword,
|
|
2287
2295
|
passwordVerification
|
|
2288
2296
|
});
|
|
2289
|
-
createKeys(this, password);
|
|
2297
|
+
createKeys(this, password, rawPassword);
|
|
2290
2298
|
},
|
|
2291
2299
|
transform(chunk, controller) {
|
|
2292
2300
|
const zipCrypto = this;
|
|
2293
|
-
if (zipCrypto.password) {
|
|
2301
|
+
if (zipCrypto.password || zipCrypto.rawPassword) {
|
|
2294
2302
|
const decryptedHeader = decrypt(zipCrypto, chunk.subarray(0, HEADER_LENGTH));
|
|
2295
|
-
zipCrypto.password = null;
|
|
2296
|
-
|
|
2303
|
+
zipCrypto.password = zipCrypto.rawPassword = null;
|
|
2304
|
+
// verify the password with a branch-free comparison of the verification byte, mirroring
|
|
2305
|
+
// the constant-time AES HMAC signature check
|
|
2306
|
+
if ((decryptedHeader.at(-1) ^ zipCrypto.passwordVerification) != 0) {
|
|
2297
2307
|
throw new Error(ERR_INVALID_PASSWORD);
|
|
2298
2308
|
}
|
|
2299
2309
|
chunk = chunk.subarray(HEADER_LENGTH);
|
|
@@ -2310,21 +2320,22 @@
|
|
|
2310
2320
|
|
|
2311
2321
|
class ZipCryptoEncryptionStream extends TransformStream {
|
|
2312
2322
|
|
|
2313
|
-
constructor({ password, passwordVerification }) {
|
|
2323
|
+
constructor({ password, rawPassword, passwordVerification }) {
|
|
2314
2324
|
super({
|
|
2315
2325
|
start() {
|
|
2316
2326
|
Object.assign(this, {
|
|
2317
2327
|
password,
|
|
2328
|
+
rawPassword,
|
|
2318
2329
|
passwordVerification
|
|
2319
2330
|
});
|
|
2320
|
-
createKeys(this, password);
|
|
2331
|
+
createKeys(this, password, rawPassword);
|
|
2321
2332
|
},
|
|
2322
2333
|
transform(chunk, controller) {
|
|
2323
2334
|
const zipCrypto = this;
|
|
2324
2335
|
let output;
|
|
2325
2336
|
let offset;
|
|
2326
|
-
if (zipCrypto.password) {
|
|
2327
|
-
zipCrypto.password = null;
|
|
2337
|
+
if (zipCrypto.password || zipCrypto.rawPassword) {
|
|
2338
|
+
zipCrypto.password = zipCrypto.rawPassword = null;
|
|
2328
2339
|
const header = getRandomValues(new Uint8Array(HEADER_LENGTH));
|
|
2329
2340
|
header[HEADER_LENGTH - 1] = zipCrypto.passwordVerification;
|
|
2330
2341
|
output = new Uint8Array(chunk.length + header.length);
|
|
@@ -2359,25 +2370,31 @@
|
|
|
2359
2370
|
return output;
|
|
2360
2371
|
}
|
|
2361
2372
|
|
|
2362
|
-
function createKeys(target, password) {
|
|
2373
|
+
function createKeys(target, password, rawPassword) {
|
|
2363
2374
|
const keys = [0x12345678, 0x23456789, 0x34567890];
|
|
2364
2375
|
Object.assign(target, {
|
|
2365
2376
|
keys,
|
|
2366
2377
|
crcKey0: new Crc32(keys[0]),
|
|
2367
2378
|
crcKey2: new Crc32(keys[2])
|
|
2368
2379
|
});
|
|
2369
|
-
|
|
2370
|
-
|
|
2380
|
+
if (rawPassword) {
|
|
2381
|
+
for (let index = 0; index < rawPassword.length; index++) {
|
|
2382
|
+
updateKeys(target, rawPassword[index]);
|
|
2383
|
+
}
|
|
2384
|
+
} else {
|
|
2385
|
+
for (let index = 0; index < password.length; index++) {
|
|
2386
|
+
updateKeys(target, password.charCodeAt(index));
|
|
2387
|
+
}
|
|
2371
2388
|
}
|
|
2372
2389
|
}
|
|
2373
2390
|
|
|
2374
2391
|
function updateKeys(target, byte) {
|
|
2375
|
-
let [
|
|
2392
|
+
let [, key1] = target.keys;
|
|
2376
2393
|
target.crcKey0.append([byte]);
|
|
2377
|
-
key0 = ~target.crcKey0.get();
|
|
2394
|
+
const key0 = ~target.crcKey0.get();
|
|
2378
2395
|
key1 = getInt32(Math.imul(getInt32(key1 + getInt8(key0)), 134775813) + 1);
|
|
2379
2396
|
target.crcKey2.append([key1 >>> 24]);
|
|
2380
|
-
key2 = ~target.crcKey2.get();
|
|
2397
|
+
const key2 = ~target.crcKey2.get();
|
|
2381
2398
|
target.keys = [key0, key1, key2];
|
|
2382
2399
|
}
|
|
2383
2400
|
|
|
@@ -2424,6 +2441,7 @@
|
|
|
2424
2441
|
|
|
2425
2442
|
|
|
2426
2443
|
const ERR_INVALID_UNCOMPRESSED_SIZE = "Invalid uncompressed size";
|
|
2444
|
+
const ERR_INVALID_COMPRESSED_DATA = "Invalid compressed data";
|
|
2427
2445
|
const FORMAT_DEFLATE_RAW = "deflate-raw";
|
|
2428
2446
|
const FORMAT_DEFLATE64_RAW = "deflate64-raw";
|
|
2429
2447
|
|
|
@@ -2480,6 +2498,7 @@
|
|
|
2480
2498
|
}
|
|
2481
2499
|
if (compressed) {
|
|
2482
2500
|
readable = pipeThroughCommpressionStream(readable, useCompressionStream, { chunkSize, deflate64 }, DecompressionStream, DecompressionStreamZlib, DecompressionStream);
|
|
2501
|
+
readable = mapInflateStreamError(readable);
|
|
2483
2502
|
}
|
|
2484
2503
|
if ((!encrypted || zipCrypto) && signed) {
|
|
2485
2504
|
crc32Stream = new Crc32Stream();
|
|
@@ -2530,6 +2549,37 @@
|
|
|
2530
2549
|
return readable.pipeThrough(transformStream);
|
|
2531
2550
|
}
|
|
2532
2551
|
|
|
2552
|
+
// DecompressionStream implementations can fail with a message-less TypeError on malformed
|
|
2553
|
+
// input; give these errors an identifiable message (errors raised by the other stages of
|
|
2554
|
+
// the pipeline, e.g. ERR_INVALID_PASSWORD, always carry a message and pass through as-is)
|
|
2555
|
+
function mapInflateStreamError(readable) {
|
|
2556
|
+
const reader = readable.getReader();
|
|
2557
|
+
return new ReadableStream({
|
|
2558
|
+
async pull(controller) {
|
|
2559
|
+
let result;
|
|
2560
|
+
try {
|
|
2561
|
+
result = await reader.read();
|
|
2562
|
+
} catch (error) {
|
|
2563
|
+
if (error && error.message) {
|
|
2564
|
+
throw error;
|
|
2565
|
+
}
|
|
2566
|
+
const mappedError = new Error(ERR_INVALID_COMPRESSED_DATA);
|
|
2567
|
+
mappedError.cause = error;
|
|
2568
|
+
throw mappedError;
|
|
2569
|
+
}
|
|
2570
|
+
const { value, done } = result;
|
|
2571
|
+
if (done) {
|
|
2572
|
+
controller.close();
|
|
2573
|
+
} else {
|
|
2574
|
+
controller.enqueue(value);
|
|
2575
|
+
}
|
|
2576
|
+
},
|
|
2577
|
+
cancel(reason) {
|
|
2578
|
+
return reader.cancel(reason);
|
|
2579
|
+
}
|
|
2580
|
+
});
|
|
2581
|
+
}
|
|
2582
|
+
|
|
2533
2583
|
/*
|
|
2534
2584
|
Copyright (c) 2022 Gildas Lormeau. All rights reserved.
|
|
2535
2585
|
|
|
@@ -2559,6 +2609,7 @@
|
|
|
2559
2609
|
*/
|
|
2560
2610
|
|
|
2561
2611
|
|
|
2612
|
+
const DEFAULT_CHUNK_SIZE = 64 * 1024;
|
|
2562
2613
|
const MESSAGE_EVENT_TYPE = "message";
|
|
2563
2614
|
const MESSAGE_START = "start";
|
|
2564
2615
|
const MESSAGE_PULL = "pull";
|
|
@@ -2627,6 +2678,9 @@
|
|
|
2627
2678
|
|
|
2628
2679
|
constructor(chunkSize) {
|
|
2629
2680
|
let pendingChunk;
|
|
2681
|
+
if (!(chunkSize >= 1)) {
|
|
2682
|
+
chunkSize = DEFAULT_CHUNK_SIZE;
|
|
2683
|
+
}
|
|
2630
2684
|
super({
|
|
2631
2685
|
transform,
|
|
2632
2686
|
flush(controller) {
|
|
@@ -2644,12 +2698,12 @@
|
|
|
2644
2698
|
chunk = newChunk;
|
|
2645
2699
|
pendingChunk = null;
|
|
2646
2700
|
}
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
pendingChunk = chunk;
|
|
2701
|
+
let offset = 0;
|
|
2702
|
+
while (chunk.length - offset > chunkSize) {
|
|
2703
|
+
controller.enqueue(chunk.slice(offset, offset + chunkSize));
|
|
2704
|
+
offset += chunkSize;
|
|
2652
2705
|
}
|
|
2706
|
+
pendingChunk = offset ? chunk.slice(offset) : chunk;
|
|
2653
2707
|
}
|
|
2654
2708
|
}
|
|
2655
2709
|
}
|
|
@@ -2684,8 +2738,10 @@
|
|
|
2684
2738
|
|
|
2685
2739
|
|
|
2686
2740
|
const MODULE_WORKER_OPTIONS = { type: "module" };
|
|
2741
|
+
const ERROR_EVENT_TYPE = "error";
|
|
2742
|
+
const MESSAGE_ERROR_EVENT_TYPE = "messageerror";
|
|
2687
2743
|
|
|
2688
|
-
let webWorkerSupported, webWorkerURI, webWorkerOptions;
|
|
2744
|
+
let webWorkerSupported, webWorkerSource, webWorkerURI, webWorkerOptions;
|
|
2689
2745
|
let transferStreamsSupported = true;
|
|
2690
2746
|
try {
|
|
2691
2747
|
transferStreamsSupported = typeof structuredClone == FUNCTION_TYPE && structuredClone(new DOMException("", "AbortError")).code !== UNDEFINED_VALUE;
|
|
@@ -2700,8 +2756,9 @@
|
|
|
2700
2756
|
const { signal } = streamOptions;
|
|
2701
2757
|
Object.assign(workerData, {
|
|
2702
2758
|
busy: true,
|
|
2759
|
+
generation: (workerData.generation || 0) + 1,
|
|
2703
2760
|
readable: readable
|
|
2704
|
-
.pipeThrough(new ChunkStream(config
|
|
2761
|
+
.pipeThrough(new ChunkStream(getChunkSize(config)))
|
|
2705
2762
|
.pipeThrough(new ProgressWatcherStream(streamOptions), { signal }),
|
|
2706
2763
|
writable,
|
|
2707
2764
|
options: Object.assign({}, options),
|
|
@@ -2724,15 +2781,17 @@
|
|
|
2724
2781
|
});
|
|
2725
2782
|
},
|
|
2726
2783
|
onTaskFinished() {
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2784
|
+
if (workerData.busy) {
|
|
2785
|
+
const { resolveTerminated } = workerData;
|
|
2786
|
+
if (resolveTerminated) {
|
|
2787
|
+
workerData.resolveTerminated = null;
|
|
2788
|
+
workerData.terminated = true;
|
|
2789
|
+
workerData.worker.terminate();
|
|
2790
|
+
resolveTerminated();
|
|
2791
|
+
}
|
|
2792
|
+
workerData.busy = false;
|
|
2793
|
+
onTaskFinished(workerData);
|
|
2733
2794
|
}
|
|
2734
|
-
workerData.busy = false;
|
|
2735
|
-
onTaskFinished(workerData);
|
|
2736
2795
|
}
|
|
2737
2796
|
});
|
|
2738
2797
|
if (webWorkerSupported === UNDEFINED_VALUE) {
|
|
@@ -2801,6 +2860,7 @@
|
|
|
2801
2860
|
}
|
|
2802
2861
|
Object.assign(workerData, {
|
|
2803
2862
|
worker,
|
|
2863
|
+
terminated: false,
|
|
2804
2864
|
interface: {
|
|
2805
2865
|
run: () => runWebWorker(workerData, { chunkSize, wasmURI, baseURI })
|
|
2806
2866
|
}
|
|
@@ -2855,37 +2915,85 @@
|
|
|
2855
2915
|
result
|
|
2856
2916
|
});
|
|
2857
2917
|
const { readable, options } = workerData;
|
|
2858
|
-
const { writable, closed } = watchClosedStream(workerData.writable);
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2918
|
+
const { writable, closed, abortPipe } = watchClosedStream(workerData.writable);
|
|
2919
|
+
let streamsTransferred;
|
|
2920
|
+
try {
|
|
2921
|
+
streamsTransferred = sendMessage({
|
|
2922
|
+
type: MESSAGE_START,
|
|
2923
|
+
options,
|
|
2924
|
+
config,
|
|
2925
|
+
readable,
|
|
2926
|
+
writable
|
|
2927
|
+
}, workerData);
|
|
2928
|
+
} catch (error) {
|
|
2929
|
+
abortPipe();
|
|
2930
|
+
try {
|
|
2931
|
+
await closed;
|
|
2932
|
+
} catch {
|
|
2933
|
+
// ignored
|
|
2934
|
+
}
|
|
2935
|
+
workerData.onTaskFinished();
|
|
2936
|
+
throw error;
|
|
2937
|
+
}
|
|
2866
2938
|
if (!streamsTransferred) {
|
|
2867
2939
|
Object.assign(workerData, {
|
|
2868
2940
|
reader: readable.getReader(),
|
|
2869
2941
|
writer: writable.getWriter()
|
|
2870
2942
|
});
|
|
2871
2943
|
}
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
await
|
|
2944
|
+
try {
|
|
2945
|
+
const resultValue = await result;
|
|
2946
|
+
await closeWritable();
|
|
2947
|
+
await closed;
|
|
2948
|
+
return resultValue;
|
|
2949
|
+
} catch (error) {
|
|
2950
|
+
await closeWritable();
|
|
2951
|
+
// unblock the pipe watcher if the worker died before closing its end of the stream
|
|
2952
|
+
abortPipe();
|
|
2953
|
+
try {
|
|
2954
|
+
await closed;
|
|
2955
|
+
} catch {
|
|
2956
|
+
// ignored
|
|
2957
|
+
}
|
|
2958
|
+
throw error;
|
|
2959
|
+
}
|
|
2960
|
+
|
|
2961
|
+
async function closeWritable() {
|
|
2962
|
+
if (!streamsTransferred && !writable.locked) {
|
|
2963
|
+
try {
|
|
2964
|
+
await writable.getWriter().close();
|
|
2965
|
+
} catch {
|
|
2966
|
+
// ignored
|
|
2967
|
+
}
|
|
2968
|
+
}
|
|
2875
2969
|
}
|
|
2876
|
-
await closed;
|
|
2877
|
-
return resultValue;
|
|
2878
2970
|
}
|
|
2879
2971
|
|
|
2880
2972
|
function watchClosedStream(writableSource) {
|
|
2973
|
+
const abortController = new AbortController();
|
|
2881
2974
|
const { writable, readable } = new TransformStream();
|
|
2882
|
-
const closed = readable.pipeTo(writableSource, { preventClose: true });
|
|
2883
|
-
|
|
2975
|
+
const closed = readable.pipeTo(writableSource, { preventClose: true, preventAbort: true, signal: abortController.signal });
|
|
2976
|
+
// the pipe can fail before `closed` is awaited (e.g. the destination write fails);
|
|
2977
|
+
// mark the rejection as handled so it cannot kill the process in the meantime
|
|
2978
|
+
closed.catch(() => { });
|
|
2979
|
+
return { writable, closed, abortPipe: () => abortController.abort() };
|
|
2980
|
+
}
|
|
2981
|
+
|
|
2982
|
+
function terminateWorker$1(workerData) {
|
|
2983
|
+
const { worker } = workerData;
|
|
2984
|
+
if (worker) {
|
|
2985
|
+
try {
|
|
2986
|
+
worker.terminate();
|
|
2987
|
+
} catch {
|
|
2988
|
+
// ignored
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2991
|
+
workerData.interface = null;
|
|
2884
2992
|
}
|
|
2885
2993
|
|
|
2886
2994
|
function getWebWorker(url, baseURI, workerData, isModuleType, useBlobURI = true) {
|
|
2887
2995
|
let worker, resolvedURI, resolvedOptions;
|
|
2888
|
-
if (webWorkerURI === UNDEFINED_VALUE) {
|
|
2996
|
+
if (webWorkerURI === UNDEFINED_VALUE || webWorkerSource !== url) {
|
|
2889
2997
|
// deno-lint-ignore valid-typeof
|
|
2890
2998
|
const isFunctionURI = typeof url == FUNCTION_TYPE;
|
|
2891
2999
|
if (isFunctionURI) {
|
|
@@ -2942,16 +3050,36 @@
|
|
|
2942
3050
|
}
|
|
2943
3051
|
}
|
|
2944
3052
|
}
|
|
3053
|
+
webWorkerSource = url;
|
|
2945
3054
|
webWorkerURI = resolvedURI;
|
|
2946
3055
|
webWorkerOptions = resolvedOptions;
|
|
2947
3056
|
} else {
|
|
2948
3057
|
worker = new Worker(webWorkerURI, webWorkerOptions);
|
|
2949
3058
|
}
|
|
2950
3059
|
worker.addEventListener(MESSAGE_EVENT_TYPE, event => onMessage(event, workerData));
|
|
3060
|
+
worker.addEventListener(ERROR_EVENT_TYPE, event => onWorkerError(event, workerData));
|
|
3061
|
+
worker.addEventListener(MESSAGE_ERROR_EVENT_TYPE, event => onWorkerError(event, workerData));
|
|
2951
3062
|
return worker;
|
|
2952
3063
|
}
|
|
2953
3064
|
|
|
2954
|
-
function
|
|
3065
|
+
function onWorkerError(event, workerData) {
|
|
3066
|
+
if (event.preventDefault) {
|
|
3067
|
+
event.preventDefault();
|
|
3068
|
+
}
|
|
3069
|
+
const { rejectResult, writer, onTaskFinished } = workerData;
|
|
3070
|
+
// the worker is unusable (e.g. its script failed to load); terminate it and
|
|
3071
|
+
// fail the current task instead of letting it hang forever
|
|
3072
|
+
terminateWorker$1(workerData);
|
|
3073
|
+
if (rejectResult) {
|
|
3074
|
+
rejectResult(event.error || new Error(event.message || ERROR_EVENT_TYPE));
|
|
3075
|
+
if (writer) {
|
|
3076
|
+
writer.releaseLock();
|
|
3077
|
+
}
|
|
3078
|
+
onTaskFinished();
|
|
3079
|
+
}
|
|
3080
|
+
}
|
|
3081
|
+
|
|
3082
|
+
function sendMessage(message, { worker, writer, transferStreams }) {
|
|
2955
3083
|
try {
|
|
2956
3084
|
const { value, readable, writable } = message;
|
|
2957
3085
|
const transferables = [];
|
|
@@ -2985,14 +3113,16 @@
|
|
|
2985
3113
|
if (writer) {
|
|
2986
3114
|
writer.releaseLock();
|
|
2987
3115
|
}
|
|
2988
|
-
onTaskFinished();
|
|
2989
3116
|
throw error;
|
|
2990
3117
|
}
|
|
2991
3118
|
}
|
|
2992
3119
|
|
|
2993
3120
|
async function onMessage({ data }, workerData) {
|
|
2994
3121
|
const { type, value, messageId, result, error } = data;
|
|
2995
|
-
const { reader, writer, resolveResult, rejectResult, onTaskFinished } = workerData;
|
|
3122
|
+
const { reader, writer, resolveResult, rejectResult, onTaskFinished, generation } = workerData;
|
|
3123
|
+
// continuations below can settle after the worker slot has been reassigned to a new
|
|
3124
|
+
// task (e.g. a pending pull read failing late); they must not touch the new task
|
|
3125
|
+
const stale = () => workerData.generation != generation;
|
|
2996
3126
|
try {
|
|
2997
3127
|
if (error) {
|
|
2998
3128
|
const { message, stack, code, name, outputSize } = error;
|
|
@@ -3002,23 +3132,32 @@
|
|
|
3002
3132
|
} else {
|
|
3003
3133
|
if (type == MESSAGE_PULL) {
|
|
3004
3134
|
const { value, done } = await reader.read();
|
|
3005
|
-
|
|
3135
|
+
if (!stale()) {
|
|
3136
|
+
sendMessage({ type: MESSAGE_DATA, value, done, messageId }, workerData);
|
|
3137
|
+
}
|
|
3006
3138
|
}
|
|
3007
3139
|
if (type == MESSAGE_DATA) {
|
|
3008
3140
|
await writer.ready;
|
|
3009
3141
|
await writer.write(new Uint8Array(value));
|
|
3010
|
-
|
|
3142
|
+
if (!stale()) {
|
|
3143
|
+
sendMessage({ type: MESSAGE_ACK_DATA, messageId }, workerData);
|
|
3144
|
+
}
|
|
3011
3145
|
}
|
|
3012
3146
|
if (type == MESSAGE_CLOSE) {
|
|
3013
3147
|
close(null, result);
|
|
3014
3148
|
}
|
|
3015
3149
|
}
|
|
3016
3150
|
} catch (error) {
|
|
3017
|
-
|
|
3018
|
-
|
|
3151
|
+
if (!stale()) {
|
|
3152
|
+
terminateWorker$1(workerData);
|
|
3153
|
+
close(error);
|
|
3154
|
+
}
|
|
3019
3155
|
}
|
|
3020
3156
|
|
|
3021
3157
|
function close(error, result) {
|
|
3158
|
+
if (stale()) {
|
|
3159
|
+
return;
|
|
3160
|
+
}
|
|
3022
3161
|
if (error) {
|
|
3023
3162
|
rejectResult(error);
|
|
3024
3163
|
} else {
|
|
@@ -3062,6 +3201,8 @@
|
|
|
3062
3201
|
|
|
3063
3202
|
let pool = [];
|
|
3064
3203
|
const pendingRequests = [];
|
|
3204
|
+
let starvationTimeout;
|
|
3205
|
+
let starvationDelay;
|
|
3065
3206
|
|
|
3066
3207
|
let indexWorker = 0;
|
|
3067
3208
|
|
|
@@ -3088,14 +3229,20 @@
|
|
|
3088
3229
|
pool.push(workerData);
|
|
3089
3230
|
return new CodecWorker(workerData, stream, workerOptions, onTaskFinished);
|
|
3090
3231
|
} else {
|
|
3091
|
-
return new Promise(resolve =>
|
|
3232
|
+
return new Promise(resolve => {
|
|
3233
|
+
pendingRequests.push({ resolve, stream, workerOptions });
|
|
3234
|
+
starvationDelay = config.workerStarvationTimeout;
|
|
3235
|
+
armStarvationTimeout();
|
|
3236
|
+
});
|
|
3092
3237
|
}
|
|
3093
3238
|
}
|
|
3094
3239
|
|
|
3095
3240
|
function onTaskFinished(workerData) {
|
|
3241
|
+
clearStarvationTimeout();
|
|
3096
3242
|
if (pendingRequests.length) {
|
|
3097
3243
|
const [{ resolve, stream, workerOptions }] = pendingRequests.splice(0, 1);
|
|
3098
3244
|
resolve(new CodecWorker(workerData, stream, workerOptions, onTaskFinished));
|
|
3245
|
+
armStarvationTimeout();
|
|
3099
3246
|
} else if (workerData.worker) {
|
|
3100
3247
|
clearTerminateTimeout(workerData);
|
|
3101
3248
|
terminateWorker(workerData, workerOptions);
|
|
@@ -3105,6 +3252,34 @@
|
|
|
3105
3252
|
}
|
|
3106
3253
|
}
|
|
3107
3254
|
|
|
3255
|
+
function armStarvationTimeout() {
|
|
3256
|
+
if (!starvationTimeout && pendingRequests.length && Number.isFinite(starvationDelay) && starvationDelay >= 0) {
|
|
3257
|
+
starvationTimeout = setTimeout(onWorkerStarvation, starvationDelay);
|
|
3258
|
+
}
|
|
3259
|
+
}
|
|
3260
|
+
|
|
3261
|
+
function clearStarvationTimeout() {
|
|
3262
|
+
if (starvationTimeout) {
|
|
3263
|
+
clearTimeout(starvationTimeout);
|
|
3264
|
+
starvationTimeout = null;
|
|
3265
|
+
}
|
|
3266
|
+
}
|
|
3267
|
+
|
|
3268
|
+
function onWorkerStarvation() {
|
|
3269
|
+
starvationTimeout = null;
|
|
3270
|
+
if (pendingRequests.length) {
|
|
3271
|
+
const [{ resolve, stream, workerOptions }] = pendingRequests.splice(0, 1);
|
|
3272
|
+
const inlineWorkerOptions = Object.assign({}, workerOptions, { useWebWorkers: false, workerURI: UNDEFINED_VALUE });
|
|
3273
|
+
resolve(new CodecWorker({}, stream, inlineWorkerOptions, onInlineTaskFinished));
|
|
3274
|
+
armStarvationTimeout();
|
|
3275
|
+
}
|
|
3276
|
+
}
|
|
3277
|
+
|
|
3278
|
+
function onInlineTaskFinished() {
|
|
3279
|
+
clearStarvationTimeout();
|
|
3280
|
+
armStarvationTimeout();
|
|
3281
|
+
}
|
|
3282
|
+
|
|
3108
3283
|
function terminateWorker(workerData, workerOptions) {
|
|
3109
3284
|
const { config } = workerOptions;
|
|
3110
3285
|
const { terminateWorkerTimeout } = config;
|
|
@@ -3162,7 +3337,7 @@
|
|
|
3162
3337
|
|
|
3163
3338
|
/* global TextDecoder */
|
|
3164
3339
|
|
|
3165
|
-
const CP437 = "\0☺☻♥♦♣♠•◘○◙♂♀♪♫☼►◄↕‼¶§▬↨↑↓→←∟↔▲▼ !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~⌂ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢£¥₧ƒáíóúñѪº¿⌐¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀αßΓπΣσµτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■
|
|
3340
|
+
const CP437 = "\0☺☻♥♦♣♠•◘○◙♂♀♪♫☼►◄↕‼¶§▬↨↑↓→←∟↔▲▼ !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~⌂ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢£¥₧ƒáíóúñѪº¿⌐¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀αßΓπΣσµτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■ ".split("");
|
|
3166
3341
|
const VALID_CP437 = CP437.length == 256;
|
|
3167
3342
|
|
|
3168
3343
|
function decodeCP437(stringValue) {
|
|
@@ -3210,7 +3385,8 @@
|
|
|
3210
3385
|
if (encoding && encoding.trim().toLowerCase() == "cp437") {
|
|
3211
3386
|
return decodeCP437(value);
|
|
3212
3387
|
} else {
|
|
3213
|
-
|
|
3388
|
+
// ignoreBOM keeps a leading U+FEFF in filenames/comments instead of dropping it
|
|
3389
|
+
return new TextDecoder(encoding, { ignoreBOM: true }).decode(value);
|
|
3214
3390
|
}
|
|
3215
3391
|
}
|
|
3216
3392
|
|
|
@@ -3387,6 +3563,7 @@
|
|
|
3387
3563
|
const OPTION_CHECK_PASSWORD_ONLY = "checkPasswordOnly";
|
|
3388
3564
|
const OPTION_CHECK_OVERLAPPING_ENTRY_ONLY = "checkOverlappingEntryOnly";
|
|
3389
3565
|
const OPTION_CHECK_OVERLAPPING_ENTRY = "checkOverlappingEntry";
|
|
3566
|
+
const OPTION_CHECK_AMBIGUITY = "checkAmbiguity";
|
|
3390
3567
|
const OPTION_CHECK_SIGNATURE = "checkSignature";
|
|
3391
3568
|
const OPTION_USE_WEB_WORKERS = "useWebWorkers";
|
|
3392
3569
|
const OPTION_USE_COMPRESSION_STREAM = "useCompressionStream";
|
|
@@ -3444,12 +3621,16 @@
|
|
|
3444
3621
|
const ERR_EXTRAFIELD_ZIP64_NOT_FOUND = "Zip64 extra field not found";
|
|
3445
3622
|
const ERR_ENCRYPTED = "File contains encrypted entry";
|
|
3446
3623
|
const ERR_UNSUPPORTED_ENCRYPTION = "Encryption method not supported";
|
|
3447
|
-
const ERR_UNSUPPORTED_COMPRESSION = "Compression method not supported";
|
|
3624
|
+
const ERR_UNSUPPORTED_COMPRESSION$1 = "Compression method not supported";
|
|
3448
3625
|
const ERR_SPLIT_ZIP_FILE = "Split zip file";
|
|
3449
3626
|
const ERR_OVERLAPPING_ENTRY = "Overlapping entry found";
|
|
3627
|
+
const ERR_AMBIGUOUS_ARCHIVE = "Ambiguous archive";
|
|
3450
3628
|
const CHARSET_UTF8 = "utf-8";
|
|
3451
3629
|
const PROPERTY_NAME_UTF8_SUFFIX = "UTF8";
|
|
3452
3630
|
const CHARSET_CP437 = "cp437";
|
|
3631
|
+
// bits of the general purpose flag that change how an entry is interpreted; the level bits (1-2) are excluded
|
|
3632
|
+
// because they are informative only
|
|
3633
|
+
const BITFLAG_AMBIGUITY_MASK = BITFLAG_ENCRYPTED | BITFLAG_DATA_DESCRIPTOR | BITFLAG_LANG_ENCODING_FLAG;
|
|
3453
3634
|
const ZIP64_PROPERTIES = [
|
|
3454
3635
|
[PROPERTY_NAME_UNCOMPRESSED_SIZE, MAX_32_BITS],
|
|
3455
3636
|
[PROPERTY_NAME_COMPRESSED_SIZE, MAX_32_BITS],
|
|
@@ -3474,7 +3655,7 @@
|
|
|
3474
3655
|
reader: new GenericReader(reader),
|
|
3475
3656
|
options,
|
|
3476
3657
|
config: getConfiguration(),
|
|
3477
|
-
readRanges:
|
|
3658
|
+
readRanges: new Map()
|
|
3478
3659
|
});
|
|
3479
3660
|
}
|
|
3480
3661
|
|
|
@@ -3507,21 +3688,30 @@
|
|
|
3507
3688
|
const commentOffset = endOfDirectoryInfo.offset;
|
|
3508
3689
|
const commentLength = getUint16(endOfDirectoryView, 20);
|
|
3509
3690
|
const appendedDataOffset = commentOffset + END_OF_CENTRAL_DIR_LENGTH + commentLength;
|
|
3691
|
+
const checkAmbiguity = getOptionValue$1(zipReader, options, OPTION_CHECK_AMBIGUITY);
|
|
3692
|
+
if (checkAmbiguity && appendedDataOffset != reader.size) {
|
|
3693
|
+
throwAmbiguousArchive("appended data");
|
|
3694
|
+
}
|
|
3510
3695
|
let lastDiskNumber = getUint16(endOfDirectoryView, 4);
|
|
3511
3696
|
const expectedLastDiskNumber = reader.lastDiskNumber || 0;
|
|
3512
3697
|
let diskNumber = getUint16(endOfDirectoryView, 6);
|
|
3513
|
-
let filesLength = getUint16(endOfDirectoryView,
|
|
3698
|
+
let filesLength = getUint16(endOfDirectoryView, 10);
|
|
3514
3699
|
let prependedDataLength = 0;
|
|
3515
|
-
let startOffset
|
|
3700
|
+
let startOffset;
|
|
3701
|
+
let zip64EndOfDirectory;
|
|
3516
3702
|
if (directoryDataOffset == MAX_32_BITS || directoryDataLength == MAX_32_BITS || filesLength == MAX_16_BITS || diskNumber == MAX_16_BITS) {
|
|
3517
|
-
const endOfDirectoryLocatorArray =
|
|
3703
|
+
const endOfDirectoryLocatorArray = endOfDirectoryInfo.offset >= ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH ?
|
|
3704
|
+
await readUint8Array(reader, endOfDirectoryInfo.offset - ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH, ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH) :
|
|
3705
|
+
new Uint8Array();
|
|
3518
3706
|
const endOfDirectoryLocatorView = getDataView$1(endOfDirectoryLocatorArray);
|
|
3519
|
-
if (
|
|
3707
|
+
if (endOfDirectoryLocatorArray.length == ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH &&
|
|
3708
|
+
getUint32(endOfDirectoryLocatorView, 0) == ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIGNATURE) {
|
|
3520
3709
|
directoryDataOffset = getBigUint64(endOfDirectoryLocatorView, 8);
|
|
3521
3710
|
let endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH, -1);
|
|
3522
3711
|
let endOfDirectoryView = getDataView$1(endOfDirectoryArray);
|
|
3523
|
-
const expectedDirectoryDataOffset = endOfDirectoryInfo.offset - ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH - ZIP64_END_OF_CENTRAL_DIR_LENGTH;
|
|
3524
|
-
if (getUint32(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE &&
|
|
3712
|
+
const expectedDirectoryDataOffset = endOfDirectoryInfo.offset - ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH - ZIP64_END_OF_CENTRAL_DIR_LENGTH - (reader.lastDiskOffset || 0);
|
|
3713
|
+
if ((endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) &&
|
|
3714
|
+
directoryDataOffset != expectedDirectoryDataOffset && expectedDirectoryDataOffset >= 0) {
|
|
3525
3715
|
const originalDirectoryDataOffset = directoryDataOffset;
|
|
3526
3716
|
directoryDataOffset = expectedDirectoryDataOffset;
|
|
3527
3717
|
if (directoryDataOffset > originalDirectoryDataOffset) {
|
|
@@ -3530,24 +3720,36 @@
|
|
|
3530
3720
|
endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH, -1);
|
|
3531
3721
|
endOfDirectoryView = getDataView$1(endOfDirectoryArray);
|
|
3532
3722
|
}
|
|
3533
|
-
if (getUint32(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) {
|
|
3723
|
+
if (endOfDirectoryArray.length < ZIP64_END_OF_CENTRAL_DIR_LENGTH || getUint32(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE) {
|
|
3534
3724
|
throw new Error(ERR_EOCDR_LOCATOR_ZIP64_NOT_FOUND);
|
|
3535
3725
|
}
|
|
3726
|
+
zip64EndOfDirectory = true;
|
|
3536
3727
|
if (lastDiskNumber == MAX_16_BITS) {
|
|
3537
3728
|
lastDiskNumber = getUint32(endOfDirectoryView, 16);
|
|
3729
|
+
} else if (checkAmbiguity && lastDiskNumber != getUint32(endOfDirectoryView, 16)) {
|
|
3730
|
+
throwAmbiguousArchive("mismatched zip64 end of central directory record");
|
|
3538
3731
|
}
|
|
3539
3732
|
if (diskNumber == MAX_16_BITS) {
|
|
3540
3733
|
diskNumber = getUint32(endOfDirectoryView, 20);
|
|
3734
|
+
} else if (checkAmbiguity && diskNumber != getUint32(endOfDirectoryView, 20)) {
|
|
3735
|
+
throwAmbiguousArchive("mismatched zip64 end of central directory record");
|
|
3541
3736
|
}
|
|
3542
3737
|
if (filesLength == MAX_16_BITS) {
|
|
3543
3738
|
filesLength = getBigUint64(endOfDirectoryView, 32);
|
|
3739
|
+
} else if (checkAmbiguity && filesLength != getBigUint64(endOfDirectoryView, 32)) {
|
|
3740
|
+
throwAmbiguousArchive("mismatched zip64 end of central directory record");
|
|
3544
3741
|
}
|
|
3545
3742
|
if (directoryDataLength == MAX_32_BITS) {
|
|
3546
3743
|
directoryDataLength = getBigUint64(endOfDirectoryView, 40);
|
|
3744
|
+
} else if (checkAmbiguity && directoryDataLength != getBigUint64(endOfDirectoryView, 40)) {
|
|
3745
|
+
throwAmbiguousArchive("mismatched zip64 end of central directory record");
|
|
3547
3746
|
}
|
|
3548
|
-
directoryDataOffset
|
|
3747
|
+
directoryDataOffset = getBigUint64(endOfDirectoryView, 48) + prependedDataLength;
|
|
3549
3748
|
}
|
|
3550
3749
|
}
|
|
3750
|
+
const declaredDirectoryDataLength = directoryDataLength;
|
|
3751
|
+
const centralDirectoryEndOffset = endOfDirectoryInfo.offset -
|
|
3752
|
+
(zip64EndOfDirectory ? ZIP64_END_OF_CENTRAL_DIR_LENGTH + ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH : 0);
|
|
3551
3753
|
if (directoryDataOffset >= reader.size) {
|
|
3552
3754
|
prependedDataLength = reader.size - directoryDataOffset - directoryDataLength - END_OF_CENTRAL_DIR_LENGTH;
|
|
3553
3755
|
directoryDataOffset = reader.size - directoryDataLength - END_OF_CENTRAL_DIR_LENGTH;
|
|
@@ -3562,8 +3764,11 @@
|
|
|
3562
3764
|
let directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength, diskNumber);
|
|
3563
3765
|
let directoryView = getDataView$1(directoryArray);
|
|
3564
3766
|
if (directoryDataLength) {
|
|
3565
|
-
|
|
3566
|
-
|
|
3767
|
+
if (directoryArray.length < 4) {
|
|
3768
|
+
throw new Error(ERR_BAD_FORMAT);
|
|
3769
|
+
}
|
|
3770
|
+
const expectedDirectoryDataOffset = centralDirectoryEndOffset - directoryDataLength - (reader.lastDiskOffset || 0);
|
|
3771
|
+
if (getUint32(directoryView, offset) != CENTRAL_FILE_HEADER_SIGNATURE && directoryDataOffset != expectedDirectoryDataOffset && diskNumber == lastDiskNumber) {
|
|
3567
3772
|
const originalDirectoryDataOffset = directoryDataOffset;
|
|
3568
3773
|
directoryDataOffset = expectedDirectoryDataOffset;
|
|
3569
3774
|
if (directoryDataOffset > originalDirectoryDataOffset) {
|
|
@@ -3573,8 +3778,8 @@
|
|
|
3573
3778
|
directoryView = getDataView$1(directoryArray);
|
|
3574
3779
|
}
|
|
3575
3780
|
}
|
|
3576
|
-
const expectedDirectoryDataLength =
|
|
3577
|
-
if (directoryDataLength != expectedDirectoryDataLength && expectedDirectoryDataLength >= 0) {
|
|
3781
|
+
const expectedDirectoryDataLength = centralDirectoryEndOffset - directoryDataOffset - (reader.lastDiskOffset || 0);
|
|
3782
|
+
if (directoryDataLength != expectedDirectoryDataLength && expectedDirectoryDataLength >= 0 && diskNumber == lastDiskNumber) {
|
|
3578
3783
|
directoryDataLength = expectedDirectoryDataLength;
|
|
3579
3784
|
directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength, diskNumber);
|
|
3580
3785
|
directoryView = getDataView$1(directoryArray);
|
|
@@ -3582,16 +3787,19 @@
|
|
|
3582
3787
|
if (directoryDataOffset < 0 || directoryDataOffset >= reader.size) {
|
|
3583
3788
|
throw new Error(ERR_BAD_FORMAT);
|
|
3584
3789
|
}
|
|
3790
|
+
startOffset = directoryDataOffset;
|
|
3585
3791
|
const filenameEncoding = getOptionValue$1(zipReader, options, OPTION_FILENAME_ENCODING);
|
|
3586
3792
|
const commentEncoding = getOptionValue$1(zipReader, options, OPTION_COMMENT_ENCODING);
|
|
3793
|
+
const filenames = checkAmbiguity ? new Set() : UNDEFINED_VALUE;
|
|
3794
|
+
let duplicateFilename;
|
|
3587
3795
|
for (let indexFile = 0; indexFile < filesLength; indexFile++) {
|
|
3588
3796
|
const fileEntry = new ZipEntry$1(reader, config, zipReader.options);
|
|
3589
|
-
if (getUint32(directoryView, offset) != CENTRAL_FILE_HEADER_SIGNATURE) {
|
|
3797
|
+
if (offset + CENTRAL_FILE_HEADER_LENGTH > directoryArray.length || getUint32(directoryView, offset) != CENTRAL_FILE_HEADER_SIGNATURE) {
|
|
3590
3798
|
throw new Error(ERR_CENTRAL_DIRECTORY_NOT_FOUND);
|
|
3591
3799
|
}
|
|
3592
3800
|
readCommonHeader(fileEntry, directoryView, offset + 6);
|
|
3593
3801
|
const languageEncodingFlag = Boolean(fileEntry.bitFlag.languageEncodingFlag);
|
|
3594
|
-
const filenameOffset = offset +
|
|
3802
|
+
const filenameOffset = offset + CENTRAL_FILE_HEADER_LENGTH;
|
|
3595
3803
|
const extraFieldOffset = filenameOffset + fileEntry.filenameLength;
|
|
3596
3804
|
const commentOffset = extraFieldOffset + fileEntry.extraFieldLength;
|
|
3597
3805
|
const versionMadeBy = getUint16(directoryView, offset + 4);
|
|
@@ -3612,7 +3820,7 @@
|
|
|
3612
3820
|
directory: Boolean(msdosAttributesRaw & FILE_ATTR_MSDOS_DIR_MASK),
|
|
3613
3821
|
archive: Boolean(msdosAttributesRaw & FILE_ATTR_MSDOS_ARCHIVE_MASK)
|
|
3614
3822
|
};
|
|
3615
|
-
const offsetFileEntry = getUint32(directoryView, offset + 42)
|
|
3823
|
+
const offsetFileEntry = getUint32(directoryView, offset + 42);
|
|
3616
3824
|
const decode = getOptionValue$1(zipReader, options, OPTION_DECODE_TEXT) || decodeText;
|
|
3617
3825
|
const rawFilenameEncoding = filenameUTF8 ? CHARSET_UTF8 : filenameEncoding || CHARSET_CP437;
|
|
3618
3826
|
const rawCommentEncoding = commentUTF8 ? CHARSET_UTF8 : commentEncoding || CHARSET_CP437;
|
|
@@ -3625,6 +3833,7 @@
|
|
|
3625
3833
|
comment = decodeText(rawComment, rawCommentEncoding);
|
|
3626
3834
|
}
|
|
3627
3835
|
Object.assign(fileEntry, {
|
|
3836
|
+
index: indexFile,
|
|
3628
3837
|
versionMadeBy,
|
|
3629
3838
|
msDosCompatible,
|
|
3630
3839
|
compressedSize: 0,
|
|
@@ -3644,8 +3853,15 @@
|
|
|
3644
3853
|
filename,
|
|
3645
3854
|
comment
|
|
3646
3855
|
});
|
|
3647
|
-
startOffset = Math.max(offsetFileEntry, startOffset);
|
|
3648
3856
|
readCommonFooter(fileEntry, fileEntry, directoryView, offset + 6);
|
|
3857
|
+
fileEntry.offset += prependedDataLength;
|
|
3858
|
+
startOffset = Math.min(fileEntry.offset, startOffset);
|
|
3859
|
+
if (checkAmbiguity) {
|
|
3860
|
+
if (filenames.has(fileEntry.filename)) {
|
|
3861
|
+
duplicateFilename = true;
|
|
3862
|
+
}
|
|
3863
|
+
filenames.add(fileEntry.filename);
|
|
3864
|
+
}
|
|
3649
3865
|
const unixExternalUpper = (fileEntry.externalFileAttributes >> 16) & MAX_16_BITS;
|
|
3650
3866
|
if (fileEntry.unixMode === UNDEFINED_VALUE && (unixExternalUpper & (FILE_ATTR_UNIX_DEFAULT_MASK | FILE_ATTR_UNIX_EXECUTABLE_MASK | FILE_ATTR_UNIX_TYPE_DIR)) != 0) {
|
|
3651
3867
|
fileEntry.unixMode = unixExternalUpper;
|
|
@@ -3666,7 +3882,7 @@
|
|
|
3666
3882
|
internalFileAttribute: fileEntry.internalFileAttributes,
|
|
3667
3883
|
externalFileAttribute: fileEntry.externalFileAttributes,
|
|
3668
3884
|
executable,
|
|
3669
|
-
directory: modeIsDir || upperIsDir || (msDosCompatible && msdosAttributes.directory) || (filename.endsWith(DIRECTORY_SIGNATURE) && !fileEntry.uncompressedSize),
|
|
3885
|
+
directory: modeIsDir || upperIsDir || (msDosCompatible && msdosAttributes.directory) || (fileEntry.filename.endsWith(DIRECTORY_SIGNATURE) && !fileEntry.uncompressedSize),
|
|
3670
3886
|
zipCrypto: fileEntry.encrypted && !fileEntry.extraFieldAES
|
|
3671
3887
|
});
|
|
3672
3888
|
const entry = new Entry(fileEntry);
|
|
@@ -3689,6 +3905,15 @@
|
|
|
3689
3905
|
}
|
|
3690
3906
|
yield entry;
|
|
3691
3907
|
}
|
|
3908
|
+
if (checkAmbiguity && offset != declaredDirectoryDataLength) {
|
|
3909
|
+
throwAmbiguousArchive("trailing central directory data");
|
|
3910
|
+
}
|
|
3911
|
+
if (duplicateFilename) {
|
|
3912
|
+
throwAmbiguousArchive("duplicate filename");
|
|
3913
|
+
}
|
|
3914
|
+
if (checkAmbiguity && (prependedDataLength || (filesLength && startOffset > 0))) {
|
|
3915
|
+
throwAmbiguousArchive("prepended data");
|
|
3916
|
+
}
|
|
3692
3917
|
const extractPrependedData = getOptionValue$1(zipReader, options, OPTION_EXTRACT_PREPENDED_DATA);
|
|
3693
3918
|
const extractAppendedData = getOptionValue$1(zipReader, options, OPTION_EXTRACT_APPENDED_DATA);
|
|
3694
3919
|
if (extractPrependedData) {
|
|
@@ -3727,6 +3952,7 @@
|
|
|
3727
3952
|
const zipEntry = this;
|
|
3728
3953
|
const {
|
|
3729
3954
|
reader,
|
|
3955
|
+
index,
|
|
3730
3956
|
offset,
|
|
3731
3957
|
diskNumberStart,
|
|
3732
3958
|
extraFieldAES,
|
|
@@ -3752,27 +3978,42 @@
|
|
|
3752
3978
|
rawPassword = rawPassword && rawPassword.length && rawPassword;
|
|
3753
3979
|
if (extraFieldAES) {
|
|
3754
3980
|
if (extraFieldAES.originalCompressionMethod != COMPRESSION_METHOD_AES) {
|
|
3755
|
-
throw new Error(ERR_UNSUPPORTED_COMPRESSION);
|
|
3981
|
+
throw new Error(ERR_UNSUPPORTED_COMPRESSION$1);
|
|
3756
3982
|
}
|
|
3757
3983
|
}
|
|
3758
3984
|
if ((compressionMethod != COMPRESSION_METHOD_STORE && compressionMethod != COMPRESSION_METHOD_DEFLATE && compressionMethod != COMPRESSION_METHOD_DEFLATE_64) && !passThrough) {
|
|
3759
|
-
throw new Error(ERR_UNSUPPORTED_COMPRESSION);
|
|
3985
|
+
throw new Error(ERR_UNSUPPORTED_COMPRESSION$1);
|
|
3760
3986
|
}
|
|
3761
|
-
if (getUint32(dataView, 0) != LOCAL_FILE_HEADER_SIGNATURE) {
|
|
3987
|
+
if (dataArray.length < HEADER_SIZE || getUint32(dataView, 0) != LOCAL_FILE_HEADER_SIGNATURE) {
|
|
3762
3988
|
throw new Error(ERR_LOCAL_FILE_HEADER_NOT_FOUND);
|
|
3763
3989
|
}
|
|
3764
3990
|
readCommonHeader(localDirectory, dataView, 4);
|
|
3765
3991
|
const {
|
|
3766
3992
|
extraFieldLength,
|
|
3767
|
-
filenameLength
|
|
3768
|
-
lastAccessDate,
|
|
3769
|
-
creationDate
|
|
3993
|
+
filenameLength
|
|
3770
3994
|
} = localDirectory;
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3995
|
+
const checkAmbiguity = getOptionValue$1(zipEntry, options, OPTION_CHECK_AMBIGUITY);
|
|
3996
|
+
let rawLocalFilename = new Uint8Array();
|
|
3997
|
+
if (checkAmbiguity && (filenameLength || extraFieldLength)) {
|
|
3998
|
+
const trailingDataArray = await readUint8Array(reader, offset + HEADER_SIZE, filenameLength + extraFieldLength, diskNumberStart);
|
|
3999
|
+
rawLocalFilename = trailingDataArray.subarray(0, filenameLength);
|
|
4000
|
+
localDirectory.rawExtraField = trailingDataArray.subarray(filenameLength);
|
|
4001
|
+
} else {
|
|
4002
|
+
localDirectory.rawExtraField = extraFieldLength ?
|
|
4003
|
+
await readUint8Array(reader, offset + HEADER_SIZE + filenameLength, extraFieldLength, diskNumberStart) :
|
|
4004
|
+
new Uint8Array();
|
|
4005
|
+
}
|
|
3774
4006
|
readCommonFooter(zipEntry, localDirectory, dataView, 4, true);
|
|
3775
|
-
|
|
4007
|
+
if (checkAmbiguity) {
|
|
4008
|
+
checkLocalDirectory(zipEntry, localDirectory, rawLocalFilename);
|
|
4009
|
+
}
|
|
4010
|
+
const { lastAccessDate, creationDate } = localDirectory;
|
|
4011
|
+
if (lastAccessDate) {
|
|
4012
|
+
fileEntry.lastAccessDate = lastAccessDate;
|
|
4013
|
+
}
|
|
4014
|
+
if (creationDate) {
|
|
4015
|
+
fileEntry.creationDate = creationDate;
|
|
4016
|
+
}
|
|
3776
4017
|
const encrypted = zipEntry.encrypted && localDirectory.encrypted && !passThrough;
|
|
3777
4018
|
const zipCrypto = encrypted && !extraFieldAES;
|
|
3778
4019
|
if (!passThrough) {
|
|
@@ -3818,7 +4059,7 @@
|
|
|
3818
4059
|
outputSize: passThrough ? compressedSize : uncompressedSize,
|
|
3819
4060
|
signature,
|
|
3820
4061
|
compressed: compressionMethod != 0 && !passThrough,
|
|
3821
|
-
encrypted
|
|
4062
|
+
encrypted,
|
|
3822
4063
|
useWebWorkers: getOptionValue$1(zipEntry, options, OPTION_USE_WEB_WORKERS),
|
|
3823
4064
|
useCompressionStream,
|
|
3824
4065
|
transferStreams: getOptionValue$1(zipEntry, options, OPTION_TRANSFER_STREAMS),
|
|
@@ -3832,6 +4073,7 @@
|
|
|
3832
4073
|
await detectOverlappingEntry({
|
|
3833
4074
|
reader,
|
|
3834
4075
|
fileEntry,
|
|
4076
|
+
index,
|
|
3835
4077
|
offset,
|
|
3836
4078
|
diskNumberStart,
|
|
3837
4079
|
signature,
|
|
@@ -3934,7 +4176,7 @@
|
|
|
3934
4176
|
directory.extraFieldUnicodeComment = extraFieldUnicodeComment;
|
|
3935
4177
|
}
|
|
3936
4178
|
const extraFieldAES = extraField.get(EXTRAFIELD_TYPE_AES);
|
|
3937
|
-
if (extraFieldAES) {
|
|
4179
|
+
if (extraFieldAES && extraFieldAES.data.length >= 7) {
|
|
3938
4180
|
readExtraFieldAES(extraFieldAES, directory, compressionMethod);
|
|
3939
4181
|
directory.extraFieldAES = extraFieldAES;
|
|
3940
4182
|
} else {
|
|
@@ -3971,19 +4213,23 @@
|
|
|
3971
4213
|
directory.zip64 = true;
|
|
3972
4214
|
const extraFieldView = getDataView$1(extraFieldZip64.data);
|
|
3973
4215
|
const missingProperties = ZIP64_PROPERTIES.filter(([propertyName, max]) => directory[propertyName] == max);
|
|
4216
|
+
const requiredLength = missingProperties.reduce((length, [, max]) => length + ZIP64_EXTRACTION[max].bytes, 0);
|
|
4217
|
+
if (extraFieldZip64.data.length < requiredLength) {
|
|
4218
|
+
throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
|
|
4219
|
+
}
|
|
3974
4220
|
for (let indexMissingProperty = 0, offset = 0; indexMissingProperty < missingProperties.length; indexMissingProperty++) {
|
|
3975
4221
|
const [propertyName, max] = missingProperties[indexMissingProperty];
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
offset += extraction.bytes;
|
|
3980
|
-
} else if (extraFieldZip64[propertyName]) {
|
|
3981
|
-
throw new Error(ERR_EXTRAFIELD_ZIP64_NOT_FOUND);
|
|
3982
|
-
}
|
|
4222
|
+
const extraction = ZIP64_EXTRACTION[max];
|
|
4223
|
+
directory[propertyName] = extraFieldZip64[propertyName] = extraction.getValue(extraFieldView, offset);
|
|
4224
|
+
offset += extraction.bytes;
|
|
3983
4225
|
}
|
|
3984
4226
|
}
|
|
3985
4227
|
|
|
3986
4228
|
function readExtraFieldUnicode(extraFieldUnicode, propertyName, rawPropertyName, directory, fileEntry) {
|
|
4229
|
+
if (extraFieldUnicode.data.length < 5) {
|
|
4230
|
+
extraFieldUnicode.valid = false;
|
|
4231
|
+
return;
|
|
4232
|
+
}
|
|
3987
4233
|
const extraFieldView = getDataView$1(extraFieldUnicode.data);
|
|
3988
4234
|
const crc32 = new Crc32();
|
|
3989
4235
|
crc32.append(fileEntry[rawPropertyName]);
|
|
@@ -4056,32 +4302,30 @@
|
|
|
4056
4302
|
function readExtraFieldUnix(extraField, directory, isInfoZip) {
|
|
4057
4303
|
try {
|
|
4058
4304
|
const view = getDataView$1(new Uint8Array(extraField.data));
|
|
4059
|
-
let
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
if (
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4305
|
+
let uid, gid;
|
|
4306
|
+
if (isInfoZip) {
|
|
4307
|
+
// Info-ZIP New Unix "ux" field (0x7875): version + variable-length uid/gid
|
|
4308
|
+
let offset = 0;
|
|
4309
|
+
const version = getUint8(view, offset++);
|
|
4310
|
+
const uidSize = getUint8(view, offset++);
|
|
4311
|
+
uid = unpackUnixId(extraField.data.subarray(offset, offset + uidSize));
|
|
4312
|
+
offset += uidSize;
|
|
4313
|
+
const gidSize = getUint8(view, offset++);
|
|
4314
|
+
gid = unpackUnixId(extraField.data.subarray(offset, offset + gidSize));
|
|
4315
|
+
Object.assign(extraField, { version, uid, gid });
|
|
4316
|
+
} else if (extraField.data.length >= 4) {
|
|
4317
|
+
// Info-ZIP Unix "Ux" field (0x7855): fixed 2-byte uid + gid (the file mode, if any, is
|
|
4318
|
+
// carried in the external file attributes, not here)
|
|
4319
|
+
uid = getUint16(view, 0);
|
|
4320
|
+
gid = getUint16(view, 2);
|
|
4321
|
+
Object.assign(extraField, { uid, gid });
|
|
4322
|
+
}
|
|
4076
4323
|
if (uid !== UNDEFINED_VALUE) {
|
|
4077
4324
|
directory.uid = uid;
|
|
4078
4325
|
}
|
|
4079
4326
|
if (gid !== UNDEFINED_VALUE) {
|
|
4080
4327
|
directory.gid = gid;
|
|
4081
4328
|
}
|
|
4082
|
-
if (unixMode !== UNDEFINED_VALUE) {
|
|
4083
|
-
directory.unixMode = unixMode;
|
|
4084
|
-
}
|
|
4085
4329
|
} catch {
|
|
4086
4330
|
// ignored
|
|
4087
4331
|
}
|
|
@@ -4095,6 +4339,9 @@
|
|
|
4095
4339
|
}
|
|
4096
4340
|
|
|
4097
4341
|
function readExtraFieldExtendedTimestamp(extraFieldExtendedTimestamp, directory, localDirectory) {
|
|
4342
|
+
if (!extraFieldExtendedTimestamp.data.length) {
|
|
4343
|
+
return;
|
|
4344
|
+
}
|
|
4098
4345
|
const extraFieldView = getDataView$1(extraFieldExtendedTimestamp.data);
|
|
4099
4346
|
const flags = getUint8(extraFieldView, 0);
|
|
4100
4347
|
const timeProperties = [];
|
|
@@ -4120,7 +4367,8 @@
|
|
|
4120
4367
|
timeProperties.forEach((propertyName, indexProperty) => {
|
|
4121
4368
|
if (extraFieldExtendedTimestamp.data.length >= offset + 4) {
|
|
4122
4369
|
const time = getUint32(extraFieldView, offset);
|
|
4123
|
-
|
|
4370
|
+
// the extended timestamp is a signed 32-bit time_t value
|
|
4371
|
+
directory[propertyName] = extraFieldExtendedTimestamp[propertyName] = new Date((time | 0) * 1000);
|
|
4124
4372
|
const rawPropertyName = timeRawProperties[indexProperty];
|
|
4125
4373
|
extraFieldExtendedTimestamp[rawPropertyName] = time;
|
|
4126
4374
|
}
|
|
@@ -4131,6 +4379,7 @@
|
|
|
4131
4379
|
async function detectOverlappingEntry({
|
|
4132
4380
|
reader,
|
|
4133
4381
|
fileEntry,
|
|
4382
|
+
index,
|
|
4134
4383
|
offset,
|
|
4135
4384
|
diskNumberStart,
|
|
4136
4385
|
signature,
|
|
@@ -4142,10 +4391,9 @@
|
|
|
4142
4391
|
readRanges
|
|
4143
4392
|
}) {
|
|
4144
4393
|
let diskOffset = 0;
|
|
4145
|
-
if (diskNumberStart) {
|
|
4146
|
-
for (let indexReader = 0; indexReader < diskNumberStart; indexReader++) {
|
|
4147
|
-
|
|
4148
|
-
diskOffset += diskReader.size;
|
|
4394
|
+
if (diskNumberStart && reader.readers) {
|
|
4395
|
+
for (let indexReader = 0; indexReader < Math.min(diskNumberStart, reader.readers.length); indexReader++) {
|
|
4396
|
+
diskOffset += reader.readers[indexReader].size;
|
|
4149
4397
|
}
|
|
4150
4398
|
}
|
|
4151
4399
|
let dataDescriptorLength = 0;
|
|
@@ -4158,7 +4406,8 @@
|
|
|
4158
4406
|
}
|
|
4159
4407
|
if (dataDescriptorLength) {
|
|
4160
4408
|
const dataDescriptorArray = await readUint8Array(reader, dataOffset + compressedSize, dataDescriptorLength + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH, diskNumberStart);
|
|
4161
|
-
const dataDescriptorSignature =
|
|
4409
|
+
const dataDescriptorSignature = dataDescriptorArray.length == dataDescriptorLength + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH &&
|
|
4410
|
+
getUint32(getDataView$1(dataDescriptorArray), 0) == DATA_DESCRIPTOR_RECORD_SIGNATURE;
|
|
4162
4411
|
if (dataDescriptorSignature) {
|
|
4163
4412
|
const readSignature = getUint32(getDataView$1(dataDescriptorArray), 4);
|
|
4164
4413
|
let readCompressedSize;
|
|
@@ -4183,14 +4432,14 @@
|
|
|
4183
4432
|
end: diskOffset + dataOffset + compressedSize + dataDescriptorLength,
|
|
4184
4433
|
fileEntry
|
|
4185
4434
|
};
|
|
4186
|
-
for (const otherRange of readRanges) {
|
|
4187
|
-
if (
|
|
4435
|
+
for (const [otherIndex, otherRange] of readRanges) {
|
|
4436
|
+
if (otherIndex != index && range.start < otherRange.end && otherRange.start < range.end) {
|
|
4188
4437
|
const error = new Error(ERR_OVERLAPPING_ENTRY);
|
|
4189
4438
|
error.overlappingEntry = otherRange.fileEntry;
|
|
4190
4439
|
throw error;
|
|
4191
4440
|
}
|
|
4192
4441
|
}
|
|
4193
|
-
readRanges.
|
|
4442
|
+
readRanges.set(index, range);
|
|
4194
4443
|
}
|
|
4195
4444
|
|
|
4196
4445
|
async function seekSignature(reader, signature, startOffset, minimumBytes, maximumLength) {
|
|
@@ -4215,6 +4464,35 @@
|
|
|
4215
4464
|
}
|
|
4216
4465
|
}
|
|
4217
4466
|
|
|
4467
|
+
function checkLocalDirectory(zipEntry, localDirectory, rawLocalFilename) {
|
|
4468
|
+
const { rawFilename } = zipEntry;
|
|
4469
|
+
if (rawLocalFilename.length != rawFilename.length ||
|
|
4470
|
+
rawLocalFilename.some((byteValue, indexByte) => byteValue != rawFilename[indexByte])) {
|
|
4471
|
+
throwAmbiguousArchive("mismatched local file header (filename)");
|
|
4472
|
+
}
|
|
4473
|
+
if ((localDirectory.rawBitFlag & BITFLAG_AMBIGUITY_MASK) != (zipEntry.rawBitFlag & BITFLAG_AMBIGUITY_MASK)) {
|
|
4474
|
+
throwAmbiguousArchive("mismatched local file header (general purpose bit flag)");
|
|
4475
|
+
}
|
|
4476
|
+
if (localDirectory.compressionMethod != zipEntry.compressionMethod) {
|
|
4477
|
+
throwAmbiguousArchive("mismatched local file header (compression method)");
|
|
4478
|
+
}
|
|
4479
|
+
// when the data descriptor flag is set, the local signature and sizes are zeroed out; all-zero values are
|
|
4480
|
+
// also tolerated when the flag is clear because some tools produce them (streaming writers predating bit 3)
|
|
4481
|
+
if (!localDirectory.bitFlag.dataDescriptor &&
|
|
4482
|
+
(localDirectory.signature || localDirectory.compressedSize || localDirectory.uncompressedSize) &&
|
|
4483
|
+
(localDirectory.signature != zipEntry.signature ||
|
|
4484
|
+
localDirectory.compressedSize != zipEntry.compressedSize ||
|
|
4485
|
+
localDirectory.uncompressedSize != zipEntry.uncompressedSize)) {
|
|
4486
|
+
throwAmbiguousArchive("mismatched local file header (signature or sizes)");
|
|
4487
|
+
}
|
|
4488
|
+
}
|
|
4489
|
+
|
|
4490
|
+
function throwAmbiguousArchive(reason) {
|
|
4491
|
+
const error = new Error(ERR_AMBIGUOUS_ARCHIVE);
|
|
4492
|
+
error.reason = reason;
|
|
4493
|
+
throw error;
|
|
4494
|
+
}
|
|
4495
|
+
|
|
4218
4496
|
function getOptionValue$1(zipReader, options, name) {
|
|
4219
4497
|
return options[name] === UNDEFINED_VALUE ? zipReader.options[name] : options[name];
|
|
4220
4498
|
}
|
|
@@ -4253,7 +4531,7 @@
|
|
|
4253
4531
|
}
|
|
4254
4532
|
|
|
4255
4533
|
function getDataView$1(array) {
|
|
4256
|
-
return new DataView(array.buffer);
|
|
4534
|
+
return new DataView(array.buffer, array.byteOffset, array.byteLength);
|
|
4257
4535
|
}
|
|
4258
4536
|
|
|
4259
4537
|
/*
|
|
@@ -4293,13 +4571,19 @@
|
|
|
4293
4571
|
const ERR_INVALID_ENCRYPTION_STRENGTH = "The strength must equal 1, 2, or 3";
|
|
4294
4572
|
const ERR_INVALID_EXTRAFIELD_TYPE = "Extra field type exceeds 65535";
|
|
4295
4573
|
const ERR_INVALID_EXTRAFIELD_DATA = "Extra field data exceeds 64KB";
|
|
4574
|
+
// same message as the constant exported by zip-reader.js
|
|
4575
|
+
const ERR_UNSUPPORTED_COMPRESSION = "Compression method not supported";
|
|
4576
|
+
const MIN_UNIX_TIME = -2147483648;
|
|
4577
|
+
const MAX_UNIX_TIME = 2147483647;
|
|
4296
4578
|
const ERR_UNSUPPORTED_FORMAT = "Zip64 is not supported (set the 'zip64' option to 'true')";
|
|
4297
4579
|
const ERR_UNDEFINED_UNCOMPRESSED_SIZE = "Undefined uncompressed size";
|
|
4580
|
+
const ERR_UNDEFINED_READER = "Undefined reader";
|
|
4298
4581
|
const ERR_ZIP_NOT_EMPTY = "Zip file not empty";
|
|
4299
4582
|
const ERR_INVALID_UID = "Invalid uid (must be integer 0..2^32-1)";
|
|
4300
4583
|
const ERR_INVALID_GID = "Invalid gid (must be integer 0..2^32-1)";
|
|
4301
4584
|
const ERR_INVALID_UNIX_MODE = "Invalid UNIX mode (must be integer 0..65535)";
|
|
4302
4585
|
const ERR_INVALID_UNIX_EXTRA_FIELD_TYPE = "Invalid unixExtraFieldType (must be 'infozip' or 'unix')";
|
|
4586
|
+
const ERR_INVALID_UNIX_ID_SIZE = "uid/gid must be 0..65535 for unixExtraFieldType 'unix' (use 'infozip' for larger ids)";
|
|
4303
4587
|
const ERR_INVALID_MSDOS_ATTRIBUTES = "Invalid msdosAttributesRaw (must be integer 0..255)";
|
|
4304
4588
|
const ERR_INVALID_MSDOS_DATA = "Invalid msdosAttributes (must be an object with boolean flags)";
|
|
4305
4589
|
|
|
@@ -4325,9 +4609,10 @@
|
|
|
4325
4609
|
files: new Map(),
|
|
4326
4610
|
filenames: new Set(),
|
|
4327
4611
|
offset: options[OPTION_OFFSET] === UNDEFINED_VALUE ? writer.size || writer.writable.size || 0 : options[OPTION_OFFSET],
|
|
4328
|
-
|
|
4612
|
+
initialOffset: options[OPTION_OFFSET] === UNDEFINED_VALUE ? 0 : options[OPTION_OFFSET] - (writer.size || writer.writable.size || 0),
|
|
4329
4613
|
pendingAddFileCalls: new Set(),
|
|
4330
|
-
bufferedWrites: 0
|
|
4614
|
+
bufferedWrites: 0,
|
|
4615
|
+
lastFileEntry: UNDEFINED_VALUE
|
|
4331
4616
|
});
|
|
4332
4617
|
}
|
|
4333
4618
|
|
|
@@ -4336,17 +4621,18 @@
|
|
|
4336
4621
|
throw new Error(ERR_ZIP_NOT_EMPTY);
|
|
4337
4622
|
}
|
|
4338
4623
|
reader = new GenericReader(reader);
|
|
4624
|
+
await initStream(reader);
|
|
4339
4625
|
const zipReader = new ZipReader(reader.readable);
|
|
4340
4626
|
const entries = await zipReader.getEntries();
|
|
4341
4627
|
await zipReader.close();
|
|
4628
|
+
await initStream(this.writer);
|
|
4342
4629
|
await reader.readable.pipeTo(this.writer.writable, { preventClose: true, preventAbort: true });
|
|
4343
4630
|
this.writer.size = this.offset = reader.size;
|
|
4344
4631
|
this.filenames = new Set(entries.map(entry => entry.filename));
|
|
4345
4632
|
this.files = new Map(entries.map(entry => {
|
|
4346
4633
|
const {
|
|
4347
4634
|
version,
|
|
4348
|
-
|
|
4349
|
-
lastModDate,
|
|
4635
|
+
rawLastModDate,
|
|
4350
4636
|
lastAccessDate,
|
|
4351
4637
|
creationDate,
|
|
4352
4638
|
rawFilename,
|
|
@@ -4359,6 +4645,7 @@
|
|
|
4359
4645
|
zip64
|
|
4360
4646
|
} = entry;
|
|
4361
4647
|
let {
|
|
4648
|
+
compressionMethod,
|
|
4362
4649
|
rawExtraFieldZip64,
|
|
4363
4650
|
rawExtraFieldAES,
|
|
4364
4651
|
rawExtraFieldExtendedTimestamp,
|
|
@@ -4371,31 +4658,39 @@
|
|
|
4371
4658
|
rawExtraFieldAES = rawExtraFieldAES || new Uint8Array();
|
|
4372
4659
|
rawExtraFieldExtendedTimestamp = rawExtraFieldExtendedTimestamp || new Uint8Array();
|
|
4373
4660
|
rawExtraFieldNTFS = rawExtraFieldNTFS || new Uint8Array();
|
|
4374
|
-
rawExtraFieldUnix =
|
|
4661
|
+
rawExtraFieldUnix = rawExtraFieldUnix || new Uint8Array();
|
|
4375
4662
|
rawExtraField = rawExtraField || new Uint8Array();
|
|
4663
|
+
if (entry.extraFieldAES) {
|
|
4664
|
+
compressionMethod = COMPRESSION_METHOD_AES;
|
|
4665
|
+
}
|
|
4376
4666
|
const extraFieldLength = getLength(rawExtraFieldZip64, rawExtraFieldAES, rawExtraFieldExtendedTimestamp, rawExtraFieldNTFS, rawExtraFieldUnix, rawExtraField);
|
|
4377
|
-
const zip64UncompressedSize = zip64 && uncompressedSize
|
|
4378
|
-
const zip64CompressedSize = zip64 && compressedSize
|
|
4667
|
+
const zip64UncompressedSize = zip64 && uncompressedSize >= MAX_32_BITS;
|
|
4668
|
+
const zip64CompressedSize = zip64 && compressedSize >= MAX_32_BITS;
|
|
4669
|
+
const bitFlagValue = (getBitFlag(level, languageEncodingFlag, dataDescriptor, encrypted, compressionMethod) & ~BITFLAG_LEVEL) | (level << 1);
|
|
4379
4670
|
const {
|
|
4380
4671
|
headerArray,
|
|
4381
4672
|
headerView
|
|
4382
4673
|
} = getHeaderArrayData({
|
|
4383
4674
|
version,
|
|
4384
|
-
bitFlag:
|
|
4675
|
+
bitFlag: bitFlagValue,
|
|
4385
4676
|
compressionMethod,
|
|
4386
4677
|
uncompressedSize,
|
|
4387
4678
|
compressedSize,
|
|
4388
|
-
|
|
4679
|
+
rawLastModDate,
|
|
4389
4680
|
rawFilename,
|
|
4390
4681
|
zip64CompressedSize,
|
|
4391
4682
|
zip64UncompressedSize,
|
|
4392
4683
|
extraFieldLength
|
|
4393
4684
|
});
|
|
4685
|
+
const { signature } = entry;
|
|
4686
|
+
if (signature !== UNDEFINED_VALUE) {
|
|
4687
|
+
setUint32(headerView, HEADER_OFFSET_SIGNATURE, signature);
|
|
4688
|
+
}
|
|
4394
4689
|
Object.assign(entry, {
|
|
4395
4690
|
zip64UncompressedSize,
|
|
4396
4691
|
zip64CompressedSize,
|
|
4397
|
-
zip64Offset: zip64 && this.offset - diskOffset
|
|
4398
|
-
zip64DiskNumberStart: zip64 && diskNumber
|
|
4692
|
+
zip64Offset: zip64 && this.offset - diskOffset >= MAX_32_BITS,
|
|
4693
|
+
zip64DiskNumberStart: zip64 && diskNumber >= MAX_16_BITS,
|
|
4399
4694
|
rawExtraFieldZip64,
|
|
4400
4695
|
rawExtraFieldAES,
|
|
4401
4696
|
rawExtraFieldExtendedTimestamp,
|
|
@@ -4413,6 +4708,7 @@
|
|
|
4413
4708
|
|
|
4414
4709
|
async add(name = "", reader, options = {}) {
|
|
4415
4710
|
const zipWriter = this;
|
|
4711
|
+
options = Object.assign({}, options);
|
|
4416
4712
|
const {
|
|
4417
4713
|
pendingAddFileCalls,
|
|
4418
4714
|
config
|
|
@@ -4423,17 +4719,24 @@
|
|
|
4423
4719
|
await new Promise(resolve => pendingEntries.push(resolve));
|
|
4424
4720
|
}
|
|
4425
4721
|
let promiseAddFile;
|
|
4722
|
+
let nameAdded;
|
|
4426
4723
|
try {
|
|
4427
4724
|
name = name.trim();
|
|
4725
|
+
if (getOptionValue(zipWriter, options, PROPERTY_NAME_DIRECTORY) && !name.endsWith(DIRECTORY_SIGNATURE)) {
|
|
4726
|
+
name += DIRECTORY_SIGNATURE;
|
|
4727
|
+
}
|
|
4428
4728
|
if (zipWriter.filenames.has(name)) {
|
|
4429
4729
|
throw new Error(ERR_DUPLICATED_NAME);
|
|
4430
4730
|
}
|
|
4431
4731
|
zipWriter.filenames.add(name);
|
|
4732
|
+
nameAdded = true;
|
|
4432
4733
|
promiseAddFile = addFile(zipWriter, name, reader, options);
|
|
4433
4734
|
pendingAddFileCalls.add(promiseAddFile);
|
|
4434
4735
|
return await promiseAddFile;
|
|
4435
4736
|
} catch (error) {
|
|
4436
|
-
|
|
4737
|
+
if (nameAdded) {
|
|
4738
|
+
zipWriter.filenames.delete(name);
|
|
4739
|
+
}
|
|
4437
4740
|
throw error;
|
|
4438
4741
|
} finally {
|
|
4439
4742
|
pendingAddFileCalls.delete(promiseAddFile);
|
|
@@ -4466,6 +4769,9 @@
|
|
|
4466
4769
|
const zipWriter = this;
|
|
4467
4770
|
const { pendingAddFileCalls, writer } = this;
|
|
4468
4771
|
const { writable } = writer;
|
|
4772
|
+
if (getLength(comment) > MAX_16_BITS) {
|
|
4773
|
+
throw new Error(ERR_INVALID_COMMENT);
|
|
4774
|
+
}
|
|
4469
4775
|
while (pendingAddFileCalls.size) {
|
|
4470
4776
|
await Promise.allSettled(Array.from(pendingAddFileCalls));
|
|
4471
4777
|
}
|
|
@@ -4479,6 +4785,37 @@
|
|
|
4479
4785
|
}
|
|
4480
4786
|
|
|
4481
4787
|
async function addFile(zipWriter, name, reader, options) {
|
|
4788
|
+
const attributesInfo = resolveAttributes(zipWriter, name, options);
|
|
4789
|
+
({ name } = attributesInfo);
|
|
4790
|
+
const metadataInfo = resolveMetadata(zipWriter, name, options);
|
|
4791
|
+
const { comment } = metadataInfo;
|
|
4792
|
+
const extraField = options[PROPERTY_NAME_EXTRA_FIELD];
|
|
4793
|
+
zipWriter.files.set(name, UNDEFINED_VALUE);
|
|
4794
|
+
let fileEntry;
|
|
4795
|
+
try {
|
|
4796
|
+
const sizesInfo = await resolveSizes(zipWriter, reader, metadataInfo, options);
|
|
4797
|
+
({ reader } = sizesInfo);
|
|
4798
|
+
const { diskOffset, diskNumber } = zipWriter.writer;
|
|
4799
|
+
options = Object.assign({}, options, attributesInfo.resolvedOptions, metadataInfo.resolvedOptions, sizesInfo.resolvedOptions, {
|
|
4800
|
+
internalFileAttribute: metadataInfo.resolvedOptions.internalFileAttributes,
|
|
4801
|
+
externalFileAttribute: attributesInfo.resolvedOptions.externalFileAttributes,
|
|
4802
|
+
signature: options[PROPERTY_NAME_SIGNATURE],
|
|
4803
|
+
offset: zipWriter.offset - diskOffset,
|
|
4804
|
+
diskNumberStart: diskNumber
|
|
4805
|
+
});
|
|
4806
|
+
const headerInfo = getHeaderInfo(options);
|
|
4807
|
+
const dataDescriptorInfo = getDataDescriptorInfo(options);
|
|
4808
|
+
const metadataSize = getLength(headerInfo.localHeaderArray, dataDescriptorInfo.dataDescriptorArray);
|
|
4809
|
+
fileEntry = await getFileEntry(zipWriter, name, reader, { headerInfo, dataDescriptorInfo, metadataSize }, options);
|
|
4810
|
+
} catch (error) {
|
|
4811
|
+
zipWriter.files.delete(name);
|
|
4812
|
+
throw error;
|
|
4813
|
+
}
|
|
4814
|
+
Object.assign(fileEntry, { name, comment, extraField });
|
|
4815
|
+
return new Entry(fileEntry);
|
|
4816
|
+
}
|
|
4817
|
+
|
|
4818
|
+
function resolveAttributes(zipWriter, name, options) {
|
|
4482
4819
|
name = name.trim();
|
|
4483
4820
|
let msDosCompatible = getOptionValue(zipWriter, options, PROPERTY_NAME_MS_DOS_COMPATIBLE);
|
|
4484
4821
|
let versionMadeBy = getOptionValue(zipWriter, options, PROPERTY_NAME_VERSION_MADE_BY, msDosCompatible ? 20 : 768);
|
|
@@ -4486,7 +4823,7 @@
|
|
|
4486
4823
|
const uid = getOptionValue(zipWriter, options, PROPERTY_NAME_UID);
|
|
4487
4824
|
const gid = getOptionValue(zipWriter, options, PROPERTY_NAME_GID);
|
|
4488
4825
|
let unixMode = getOptionValue(zipWriter, options, PROPERTY_NAME_UNIX_MODE);
|
|
4489
|
-
|
|
4826
|
+
let unixExtraFieldType = getOptionValue(zipWriter, options, OPTION_UNIX_EXTRA_FIELD_TYPE);
|
|
4490
4827
|
let setuid = getOptionValue(zipWriter, options, PROPERTY_NAME_SETUID);
|
|
4491
4828
|
let setgid = getOptionValue(zipWriter, options, PROPERTY_NAME_SETGID);
|
|
4492
4829
|
let sticky = getOptionValue(zipWriter, options, PROPERTY_NAME_STICKY);
|
|
@@ -4502,6 +4839,13 @@
|
|
|
4502
4839
|
if (unixExtraFieldType !== UNDEFINED_VALUE && unixExtraFieldType !== INFOZIP_EXTRA_FIELD_TYPE && unixExtraFieldType !== UNIX_EXTRA_FIELD_TYPE) {
|
|
4503
4840
|
throw new Error(ERR_INVALID_UNIX_EXTRA_FIELD_TYPE);
|
|
4504
4841
|
}
|
|
4842
|
+
if (unixExtraFieldType === UNIX_EXTRA_FIELD_TYPE &&
|
|
4843
|
+
((uid !== UNDEFINED_VALUE && uid > MAX_16_BITS) || (gid !== UNDEFINED_VALUE && gid > MAX_16_BITS))) {
|
|
4844
|
+
throw new Error(ERR_INVALID_UNIX_ID_SIZE);
|
|
4845
|
+
}
|
|
4846
|
+
if (unixExtraFieldType === UNDEFINED_VALUE && (uid !== UNDEFINED_VALUE || gid !== UNDEFINED_VALUE)) {
|
|
4847
|
+
unixExtraFieldType = INFOZIP_EXTRA_FIELD_TYPE;
|
|
4848
|
+
}
|
|
4505
4849
|
let msdosAttributesRaw = getOptionValue(zipWriter, options, PROPERTY_NAME_MSDOS_ATTRIBUTES_RAW);
|
|
4506
4850
|
let msdosAttributes = getOptionValue(zipWriter, options, PROPERTY_NAME_MSDOS_ATTRIBUTES);
|
|
4507
4851
|
const hasUnixMetadata = uid !== UNDEFINED_VALUE || gid !== UNDEFINED_VALUE || unixMode !== UNDEFINED_VALUE || unixExtraFieldType;
|
|
@@ -4572,6 +4916,27 @@
|
|
|
4572
4916
|
if (hasMsDosProvided) {
|
|
4573
4917
|
externalFileAttributes = (externalFileAttributes & MAX_32_BITS) | (msdosAttributesRaw & MAX_8_BITS);
|
|
4574
4918
|
}
|
|
4919
|
+
return {
|
|
4920
|
+
name,
|
|
4921
|
+
resolvedOptions: {
|
|
4922
|
+
versionMadeBy,
|
|
4923
|
+
msDosCompatible,
|
|
4924
|
+
externalFileAttributes,
|
|
4925
|
+
unixExternalUpper,
|
|
4926
|
+
uid,
|
|
4927
|
+
gid,
|
|
4928
|
+
unixMode,
|
|
4929
|
+
unixExtraFieldType,
|
|
4930
|
+
setuid,
|
|
4931
|
+
setgid,
|
|
4932
|
+
sticky,
|
|
4933
|
+
msdosAttributesRaw,
|
|
4934
|
+
msdosAttributes
|
|
4935
|
+
}
|
|
4936
|
+
};
|
|
4937
|
+
}
|
|
4938
|
+
|
|
4939
|
+
function resolveMetadata(zipWriter, name, options) {
|
|
4575
4940
|
const encode = getOptionValue(zipWriter, options, OPTION_ENCODE_TEXT, encodeText);
|
|
4576
4941
|
let rawFilename = encode(name);
|
|
4577
4942
|
if (rawFilename === UNDEFINED_VALUE) {
|
|
@@ -4610,10 +4975,14 @@
|
|
|
4610
4975
|
const transferStreams = getOptionValue(zipWriter, options, OPTION_TRANSFER_STREAMS, true);
|
|
4611
4976
|
const bufferedWrite = getOptionValue(zipWriter, options, OPTION_BUFFERED_WRITE);
|
|
4612
4977
|
const createTempStream = getOptionValue(zipWriter, options, OPTION_CREATE_TEMP_STREAM);
|
|
4613
|
-
const dataDescriptorSignature = getOptionValue(zipWriter, options, OPTION_DATA_DESCRIPTOR_SIGNATURE,
|
|
4978
|
+
const dataDescriptorSignature = getOptionValue(zipWriter, options, OPTION_DATA_DESCRIPTOR_SIGNATURE, true);
|
|
4614
4979
|
const signal = getOptionValue(zipWriter, options, OPTION_SIGNAL);
|
|
4615
4980
|
const useUnicodeFileNames = getOptionValue(zipWriter, options, OPTION_USE_UNICODE_FILE_NAMES, true);
|
|
4616
4981
|
const compressionMethod = getOptionValue(zipWriter, options, PROPERTY_NAME_COMPRESSION_METHOD);
|
|
4982
|
+
if (!passThrough && compressionMethod !== UNDEFINED_VALUE &&
|
|
4983
|
+
compressionMethod !== COMPRESSION_METHOD_STORE && compressionMethod !== COMPRESSION_METHOD_DEFLATE) {
|
|
4984
|
+
throw new Error(ERR_UNSUPPORTED_COMPRESSION);
|
|
4985
|
+
}
|
|
4617
4986
|
let level = getOptionValue(zipWriter, options, OPTION_LEVEL);
|
|
4618
4987
|
let useCompressionStream = getOptionValue(zipWriter, options, OPTION_USE_COMPRESSION_STREAM);
|
|
4619
4988
|
let dataDescriptor = getOptionValue(zipWriter, options, OPTION_DATA_DESCRIPTOR);
|
|
@@ -4629,7 +4998,7 @@
|
|
|
4629
4998
|
if (!useCompressionStream && (zipWriter.config.CompressionStream === UNDEFINED_VALUE && zipWriter.config.CompressionStreamZlib === UNDEFINED_VALUE)) {
|
|
4630
4999
|
level = 0;
|
|
4631
5000
|
}
|
|
4632
|
-
|
|
5001
|
+
const zip64 = getOptionValue(zipWriter, options, PROPERTY_NAME_ZIP64);
|
|
4633
5002
|
if (!zipCrypto && (password !== UNDEFINED_VALUE || rawPassword !== UNDEFINED_VALUE) && !(encryptionStrength >= 1 && encryptionStrength <= 3)) {
|
|
4634
5003
|
throw new Error(ERR_INVALID_ENCRYPTION_STRENGTH);
|
|
4635
5004
|
}
|
|
@@ -4640,6 +5009,7 @@
|
|
|
4640
5009
|
let offset = 0;
|
|
4641
5010
|
extraField.forEach(data => extraFieldSize += 4 + getLength(data));
|
|
4642
5011
|
rawExtraField = new Uint8Array(extraFieldSize);
|
|
5012
|
+
const rawExtraFieldView = getDataView(rawExtraField);
|
|
4643
5013
|
extraField.forEach((data, type) => {
|
|
4644
5014
|
if (type > MAX_16_BITS) {
|
|
4645
5015
|
throw new Error(ERR_INVALID_EXTRAFIELD_TYPE);
|
|
@@ -4647,22 +5017,72 @@
|
|
|
4647
5017
|
if (getLength(data) > MAX_16_BITS) {
|
|
4648
5018
|
throw new Error(ERR_INVALID_EXTRAFIELD_DATA);
|
|
4649
5019
|
}
|
|
4650
|
-
|
|
4651
|
-
|
|
5020
|
+
setUint16(rawExtraFieldView, offset, type);
|
|
5021
|
+
setUint16(rawExtraFieldView, offset + 2, getLength(data));
|
|
4652
5022
|
arraySet(rawExtraField, data, offset + 4);
|
|
4653
5023
|
offset += 4 + getLength(data);
|
|
4654
5024
|
});
|
|
4655
5025
|
}
|
|
5026
|
+
return {
|
|
5027
|
+
comment,
|
|
5028
|
+
resolvedOptions: {
|
|
5029
|
+
rawFilename,
|
|
5030
|
+
rawComment,
|
|
5031
|
+
version,
|
|
5032
|
+
lastModDate,
|
|
5033
|
+
lastAccessDate,
|
|
5034
|
+
creationDate,
|
|
5035
|
+
internalFileAttributes,
|
|
5036
|
+
passThrough,
|
|
5037
|
+
password,
|
|
5038
|
+
rawPassword,
|
|
5039
|
+
encryptionStrength,
|
|
5040
|
+
zipCrypto,
|
|
5041
|
+
extendedTimestamp,
|
|
5042
|
+
keepOrder,
|
|
5043
|
+
useWebWorkers,
|
|
5044
|
+
transferStreams,
|
|
5045
|
+
bufferedWrite,
|
|
5046
|
+
createTempStream,
|
|
5047
|
+
dataDescriptorSignature,
|
|
5048
|
+
signal,
|
|
5049
|
+
useUnicodeFileNames,
|
|
5050
|
+
compressionMethod,
|
|
5051
|
+
level,
|
|
5052
|
+
useCompressionStream,
|
|
5053
|
+
dataDescriptor,
|
|
5054
|
+
zip64,
|
|
5055
|
+
rawExtraField
|
|
5056
|
+
}
|
|
5057
|
+
};
|
|
5058
|
+
}
|
|
5059
|
+
|
|
5060
|
+
async function resolveSizes(zipWriter, reader, { resolvedOptions: metadata }, options) {
|
|
5061
|
+
const { passThrough, zipCrypto, password, rawPassword, encryptionStrength } = metadata;
|
|
5062
|
+
let { dataDescriptor, zip64, level, compressionMethod } = metadata;
|
|
4656
5063
|
let maximumCompressedSize = 0;
|
|
4657
|
-
let maximumEntrySize = 0;
|
|
4658
5064
|
let uncompressedSize = 0;
|
|
4659
5065
|
if (passThrough) {
|
|
5066
|
+
// the headers of a passThrough entry describe the payload verbatim: without a
|
|
5067
|
+
// reader they would declare content that is not there
|
|
5068
|
+
if (!reader) {
|
|
5069
|
+
throw new Error(ERR_UNDEFINED_READER);
|
|
5070
|
+
}
|
|
4660
5071
|
uncompressedSize = options[PROPERTY_NAME_UNCOMPRESSED_SIZE];
|
|
4661
5072
|
if (uncompressedSize === UNDEFINED_VALUE) {
|
|
4662
5073
|
throw new Error(ERR_UNDEFINED_UNCOMPRESSED_SIZE);
|
|
4663
5074
|
}
|
|
4664
5075
|
}
|
|
4665
5076
|
const zip64Enabled = zip64 === true;
|
|
5077
|
+
const encrypted = getOptionValue(zipWriter, options, PROPERTY_NAME_ENCRYPTED);
|
|
5078
|
+
// entries without a reader have no payload: write them as empty stored files instead of
|
|
5079
|
+
// declaring a compressed or encrypted content that does not exist
|
|
5080
|
+
const encryptedEntry = Boolean(reader) && (Boolean((password && getLength(password)) || (rawPassword && getLength(rawPassword))) || (passThrough && encrypted));
|
|
5081
|
+
if (!reader) {
|
|
5082
|
+
level = 0;
|
|
5083
|
+
compressionMethod = COMPRESSION_METHOD_STORE;
|
|
5084
|
+
}
|
|
5085
|
+
const encryptionOverhead = encryptedEntry ? (zipCrypto ? 12 : 16 + encryptionStrength * 4) : 0;
|
|
4666
5086
|
if (reader) {
|
|
4667
5087
|
reader = new GenericReader(reader);
|
|
4668
5088
|
await initStream(reader);
|
|
@@ -4675,16 +5095,15 @@
|
|
|
4675
5095
|
}
|
|
4676
5096
|
} else {
|
|
4677
5097
|
options.uncompressedSize = uncompressedSize = reader.size;
|
|
4678
|
-
maximumCompressedSize = getMaximumCompressedSize(uncompressedSize);
|
|
5098
|
+
maximumCompressedSize = getMaximumCompressedSize(uncompressedSize) + encryptionOverhead;
|
|
4679
5099
|
}
|
|
4680
5100
|
} else {
|
|
4681
5101
|
options.uncompressedSize = uncompressedSize;
|
|
4682
|
-
maximumCompressedSize = getMaximumCompressedSize(uncompressedSize);
|
|
5102
|
+
maximumCompressedSize = getMaximumCompressedSize(uncompressedSize) + encryptionOverhead;
|
|
4683
5103
|
}
|
|
4684
5104
|
}
|
|
4685
|
-
const
|
|
4686
|
-
const
|
|
4687
|
-
const zip64CompressedSize = zip64Enabled || maximumCompressedSize > MAX_32_BITS;
|
|
5105
|
+
const zip64UncompressedSize = zip64Enabled || uncompressedSize >= MAX_32_BITS;
|
|
5106
|
+
const zip64CompressedSize = zip64Enabled || maximumCompressedSize >= MAX_32_BITS;
|
|
4688
5107
|
if (zip64UncompressedSize || zip64CompressedSize) {
|
|
4689
5108
|
if (zip64 === false) {
|
|
4690
5109
|
throw new Error(ERR_UNSUPPORTED_FORMAT);
|
|
@@ -4693,73 +5112,19 @@
|
|
|
4693
5112
|
}
|
|
4694
5113
|
}
|
|
4695
5114
|
zip64 = zip64 || false;
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
password,
|
|
4710
|
-
rawPassword,
|
|
4711
|
-
level,
|
|
4712
|
-
useWebWorkers,
|
|
4713
|
-
transferStreams,
|
|
4714
|
-
encryptionStrength,
|
|
4715
|
-
extendedTimestamp,
|
|
4716
|
-
zipCrypto,
|
|
4717
|
-
bufferedWrite,
|
|
4718
|
-
createTempStream,
|
|
4719
|
-
keepOrder,
|
|
4720
|
-
useUnicodeFileNames,
|
|
4721
|
-
dataDescriptor,
|
|
4722
|
-
dataDescriptorSignature,
|
|
4723
|
-
signal,
|
|
4724
|
-
msDosCompatible,
|
|
4725
|
-
internalFileAttribute: internalFileAttributes,
|
|
4726
|
-
internalFileAttributes,
|
|
4727
|
-
externalFileAttribute: externalFileAttributes,
|
|
4728
|
-
externalFileAttributes,
|
|
4729
|
-
useCompressionStream,
|
|
4730
|
-
passThrough,
|
|
4731
|
-
encrypted: Boolean((password && getLength(password)) || (rawPassword && getLength(rawPassword))) || (passThrough && encrypted),
|
|
4732
|
-
signature: options[PROPERTY_NAME_SIGNATURE],
|
|
4733
|
-
compressionMethod,
|
|
4734
|
-
uncompressedSize,
|
|
4735
|
-
offset: zipWriter.offset - diskOffset,
|
|
4736
|
-
diskNumberStart: diskNumber,
|
|
4737
|
-
uid,
|
|
4738
|
-
gid,
|
|
4739
|
-
setuid,
|
|
4740
|
-
setgid,
|
|
4741
|
-
sticky,
|
|
4742
|
-
unixMode,
|
|
4743
|
-
msdosAttributesRaw,
|
|
4744
|
-
msdosAttributes,
|
|
4745
|
-
unixExternalUpper
|
|
4746
|
-
});
|
|
4747
|
-
const headerInfo = getHeaderInfo(options);
|
|
4748
|
-
const dataDescriptorInfo = getDataDescriptorInfo(options);
|
|
4749
|
-
const metadataSize = getLength(headerInfo.localHeaderArray, dataDescriptorInfo.dataDescriptorArray);
|
|
4750
|
-
maximumEntrySize = metadataSize + maximumCompressedSize;
|
|
4751
|
-
if (zipWriter.options[OPTION_USDZ]) {
|
|
4752
|
-
maximumEntrySize += maximumEntrySize + 64;
|
|
4753
|
-
}
|
|
4754
|
-
zipWriter.pendingEntriesSize += maximumEntrySize;
|
|
4755
|
-
let fileEntry;
|
|
4756
|
-
try {
|
|
4757
|
-
fileEntry = await getFileEntry(zipWriter, name, reader, { headerInfo, dataDescriptorInfo, metadataSize }, options);
|
|
4758
|
-
} finally {
|
|
4759
|
-
zipWriter.pendingEntriesSize -= maximumEntrySize;
|
|
4760
|
-
}
|
|
4761
|
-
Object.assign(fileEntry, { name, comment, extraField });
|
|
4762
|
-
return new Entry(fileEntry);
|
|
5115
|
+
return {
|
|
5116
|
+
reader,
|
|
5117
|
+
resolvedOptions: {
|
|
5118
|
+
dataDescriptor,
|
|
5119
|
+
zip64,
|
|
5120
|
+
zip64UncompressedSize,
|
|
5121
|
+
zip64CompressedSize,
|
|
5122
|
+
uncompressedSize,
|
|
5123
|
+
level,
|
|
5124
|
+
compressionMethod,
|
|
5125
|
+
encrypted: encryptedEntry
|
|
5126
|
+
}
|
|
5127
|
+
};
|
|
4763
5128
|
}
|
|
4764
5129
|
|
|
4765
5130
|
async function getFileEntry(zipWriter, name, reader, entryInfo, options) {
|
|
@@ -4776,37 +5141,40 @@
|
|
|
4776
5141
|
headerInfo
|
|
4777
5142
|
} = entryInfo;
|
|
4778
5143
|
const usdz = zipWriter.options[OPTION_USDZ];
|
|
4779
|
-
const previousFileEntry =
|
|
5144
|
+
const previousFileEntry = zipWriter.lastFileEntry;
|
|
4780
5145
|
let fileEntry = {};
|
|
4781
5146
|
let bufferedWrite;
|
|
4782
5147
|
let releaseLockWriter;
|
|
4783
5148
|
let releaseLockCurrentFileEntry;
|
|
4784
5149
|
let writingBufferedEntryData;
|
|
4785
5150
|
let writingEntryData;
|
|
5151
|
+
let writerSizeBeforeEntry;
|
|
5152
|
+
let flushedBufferedSize = 0;
|
|
4786
5153
|
let fileWriter;
|
|
4787
5154
|
files.set(name, fileEntry);
|
|
5155
|
+
zipWriter.lastFileEntry = fileEntry;
|
|
4788
5156
|
try {
|
|
4789
5157
|
let lockPreviousFileEntry;
|
|
4790
5158
|
if (keepOrder) {
|
|
4791
5159
|
lockPreviousFileEntry = previousFileEntry && previousFileEntry.lock;
|
|
4792
5160
|
requestLockCurrentFileEntry();
|
|
4793
5161
|
}
|
|
4794
|
-
if (
|
|
5162
|
+
if (options.bufferedWrite || !keepOrder || zipWriter.writerLocked || zipWriter.bufferedWrites || !dataDescriptor) {
|
|
5163
|
+
bufferedWrite = true;
|
|
5164
|
+
zipWriter.bufferedWrites++;
|
|
4795
5165
|
if (options.createTempStream) {
|
|
4796
5166
|
fileWriter = await options.createTempStream();
|
|
4797
5167
|
} else {
|
|
4798
5168
|
fileWriter = new TransformStream(UNDEFINED_VALUE, UNDEFINED_VALUE, { highWaterMark: INFINITY_VALUE });
|
|
4799
5169
|
}
|
|
4800
5170
|
fileWriter.size = 0;
|
|
4801
|
-
bufferedWrite = true;
|
|
4802
|
-
zipWriter.bufferedWrites++;
|
|
4803
5171
|
await initStream(writer);
|
|
4804
5172
|
} else {
|
|
4805
5173
|
fileWriter = writer;
|
|
4806
5174
|
await requestLockWriter();
|
|
4807
5175
|
}
|
|
4808
5176
|
await initStream(fileWriter);
|
|
4809
|
-
const {
|
|
5177
|
+
const { diskOffset } = writer;
|
|
4810
5178
|
if (zipWriter.addSplitZipSignature) {
|
|
4811
5179
|
delete zipWriter.addSplitZipSignature;
|
|
4812
5180
|
const signatureArray = new Uint8Array(4);
|
|
@@ -4815,21 +5183,20 @@
|
|
|
4815
5183
|
await writeData(writer, signatureArray);
|
|
4816
5184
|
zipWriter.offset += 4;
|
|
4817
5185
|
}
|
|
4818
|
-
if (usdz) {
|
|
5186
|
+
if (usdz && !bufferedWrite) {
|
|
4819
5187
|
appendExtraFieldUSDZ(entryInfo, zipWriter.offset - diskOffset);
|
|
4820
5188
|
}
|
|
4821
|
-
const {
|
|
4822
|
-
localHeaderView,
|
|
4823
|
-
localHeaderArray
|
|
4824
|
-
} = headerInfo;
|
|
5189
|
+
const { localHeaderArray } = headerInfo;
|
|
4825
5190
|
if (!bufferedWrite) {
|
|
4826
5191
|
await lockPreviousFileEntry;
|
|
4827
|
-
await skipDiskIfNeeded(
|
|
5192
|
+
await skipDiskIfNeeded();
|
|
4828
5193
|
}
|
|
4829
|
-
const
|
|
4830
|
-
|
|
5194
|
+
const diskNumberStart = writer.diskNumber;
|
|
5195
|
+
const entryOffset = getSegmentOffset(zipWriter, writer);
|
|
5196
|
+
fileEntry.diskNumberStart = diskNumberStart;
|
|
4831
5197
|
if (!bufferedWrite) {
|
|
4832
5198
|
writingEntryData = true;
|
|
5199
|
+
writerSizeBeforeEntry = writer.size;
|
|
4833
5200
|
await writeData(fileWriter, localHeaderArray);
|
|
4834
5201
|
}
|
|
4835
5202
|
fileEntry = await createFileEntry(reader, fileWriter, fileEntry, entryInfo, zipWriter.config, options);
|
|
@@ -4842,16 +5209,24 @@
|
|
|
4842
5209
|
await Promise.all([fileWriter.writable.getWriter().close(), lockPreviousFileEntry]);
|
|
4843
5210
|
await requestLockWriter();
|
|
4844
5211
|
writingBufferedEntryData = true;
|
|
5212
|
+
writerSizeBeforeEntry = writer.size;
|
|
5213
|
+
await skipDiskIfNeeded();
|
|
4845
5214
|
fileEntry.diskNumberStart = writer.diskNumber;
|
|
4846
|
-
fileEntry.offset = zipWriter
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
|
|
5215
|
+
fileEntry.offset = getSegmentOffset(zipWriter, writer);
|
|
5216
|
+
if (usdz) {
|
|
5217
|
+
// the padding depends on the entry offset, only known now that the writer is locked
|
|
5218
|
+
const previousMetadataSize = entryInfo.metadataSize;
|
|
5219
|
+
appendExtraFieldUSDZ(entryInfo, zipWriter.offset - writer.diskOffset);
|
|
5220
|
+
fileEntry.size += entryInfo.metadataSize - previousMetadataSize;
|
|
5221
|
+
}
|
|
5222
|
+
updateLocalHeader(fileEntry, headerInfo.localHeaderView, options);
|
|
5223
|
+
await writeData(writer, headerInfo.localHeaderArray);
|
|
5224
|
+
await flushBufferedData(fileWriter.readable, writer, signal, chunkLength => flushedBufferedSize += chunkLength);
|
|
4851
5225
|
writer.size += fileWriter.size;
|
|
4852
5226
|
writingBufferedEntryData = false;
|
|
4853
5227
|
} else {
|
|
4854
|
-
fileEntry.
|
|
5228
|
+
fileEntry.diskNumberStart = diskNumberStart;
|
|
5229
|
+
fileEntry.offset = entryOffset;
|
|
4855
5230
|
}
|
|
4856
5231
|
zipWriter.offset += fileEntry.size;
|
|
4857
5232
|
return fileEntry;
|
|
@@ -4865,10 +5240,9 @@
|
|
|
4865
5240
|
// ignored
|
|
4866
5241
|
}
|
|
4867
5242
|
}
|
|
5243
|
+
zipWriter.offset += writer.size - writerSizeBeforeEntry;
|
|
4868
5244
|
if (bufferedWrite) {
|
|
4869
|
-
zipWriter.offset +=
|
|
4870
|
-
} else {
|
|
4871
|
-
zipWriter.offset = fileWriter.size;
|
|
5245
|
+
zipWriter.offset += flushedBufferedSize;
|
|
4872
5246
|
}
|
|
4873
5247
|
}
|
|
4874
5248
|
files.delete(name);
|
|
@@ -4899,10 +5273,10 @@
|
|
|
4899
5273
|
await lockWriter;
|
|
4900
5274
|
}
|
|
4901
5275
|
|
|
4902
|
-
async function skipDiskIfNeeded(
|
|
5276
|
+
async function skipDiskIfNeeded() {
|
|
4903
5277
|
if (getLength(headerInfo.localHeaderArray) > writer.availableSize) {
|
|
4904
5278
|
writer.availableSize = 0;
|
|
4905
|
-
await writeData(
|
|
5279
|
+
await writeData(writer, new Uint8Array());
|
|
4906
5280
|
}
|
|
4907
5281
|
}
|
|
4908
5282
|
}
|
|
@@ -5043,6 +5417,10 @@
|
|
|
5043
5417
|
uncompressedSize = result.inputSize;
|
|
5044
5418
|
signature = result.signature;
|
|
5045
5419
|
}
|
|
5420
|
+
if ((!zip64CompressedSize && compressedSize >= MAX_32_BITS) ||
|
|
5421
|
+
(!zip64UncompressedSize && uncompressedSize >= MAX_32_BITS)) {
|
|
5422
|
+
throw new Error(ERR_UNSUPPORTED_FORMAT);
|
|
5423
|
+
}
|
|
5046
5424
|
} catch (error) {
|
|
5047
5425
|
if (error.outputSize !== UNDEFINED_VALUE) {
|
|
5048
5426
|
writer.size += error.outputSize;
|
|
@@ -5105,35 +5483,28 @@
|
|
|
5105
5483
|
uncompressedSize
|
|
5106
5484
|
} = options;
|
|
5107
5485
|
let { version, compressionMethod } = options;
|
|
5108
|
-
const compressed = !directory && (
|
|
5486
|
+
const compressed = !directory && (compressionMethod === UNDEFINED_VALUE
|
|
5487
|
+
? (level === UNDEFINED_VALUE || level > 0)
|
|
5488
|
+
: compressionMethod !== COMPRESSION_METHOD_STORE);
|
|
5109
5489
|
let rawLocalExtraFieldZip64;
|
|
5110
5490
|
const uncompressedFile = passThrough || !compressed;
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
setUint16(rawLocalExtraFieldZip64View, 2, getLength(rawLocalExtraFieldZip64) - 4);
|
|
5491
|
+
// entries without a data descriptor are always buffered (cf. getFileEntry), so the
|
|
5492
|
+
// zip64 extra field values can be patched into the local header before flushing it
|
|
5493
|
+
const zip64ExtraFieldComplete = zip64 && (options.bufferedWrite || !dataDescriptor || ((!zip64UncompressedSize && !zip64CompressedSize) || uncompressedFile));
|
|
5494
|
+
const writeLocalExtraFieldZip64 = zip64ExtraFieldComplete || (zip64 && dataDescriptor && (zip64UncompressedSize || zip64CompressedSize));
|
|
5495
|
+
if (zip64 && (zip64UncompressedSize || zip64CompressedSize)) {
|
|
5496
|
+
// APPNOTE 4.5.3: the zip64 extra field of a local header must include both the
|
|
5497
|
+
// uncompressed and compressed size fields
|
|
5498
|
+
const length = 4 + 16;
|
|
5499
|
+
const extraFieldZip64 = createRecordWriter(length);
|
|
5500
|
+
extraFieldZip64.uint16(EXTRAFIELD_TYPE_ZIP64);
|
|
5501
|
+
extraFieldZip64.uint16(length - 4);
|
|
5502
|
+
rawLocalExtraFieldZip64 = extraFieldZip64.array;
|
|
5124
5503
|
if (zip64ExtraFieldComplete) {
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
rawLocalExtraFieldZip64Offset += 8;
|
|
5130
|
-
}
|
|
5131
|
-
if (zip64CompressedSize && uncompressedFile) {
|
|
5132
|
-
setBigUint64(rawLocalExtraFieldZip64View, rawLocalExtraFieldZip64Offset, BigInt(uncompressedSize));
|
|
5133
|
-
rawLocalExtraFieldZip64Offset += 8;
|
|
5134
|
-
}
|
|
5135
|
-
if (rawLocalExtraFieldZip64Offset == 4) {
|
|
5136
|
-
rawLocalExtraFieldZip64 = new Uint8Array();
|
|
5504
|
+
extraFieldZip64.uint64(uncompressedSize);
|
|
5505
|
+
if (uncompressedFile) {
|
|
5506
|
+
const encryptionOverhead = encrypted ? (zipCrypto ? 12 : 16 + encryptionStrength * 4) : 0;
|
|
5507
|
+
extraFieldZip64.uint64(passThrough ? 0 : uncompressedSize + encryptionOverhead);
|
|
5137
5508
|
}
|
|
5138
5509
|
}
|
|
5139
5510
|
} else {
|
|
@@ -5141,11 +5512,11 @@
|
|
|
5141
5512
|
}
|
|
5142
5513
|
let rawExtraFieldAES;
|
|
5143
5514
|
if (encrypted && !zipCrypto) {
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
|
|
5515
|
+
const extraFieldAES = createRecordWriter(getLength(EXTRAFIELD_DATA_AES) + 2);
|
|
5516
|
+
extraFieldAES.uint16(EXTRAFIELD_TYPE_AES);
|
|
5517
|
+
extraFieldAES.bytes(EXTRAFIELD_DATA_AES);
|
|
5518
|
+
rawExtraFieldAES = extraFieldAES.array;
|
|
5519
|
+
rawExtraFieldAES[8] = encryptionStrength;
|
|
5149
5520
|
} else {
|
|
5150
5521
|
rawExtraFieldAES = new Uint8Array();
|
|
5151
5522
|
}
|
|
@@ -5153,33 +5524,38 @@
|
|
|
5153
5524
|
let rawExtraFieldExtendedTimestamp;
|
|
5154
5525
|
let extraFieldExtendedTimestampFlag;
|
|
5155
5526
|
if (extendedTimestamp) {
|
|
5156
|
-
|
|
5157
|
-
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
5527
|
+
const lastModTimeUnix = getTimeUnix(lastModDate);
|
|
5528
|
+
if (inUnixTimeRange(lastModTimeUnix)) {
|
|
5529
|
+
const extraFieldTimestampLength = 9 + (lastAccessDate ? 4 : 0) + (creationDate ? 4 : 0);
|
|
5530
|
+
const extraFieldTimestamp = createRecordWriter(extraFieldTimestampLength);
|
|
5531
|
+
extraFieldExtendedTimestampFlag = 0x1 + (lastAccessDate ? 0x2 : 0) + (creationDate ? 0x4 : 0);
|
|
5532
|
+
extraFieldTimestamp.uint16(EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP);
|
|
5533
|
+
extraFieldTimestamp.uint16(extraFieldTimestampLength - 4);
|
|
5534
|
+
extraFieldTimestamp.uint8(extraFieldExtendedTimestampFlag);
|
|
5535
|
+
extraFieldTimestamp.uint32(lastModTimeUnix);
|
|
5536
|
+
if (lastAccessDate) {
|
|
5537
|
+
extraFieldTimestamp.uint32(clampUnixTime(getTimeUnix(lastAccessDate)));
|
|
5538
|
+
}
|
|
5539
|
+
if (creationDate) {
|
|
5540
|
+
extraFieldTimestamp.uint32(clampUnixTime(getTimeUnix(creationDate)));
|
|
5541
|
+
}
|
|
5542
|
+
rawExtraFieldExtendedTimestamp = extraFieldTimestamp.array;
|
|
5543
|
+
} else {
|
|
5544
|
+
// the date cannot be stored as a 32-bit time_t; the NTFS field replaces it
|
|
5545
|
+
rawExtraFieldExtendedTimestamp = new Uint8Array();
|
|
5171
5546
|
}
|
|
5172
5547
|
try {
|
|
5173
|
-
rawExtraFieldNTFS = new Uint8Array(36);
|
|
5174
|
-
const extraFieldNTFSView = getDataView(rawExtraFieldNTFS);
|
|
5175
5548
|
const lastModTimeNTFS = getTimeNTFS(lastModDate);
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
5182
|
-
|
|
5549
|
+
const extraFieldNTFS = createRecordWriter(36);
|
|
5550
|
+
extraFieldNTFS.uint16(EXTRAFIELD_TYPE_NTFS);
|
|
5551
|
+
extraFieldNTFS.uint16(32);
|
|
5552
|
+
extraFieldNTFS.skip(4);
|
|
5553
|
+
extraFieldNTFS.uint16(EXTRAFIELD_TYPE_NTFS_TAG1);
|
|
5554
|
+
extraFieldNTFS.uint16(24);
|
|
5555
|
+
extraFieldNTFS.uint64(lastModTimeNTFS);
|
|
5556
|
+
extraFieldNTFS.uint64(getTimeNTFS(lastAccessDate) || lastModTimeNTFS);
|
|
5557
|
+
extraFieldNTFS.uint64(getTimeNTFS(creationDate) || lastModTimeNTFS);
|
|
5558
|
+
rawExtraFieldNTFS = extraFieldNTFS.array;
|
|
5183
5559
|
} catch {
|
|
5184
5560
|
rawExtraFieldNTFS = new Uint8Array();
|
|
5185
5561
|
}
|
|
@@ -5188,41 +5564,27 @@
|
|
|
5188
5564
|
}
|
|
5189
5565
|
let rawExtraFieldUnix;
|
|
5190
5566
|
try {
|
|
5191
|
-
const { uid, gid,
|
|
5192
|
-
if (unixExtraFieldType && (uid !== UNDEFINED_VALUE || gid !== UNDEFINED_VALUE
|
|
5567
|
+
const { uid, gid, unixExtraFieldType } = options;
|
|
5568
|
+
if (unixExtraFieldType == INFOZIP_EXTRA_FIELD_TYPE && (uid !== UNDEFINED_VALUE || gid !== UNDEFINED_VALUE)) {
|
|
5193
5569
|
const uidBytes = packUnixId(uid);
|
|
5194
5570
|
const gidBytes = packUnixId(gid);
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
rawExtraFieldUnix = new Uint8Array(4 + payloadLength);
|
|
5213
|
-
const rawExtraFieldUnixView = getDataView(rawExtraFieldUnix);
|
|
5214
|
-
setUint16(rawExtraFieldUnixView, 0, unixExtraFieldType == INFOZIP_EXTRA_FIELD_TYPE ? EXTRAFIELD_TYPE_INFOZIP : EXTRAFIELD_TYPE_UNIX);
|
|
5215
|
-
setUint16(rawExtraFieldUnixView, 2, payloadLength);
|
|
5216
|
-
setUint8(rawExtraFieldUnixView, 4, 1);
|
|
5217
|
-
setUint8(rawExtraFieldUnixView, 5, uidBytes.length);
|
|
5218
|
-
let offset = 6;
|
|
5219
|
-
arraySet(rawExtraFieldUnix, uidBytes, offset);
|
|
5220
|
-
offset += uidBytes.length;
|
|
5221
|
-
setUint8(rawExtraFieldUnixView, offset, gidBytes.length);
|
|
5222
|
-
offset++;
|
|
5223
|
-
arraySet(rawExtraFieldUnix, gidBytes, offset);
|
|
5224
|
-
offset += gidBytes.length;
|
|
5225
|
-
arraySet(rawExtraFieldUnix, modeArray, offset);
|
|
5571
|
+
const payloadLength = 3 + uidBytes.length + gidBytes.length;
|
|
5572
|
+
const extraFieldUnix = createRecordWriter(4 + payloadLength);
|
|
5573
|
+
extraFieldUnix.uint16(EXTRAFIELD_TYPE_INFOZIP);
|
|
5574
|
+
extraFieldUnix.uint16(payloadLength);
|
|
5575
|
+
extraFieldUnix.uint8(1);
|
|
5576
|
+
extraFieldUnix.uint8(uidBytes.length);
|
|
5577
|
+
extraFieldUnix.bytes(uidBytes);
|
|
5578
|
+
extraFieldUnix.uint8(gidBytes.length);
|
|
5579
|
+
extraFieldUnix.bytes(gidBytes);
|
|
5580
|
+
rawExtraFieldUnix = extraFieldUnix.array;
|
|
5581
|
+
} else if (unixExtraFieldType == UNIX_EXTRA_FIELD_TYPE && (uid !== UNDEFINED_VALUE || gid !== UNDEFINED_VALUE)) {
|
|
5582
|
+
const extraFieldUnix = createRecordWriter(8);
|
|
5583
|
+
extraFieldUnix.uint16(EXTRAFIELD_TYPE_UNIX);
|
|
5584
|
+
extraFieldUnix.uint16(4);
|
|
5585
|
+
extraFieldUnix.uint16((uid === UNDEFINED_VALUE ? 0 : uid) & MAX_16_BITS);
|
|
5586
|
+
extraFieldUnix.uint16((gid === UNDEFINED_VALUE ? 0 : gid) & MAX_16_BITS);
|
|
5587
|
+
rawExtraFieldUnix = extraFieldUnix.array;
|
|
5226
5588
|
} else {
|
|
5227
5589
|
rawExtraFieldUnix = new Uint8Array();
|
|
5228
5590
|
}
|
|
@@ -5240,8 +5602,11 @@
|
|
|
5240
5602
|
rawExtraFieldAES[9] = compressionMethod;
|
|
5241
5603
|
compressionMethod = COMPRESSION_METHOD_AES;
|
|
5242
5604
|
}
|
|
5243
|
-
const localExtraFieldZip64Length =
|
|
5605
|
+
const localExtraFieldZip64Length = writeLocalExtraFieldZip64 ? getLength(rawLocalExtraFieldZip64) : 0;
|
|
5244
5606
|
const extraFieldLength = localExtraFieldZip64Length + getLength(rawExtraFieldAES, rawExtraFieldExtendedTimestamp, rawExtraFieldNTFS, rawExtraFieldUnix, rawExtraField);
|
|
5607
|
+
if (extraFieldLength > MAX_16_BITS) {
|
|
5608
|
+
throw new Error(ERR_INVALID_EXTRAFIELD_DATA);
|
|
5609
|
+
}
|
|
5245
5610
|
const {
|
|
5246
5611
|
headerArray,
|
|
5247
5612
|
headerView,
|
|
@@ -5257,29 +5622,27 @@
|
|
|
5257
5622
|
zip64UncompressedSize,
|
|
5258
5623
|
extraFieldLength
|
|
5259
5624
|
});
|
|
5260
|
-
|
|
5261
|
-
const localHeaderArray =
|
|
5625
|
+
const localHeader = createRecordWriter(HEADER_SIZE + getLength(rawFilename) + extraFieldLength);
|
|
5626
|
+
const localHeaderArray = localHeader.array;
|
|
5262
5627
|
const localHeaderView = getDataView(localHeaderArray);
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
localHeaderOffset += getLength(rawExtraFieldExtendedTimestamp);
|
|
5275
|
-
arraySet(localHeaderArray, rawExtraFieldNTFS, localHeaderOffset);
|
|
5276
|
-
localHeaderOffset += getLength(rawExtraFieldNTFS);
|
|
5277
|
-
arraySet(localHeaderArray, rawExtraFieldUnix, localHeaderOffset);
|
|
5278
|
-
localHeaderOffset += getLength(rawExtraFieldUnix);
|
|
5279
|
-
arraySet(localHeaderArray, rawExtraField, localHeaderOffset);
|
|
5628
|
+
localHeader.uint32(LOCAL_FILE_HEADER_SIGNATURE);
|
|
5629
|
+
localHeader.bytes(headerArray);
|
|
5630
|
+
localHeader.bytes(rawFilename);
|
|
5631
|
+
if (writeLocalExtraFieldZip64) {
|
|
5632
|
+
localHeader.bytes(rawLocalExtraFieldZip64);
|
|
5633
|
+
}
|
|
5634
|
+
localHeader.bytes(rawExtraFieldAES);
|
|
5635
|
+
localHeader.bytes(rawExtraFieldExtendedTimestamp);
|
|
5636
|
+
localHeader.bytes(rawExtraFieldNTFS);
|
|
5637
|
+
localHeader.bytes(rawExtraFieldUnix);
|
|
5638
|
+
localHeader.bytes(rawExtraField);
|
|
5280
5639
|
if (dataDescriptor) {
|
|
5281
|
-
|
|
5282
|
-
|
|
5640
|
+
if (!zip64CompressedSize) {
|
|
5641
|
+
setUint32(localHeaderView, HEADER_OFFSET_COMPRESSED_SIZE + LOCAL_HEADER_COMMON_OFFSET, 0);
|
|
5642
|
+
}
|
|
5643
|
+
if (!zip64UncompressedSize) {
|
|
5644
|
+
setUint32(localHeaderView, HEADER_OFFSET_UNCOMPRESSED_SIZE + LOCAL_HEADER_COMMON_OFFSET, 0);
|
|
5645
|
+
}
|
|
5283
5646
|
}
|
|
5284
5647
|
return {
|
|
5285
5648
|
localHeaderArray,
|
|
@@ -5306,7 +5669,7 @@
|
|
|
5306
5669
|
function appendExtraFieldUSDZ(entryInfo, zipWriterOffset) {
|
|
5307
5670
|
const { headerInfo } = entryInfo;
|
|
5308
5671
|
let { localHeaderArray, extraFieldLength } = headerInfo;
|
|
5309
|
-
let localHeaderArrayView
|
|
5672
|
+
let localHeaderArrayView;
|
|
5310
5673
|
let extraBytesLength = 64 - ((zipWriterOffset + getLength(localHeaderArray)) % 64);
|
|
5311
5674
|
if (extraBytesLength < 4) {
|
|
5312
5675
|
extraBytesLength += 64;
|
|
@@ -5314,13 +5677,14 @@
|
|
|
5314
5677
|
const rawExtraFieldUSDZ = new Uint8Array(extraBytesLength);
|
|
5315
5678
|
const extraFieldUSDZView = getDataView(rawExtraFieldUSDZ);
|
|
5316
5679
|
setUint16(extraFieldUSDZView, 0, EXTRAFIELD_TYPE_USDZ);
|
|
5317
|
-
setUint16(extraFieldUSDZView, 2, extraBytesLength -
|
|
5680
|
+
setUint16(extraFieldUSDZView, 2, extraBytesLength - 4);
|
|
5318
5681
|
const previousLocalHeaderArray = localHeaderArray;
|
|
5319
5682
|
headerInfo.localHeaderArray = localHeaderArray = new Uint8Array(getLength(previousLocalHeaderArray) + extraBytesLength);
|
|
5320
5683
|
arraySet(localHeaderArray, previousLocalHeaderArray);
|
|
5321
5684
|
arraySet(localHeaderArray, rawExtraFieldUSDZ, getLength(previousLocalHeaderArray));
|
|
5322
5685
|
localHeaderArrayView = getDataView(localHeaderArray);
|
|
5323
5686
|
setUint16(localHeaderArrayView, 28, extraFieldLength + extraBytesLength);
|
|
5687
|
+
headerInfo.localHeaderView = localHeaderArrayView;
|
|
5324
5688
|
entryInfo.metadataSize += extraBytesLength;
|
|
5325
5689
|
}
|
|
5326
5690
|
|
|
@@ -5443,37 +5807,32 @@
|
|
|
5443
5807
|
}, localHeaderView, { dataDescriptor }) {
|
|
5444
5808
|
if (!dataDescriptor) {
|
|
5445
5809
|
if (!encrypted) {
|
|
5446
|
-
setUint32(localHeaderView, HEADER_OFFSET_SIGNATURE +
|
|
5810
|
+
setUint32(localHeaderView, HEADER_OFFSET_SIGNATURE + LOCAL_HEADER_COMMON_OFFSET, signature);
|
|
5447
5811
|
}
|
|
5448
5812
|
if (!zip64CompressedSize) {
|
|
5449
|
-
setUint32(localHeaderView, HEADER_OFFSET_COMPRESSED_SIZE +
|
|
5813
|
+
setUint32(localHeaderView, HEADER_OFFSET_COMPRESSED_SIZE + LOCAL_HEADER_COMMON_OFFSET, compressedSize);
|
|
5450
5814
|
}
|
|
5451
5815
|
if (!zip64UncompressedSize) {
|
|
5452
|
-
setUint32(localHeaderView, HEADER_OFFSET_UNCOMPRESSED_SIZE +
|
|
5816
|
+
setUint32(localHeaderView, HEADER_OFFSET_UNCOMPRESSED_SIZE + LOCAL_HEADER_COMMON_OFFSET, uncompressedSize);
|
|
5453
5817
|
}
|
|
5454
5818
|
}
|
|
5455
5819
|
if (zip64 && localExtraFieldZip64Length) {
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
}
|
|
5461
|
-
if (zip64CompressedSize) {
|
|
5462
|
-
setBigUint64(localHeaderView, localHeaderOffset, BigInt(compressedSize));
|
|
5463
|
-
localHeaderOffset += 8;
|
|
5464
|
-
}
|
|
5820
|
+
// the local zip64 extra field always contains both size slots
|
|
5821
|
+
const localHeaderOffset = HEADER_SIZE + getLength(rawFilename) + 4;
|
|
5822
|
+
setBigUint64(localHeaderView, localHeaderOffset, BigInt(uncompressedSize));
|
|
5823
|
+
setBigUint64(localHeaderView, localHeaderOffset + 8, BigInt(compressedSize));
|
|
5465
5824
|
}
|
|
5466
5825
|
}
|
|
5467
5826
|
|
|
5468
5827
|
|
|
5469
5828
|
async function closeFile(zipWriter, comment, options) {
|
|
5470
|
-
const
|
|
5471
|
-
const {
|
|
5472
|
-
|
|
5473
|
-
|
|
5829
|
+
const directoryDataLength = createDirectoryRecords(zipWriter.files);
|
|
5830
|
+
const { cdStartDiskNumber, cdStartDiskOffset } = await writeDirectoryRecords(zipWriter, directoryDataLength, options);
|
|
5831
|
+
await writeEndOfDirectoryRecord(zipWriter, comment, options, { cdStartDiskNumber, cdStartDiskOffset, directoryDataLength });
|
|
5832
|
+
}
|
|
5833
|
+
|
|
5834
|
+
function createDirectoryRecords(files) {
|
|
5474
5835
|
let directoryDataLength = 0;
|
|
5475
|
-
let directoryOffset = zipWriter.offset - diskOffset;
|
|
5476
|
-
let filesLength = files.size;
|
|
5477
5836
|
for (const [, fileEntry] of files) {
|
|
5478
5837
|
const {
|
|
5479
5838
|
rawFilename,
|
|
@@ -5490,24 +5849,27 @@
|
|
|
5490
5849
|
uncompressedSize,
|
|
5491
5850
|
compressedSize
|
|
5492
5851
|
} = fileEntry;
|
|
5493
|
-
const zip64Offset = fileEntry.offset
|
|
5494
|
-
const zip64DiskNumberStart = fileEntry.diskNumberStart
|
|
5852
|
+
const zip64Offset = fileEntry.offset >= MAX_32_BITS;
|
|
5853
|
+
const zip64DiskNumberStart = fileEntry.diskNumberStart >= MAX_16_BITS;
|
|
5495
5854
|
let rawExtraFieldZip64;
|
|
5496
5855
|
if (zip64Offset || zip64DiskNumberStart || zip64UncompressedSize || zip64CompressedSize) {
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
|
|
5501
|
-
if (
|
|
5502
|
-
|
|
5503
|
-
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
if (
|
|
5508
|
-
|
|
5509
|
-
|
|
5510
|
-
if (zip64DiskNumberStart) {
|
|
5856
|
+
const length = 4 + (zip64UncompressedSize ? 8 : 0) + (zip64CompressedSize ? 8 : 0) + (zip64Offset ? 8 : 0) + (zip64DiskNumberStart ? 4 : 0);
|
|
5857
|
+
const extraFieldZip64 = createRecordWriter(length);
|
|
5858
|
+
extraFieldZip64.uint16(EXTRAFIELD_TYPE_ZIP64);
|
|
5859
|
+
extraFieldZip64.uint16(length - 4);
|
|
5860
|
+
if (zip64UncompressedSize) {
|
|
5861
|
+
extraFieldZip64.uint64(uncompressedSize);
|
|
5862
|
+
}
|
|
5863
|
+
if (zip64CompressedSize) {
|
|
5864
|
+
extraFieldZip64.uint64(compressedSize);
|
|
5865
|
+
}
|
|
5866
|
+
if (zip64Offset) {
|
|
5867
|
+
extraFieldZip64.uint64(fileEntry.offset);
|
|
5868
|
+
}
|
|
5869
|
+
if (zip64DiskNumberStart) {
|
|
5870
|
+
extraFieldZip64.uint32(fileEntry.diskNumberStart);
|
|
5871
|
+
}
|
|
5872
|
+
rawExtraFieldZip64 = extraFieldZip64.array;
|
|
5511
5873
|
} else {
|
|
5512
5874
|
rawExtraFieldZip64 = new Uint8Array();
|
|
5513
5875
|
}
|
|
@@ -5515,32 +5877,41 @@
|
|
|
5515
5877
|
fileEntry.zip64Offset = zip64Offset;
|
|
5516
5878
|
fileEntry.zip64DiskNumberStart = zip64DiskNumberStart;
|
|
5517
5879
|
let rawExtraFieldTimestamp;
|
|
5518
|
-
|
|
5519
|
-
|
|
5520
|
-
const
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
|
|
5524
|
-
|
|
5880
|
+
const lastModTimeUnix = getTimeUnix(lastModDate);
|
|
5881
|
+
if (extendedTimestamp && inUnixTimeRange(lastModTimeUnix)) {
|
|
5882
|
+
const extraFieldTimestamp = createRecordWriter(9);
|
|
5883
|
+
extraFieldTimestamp.uint16(EXTRAFIELD_TYPE_EXTENDED_TIMESTAMP);
|
|
5884
|
+
extraFieldTimestamp.uint16(5);
|
|
5885
|
+
extraFieldTimestamp.uint8(extraFieldExtendedTimestampFlag);
|
|
5886
|
+
extraFieldTimestamp.uint32(lastModTimeUnix);
|
|
5887
|
+
rawExtraFieldTimestamp = extraFieldTimestamp.array;
|
|
5525
5888
|
} else {
|
|
5526
5889
|
rawExtraFieldTimestamp = new Uint8Array();
|
|
5527
5890
|
}
|
|
5528
5891
|
fileEntry.rawExtraFieldExtendedTimestamp = rawExtraFieldTimestamp;
|
|
5529
|
-
|
|
5530
|
-
|
|
5531
|
-
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
|
|
5535
|
-
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
|
|
5892
|
+
const extraFieldLength = getLength(
|
|
5893
|
+
rawExtraFieldZip64,
|
|
5894
|
+
rawExtraFieldAES,
|
|
5895
|
+
rawExtraFieldNTFS,
|
|
5896
|
+
rawExtraFieldUnix,
|
|
5897
|
+
rawExtraFieldTimestamp,
|
|
5898
|
+
rawExtraField);
|
|
5899
|
+
if (extraFieldLength > MAX_16_BITS) {
|
|
5900
|
+
throw new Error(ERR_INVALID_EXTRAFIELD_DATA);
|
|
5901
|
+
}
|
|
5902
|
+
directoryDataLength += CENTRAL_FILE_HEADER_LENGTH + getLength(rawFilename, rawComment) + extraFieldLength;
|
|
5539
5903
|
}
|
|
5904
|
+
return directoryDataLength;
|
|
5905
|
+
}
|
|
5906
|
+
|
|
5907
|
+
async function writeDirectoryRecords(zipWriter, directoryDataLength, options) {
|
|
5908
|
+
const { files, writer } = zipWriter;
|
|
5540
5909
|
const directoryArray = new Uint8Array(directoryDataLength);
|
|
5541
|
-
const directoryView = getDataView(directoryArray);
|
|
5542
5910
|
await initStream(writer);
|
|
5911
|
+
let offset = 0;
|
|
5543
5912
|
let directoryDiskOffset = 0;
|
|
5913
|
+
let cdStartDiskNumber = writer.diskNumber;
|
|
5914
|
+
let cdStartDiskOffset = writer.diskOffset;
|
|
5544
5915
|
for (const [indexFileEntry, fileEntry] of Array.from(files.values()).entries()) {
|
|
5545
5916
|
const {
|
|
5546
5917
|
offset: fileEntryOffset,
|
|
@@ -5566,52 +5937,46 @@
|
|
|
5566
5937
|
compressedSize
|
|
5567
5938
|
} = fileEntry;
|
|
5568
5939
|
const extraFieldLength = getLength(rawExtraFieldZip64, rawExtraFieldAES, rawExtraFieldExtendedTimestamp, rawExtraFieldNTFS, rawExtraFieldUnix, rawExtraField);
|
|
5569
|
-
|
|
5570
|
-
|
|
5940
|
+
const directoryRecordLength = CENTRAL_FILE_HEADER_LENGTH + getLength(rawFilename, rawComment) + extraFieldLength;
|
|
5941
|
+
if (offset + directoryRecordLength - directoryDiskOffset > writer.availableSize) {
|
|
5942
|
+
await writeData(writer, directoryArray.slice(directoryDiskOffset, offset));
|
|
5943
|
+
directoryDiskOffset = offset;
|
|
5944
|
+
writer.availableSize = 0;
|
|
5945
|
+
await writeData(writer, new Uint8Array());
|
|
5946
|
+
}
|
|
5947
|
+
if (indexFileEntry == 0) {
|
|
5948
|
+
cdStartDiskNumber = writer.diskNumber;
|
|
5949
|
+
cdStartDiskOffset = writer.diskOffset;
|
|
5950
|
+
}
|
|
5571
5951
|
if (!zip64UncompressedSize) {
|
|
5572
5952
|
setUint32(headerView, HEADER_OFFSET_UNCOMPRESSED_SIZE, uncompressedSize);
|
|
5573
5953
|
}
|
|
5574
5954
|
if (!zip64CompressedSize) {
|
|
5575
5955
|
setUint32(headerView, HEADER_OFFSET_COMPRESSED_SIZE, compressedSize);
|
|
5576
5956
|
}
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
|
|
5600
|
-
directoryOffset += getLength(rawExtraFieldExtendedTimestamp);
|
|
5601
|
-
arraySet(directoryArray, rawExtraFieldNTFS, directoryOffset);
|
|
5602
|
-
directoryOffset += getLength(rawExtraFieldNTFS);
|
|
5603
|
-
arraySet(directoryArray, rawExtraFieldUnix, directoryOffset);
|
|
5604
|
-
directoryOffset += getLength(rawExtraFieldUnix);
|
|
5605
|
-
arraySet(directoryArray, rawExtraField, directoryOffset);
|
|
5606
|
-
directoryOffset += getLength(rawExtraField);
|
|
5607
|
-
arraySet(directoryArray, rawComment, directoryOffset);
|
|
5608
|
-
directoryOffset += getLength(rawComment);
|
|
5609
|
-
if (offset - directoryDiskOffset > writer.availableSize) {
|
|
5610
|
-
writer.availableSize = 0;
|
|
5611
|
-
await writeData(writer, directoryArray.slice(directoryDiskOffset, offset));
|
|
5612
|
-
directoryDiskOffset = offset;
|
|
5613
|
-
}
|
|
5614
|
-
offset = directoryOffset;
|
|
5957
|
+
if ((zip64Offset || zip64DiskNumberStart) && fileEntry.version < VERSION_ZIP64) {
|
|
5958
|
+
setUint16(headerView, HEADER_OFFSET_VERSION, VERSION_ZIP64);
|
|
5959
|
+
}
|
|
5960
|
+
const directoryRecord = createRecordWriter(directoryRecordLength);
|
|
5961
|
+
directoryRecord.uint32(CENTRAL_FILE_HEADER_SIGNATURE);
|
|
5962
|
+
directoryRecord.uint16(versionMadeBy);
|
|
5963
|
+
directoryRecord.bytes(headerArray.subarray(0, HEADER_SIZE - 4 - 2));
|
|
5964
|
+
directoryRecord.uint16(extraFieldLength);
|
|
5965
|
+
directoryRecord.uint16(getLength(rawComment));
|
|
5966
|
+
directoryRecord.uint16(zip64DiskNumberStart ? MAX_16_BITS : diskNumberStart);
|
|
5967
|
+
directoryRecord.uint16(internalFileAttributes);
|
|
5968
|
+
directoryRecord.uint32(externalFileAttributes);
|
|
5969
|
+
directoryRecord.uint32(zip64Offset ? MAX_32_BITS : fileEntryOffset);
|
|
5970
|
+
directoryRecord.bytes(rawFilename);
|
|
5971
|
+
directoryRecord.bytes(rawExtraFieldZip64);
|
|
5972
|
+
directoryRecord.bytes(rawExtraFieldAES);
|
|
5973
|
+
directoryRecord.bytes(rawExtraFieldExtendedTimestamp);
|
|
5974
|
+
directoryRecord.bytes(rawExtraFieldNTFS);
|
|
5975
|
+
directoryRecord.bytes(rawExtraFieldUnix);
|
|
5976
|
+
directoryRecord.bytes(rawExtraField);
|
|
5977
|
+
directoryRecord.bytes(rawComment);
|
|
5978
|
+
arraySet(directoryArray, directoryRecord.array, offset);
|
|
5979
|
+
offset += directoryRecordLength;
|
|
5615
5980
|
if (options.onprogress) {
|
|
5616
5981
|
try {
|
|
5617
5982
|
await options.onprogress(indexFileEntry + 1, files.size, new Entry(fileEntry));
|
|
@@ -5621,36 +5986,53 @@
|
|
|
5621
5986
|
}
|
|
5622
5987
|
}
|
|
5623
5988
|
await writeData(writer, directoryDiskOffset ? directoryArray.slice(directoryDiskOffset) : directoryArray);
|
|
5989
|
+
return { cdStartDiskNumber, cdStartDiskOffset };
|
|
5990
|
+
}
|
|
5991
|
+
|
|
5992
|
+
async function writeEndOfDirectoryRecord(zipWriter, comment, options, cdInfo) {
|
|
5993
|
+
const { writer } = zipWriter;
|
|
5994
|
+
const { cdStartDiskNumber, cdStartDiskOffset } = cdInfo;
|
|
5995
|
+
let { directoryDataLength } = cdInfo;
|
|
5996
|
+
let filesLength = zipWriter.files.size;
|
|
5997
|
+
let diskNumber = cdStartDiskNumber;
|
|
5998
|
+
let directoryOffset = zipWriter.offset - cdStartDiskOffset - (cdStartDiskNumber ? zipWriter.initialOffset : 0);
|
|
5624
5999
|
let lastDiskNumber = writer.diskNumber;
|
|
5625
|
-
|
|
5626
|
-
if (availableSize < END_OF_CENTRAL_DIR_LENGTH) {
|
|
6000
|
+
if (writer.availableSize < END_OF_CENTRAL_DIR_LENGTH) {
|
|
5627
6001
|
lastDiskNumber++;
|
|
5628
6002
|
}
|
|
5629
6003
|
let zip64 = getOptionValue(zipWriter, options, PROPERTY_NAME_ZIP64);
|
|
5630
|
-
if (directoryOffset
|
|
6004
|
+
if (directoryOffset >= MAX_32_BITS || directoryDataLength >= MAX_32_BITS || filesLength >= MAX_16_BITS || lastDiskNumber >= MAX_16_BITS) {
|
|
5631
6005
|
if (zip64 === false) {
|
|
5632
6006
|
throw new Error(ERR_UNSUPPORTED_FORMAT);
|
|
5633
6007
|
} else {
|
|
5634
6008
|
zip64 = true;
|
|
5635
6009
|
}
|
|
5636
6010
|
}
|
|
5637
|
-
const
|
|
5638
|
-
|
|
5639
|
-
|
|
6011
|
+
const commentLength = getLength(comment);
|
|
6012
|
+
if (commentLength > MAX_16_BITS) {
|
|
6013
|
+
throw new Error(ERR_INVALID_COMMENT);
|
|
6014
|
+
}
|
|
6015
|
+
const endOfdirectoryRecord = createRecordWriter(zip64 ? ZIP64_END_OF_CENTRAL_DIR_TOTAL_LENGTH : END_OF_CENTRAL_DIR_LENGTH);
|
|
6016
|
+
if (getLength(endOfdirectoryRecord.array) + commentLength > writer.availableSize) {
|
|
6017
|
+
writer.availableSize = 0;
|
|
6018
|
+
await writeData(writer, new Uint8Array());
|
|
6019
|
+
}
|
|
6020
|
+
lastDiskNumber = writer.diskNumber;
|
|
5640
6021
|
if (zip64) {
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
6022
|
+
endOfdirectoryRecord.uint32(ZIP64_END_OF_CENTRAL_DIR_SIGNATURE);
|
|
6023
|
+
endOfdirectoryRecord.uint64(44);
|
|
6024
|
+
endOfdirectoryRecord.uint16(45);
|
|
6025
|
+
endOfdirectoryRecord.uint16(45);
|
|
6026
|
+
endOfdirectoryRecord.uint32(lastDiskNumber);
|
|
6027
|
+
endOfdirectoryRecord.uint32(diskNumber);
|
|
6028
|
+
endOfdirectoryRecord.uint64(filesLength);
|
|
6029
|
+
endOfdirectoryRecord.uint64(filesLength);
|
|
6030
|
+
endOfdirectoryRecord.uint64(directoryDataLength);
|
|
6031
|
+
endOfdirectoryRecord.uint64(directoryOffset);
|
|
6032
|
+
endOfdirectoryRecord.uint32(ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIGNATURE);
|
|
6033
|
+
endOfdirectoryRecord.uint32(lastDiskNumber);
|
|
6034
|
+
endOfdirectoryRecord.uint64(BigInt(zipWriter.offset) + BigInt(directoryDataLength) - BigInt(writer.diskOffset) - BigInt(writer.diskNumber ? zipWriter.initialOffset : 0));
|
|
6035
|
+
endOfdirectoryRecord.uint32(lastDiskNumber + 1);
|
|
5654
6036
|
const supportZip64SplitFile = getOptionValue(zipWriter, options, OPTION_SUPPORT_ZIP64_SPLIT_FILE, true);
|
|
5655
6037
|
if (supportZip64SplitFile) {
|
|
5656
6038
|
lastDiskNumber = MAX_16_BITS;
|
|
@@ -5659,29 +6041,40 @@
|
|
|
5659
6041
|
filesLength = MAX_16_BITS;
|
|
5660
6042
|
directoryOffset = MAX_32_BITS;
|
|
5661
6043
|
directoryDataLength = MAX_32_BITS;
|
|
5662
|
-
offset += ZIP64_END_OF_CENTRAL_DIR_LENGTH + ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH;
|
|
5663
|
-
}
|
|
5664
|
-
setUint32(endOfdirectoryView, offset, END_OF_CENTRAL_DIR_SIGNATURE);
|
|
5665
|
-
setUint16(endOfdirectoryView, offset + 4, lastDiskNumber);
|
|
5666
|
-
setUint16(endOfdirectoryView, offset + 6, diskNumber);
|
|
5667
|
-
setUint16(endOfdirectoryView, offset + 8, filesLength);
|
|
5668
|
-
setUint16(endOfdirectoryView, offset + 10, filesLength);
|
|
5669
|
-
setUint32(endOfdirectoryView, offset + 12, directoryDataLength);
|
|
5670
|
-
setUint32(endOfdirectoryView, offset + 16, directoryOffset);
|
|
5671
|
-
const commentLength = getLength(comment);
|
|
5672
|
-
if (commentLength) {
|
|
5673
|
-
if (commentLength <= MAX_16_BITS) {
|
|
5674
|
-
setUint16(endOfdirectoryView, offset + 20, commentLength);
|
|
5675
|
-
} else {
|
|
5676
|
-
throw new Error(ERR_INVALID_COMMENT);
|
|
5677
|
-
}
|
|
5678
6044
|
}
|
|
5679
|
-
|
|
6045
|
+
endOfdirectoryRecord.uint32(END_OF_CENTRAL_DIR_SIGNATURE);
|
|
6046
|
+
endOfdirectoryRecord.uint16(lastDiskNumber);
|
|
6047
|
+
endOfdirectoryRecord.uint16(diskNumber);
|
|
6048
|
+
endOfdirectoryRecord.uint16(filesLength);
|
|
6049
|
+
endOfdirectoryRecord.uint16(filesLength);
|
|
6050
|
+
endOfdirectoryRecord.uint32(directoryDataLength);
|
|
6051
|
+
endOfdirectoryRecord.uint32(directoryOffset);
|
|
6052
|
+
endOfdirectoryRecord.uint16(commentLength);
|
|
6053
|
+
await writeData(writer, endOfdirectoryRecord.array);
|
|
5680
6054
|
if (commentLength) {
|
|
5681
6055
|
await writeData(writer, comment);
|
|
5682
6056
|
}
|
|
5683
6057
|
}
|
|
5684
6058
|
|
|
6059
|
+
function createRecordWriter(length) {
|
|
6060
|
+
const array = new Uint8Array(length);
|
|
6061
|
+
const view = getDataView(array);
|
|
6062
|
+
let offset = 0;
|
|
6063
|
+
return {
|
|
6064
|
+
array,
|
|
6065
|
+
uint8: value => { setUint8(view, offset, value); offset += 1; },
|
|
6066
|
+
uint16: value => { setUint16(view, offset, value); offset += 2; },
|
|
6067
|
+
uint32: value => { setUint32(view, offset, value); offset += 4; },
|
|
6068
|
+
uint64: value => { setBigUint64(view, offset, BigInt(value)); offset += 8; },
|
|
6069
|
+
bytes: value => { arraySet(array, value, offset); offset += getLength(value); },
|
|
6070
|
+
skip: count => offset += count
|
|
6071
|
+
};
|
|
6072
|
+
}
|
|
6073
|
+
|
|
6074
|
+
function getSegmentOffset(zipWriter, writer) {
|
|
6075
|
+
return zipWriter.offset - writer.diskOffset - (writer.diskNumber ? zipWriter.initialOffset : 0);
|
|
6076
|
+
}
|
|
6077
|
+
|
|
5685
6078
|
async function writeData(writer, array) {
|
|
5686
6079
|
const { writable } = writer;
|
|
5687
6080
|
const streamWriter = writable.getWriter();
|
|
@@ -5694,12 +6087,40 @@
|
|
|
5694
6087
|
}
|
|
5695
6088
|
}
|
|
5696
6089
|
|
|
6090
|
+
async function flushBufferedData(readable, writer, signal, onChunkWritten) {
|
|
6091
|
+
const streamWriter = writer.writable.getWriter();
|
|
6092
|
+
try {
|
|
6093
|
+
await readable.pipeTo(new WritableStream({
|
|
6094
|
+
async write(chunk) {
|
|
6095
|
+
await streamWriter.ready;
|
|
6096
|
+
await streamWriter.write(chunk);
|
|
6097
|
+
onChunkWritten(getLength(chunk));
|
|
6098
|
+
}
|
|
6099
|
+
}), { preventClose: true, preventAbort: true, signal });
|
|
6100
|
+
} finally {
|
|
6101
|
+
streamWriter.releaseLock();
|
|
6102
|
+
}
|
|
6103
|
+
}
|
|
6104
|
+
|
|
5697
6105
|
function getTimeNTFS(date) {
|
|
5698
6106
|
if (date) {
|
|
5699
6107
|
return ((BigInt(date.getTime()) + BigInt(11644473600000)) * BigInt(10000));
|
|
5700
6108
|
}
|
|
5701
6109
|
}
|
|
5702
6110
|
|
|
6111
|
+
function getTimeUnix(date) {
|
|
6112
|
+
return Math.floor(date.getTime() / 1000);
|
|
6113
|
+
}
|
|
6114
|
+
|
|
6115
|
+
// the extended timestamp extra field stores dates as signed 32-bit time_t values
|
|
6116
|
+
function inUnixTimeRange(timeUnix) {
|
|
6117
|
+
return timeUnix >= MIN_UNIX_TIME && timeUnix <= MAX_UNIX_TIME;
|
|
6118
|
+
}
|
|
6119
|
+
|
|
6120
|
+
function clampUnixTime(timeUnix) {
|
|
6121
|
+
return Math.min(MAX_UNIX_TIME, Math.max(MIN_UNIX_TIME, timeUnix));
|
|
6122
|
+
}
|
|
6123
|
+
|
|
5703
6124
|
function getOptionValue(zipWriter, options, name, defaultValue) {
|
|
5704
6125
|
const result = options[name] === UNDEFINED_VALUE ? zipWriter.options[name] : options[name];
|
|
5705
6126
|
return result === UNDEFINED_VALUE ? defaultValue : result;
|
|
@@ -5730,7 +6151,7 @@
|
|
|
5730
6151
|
}
|
|
5731
6152
|
|
|
5732
6153
|
function getDataView(array) {
|
|
5733
|
-
return new DataView(array.buffer);
|
|
6154
|
+
return new DataView(array.buffer, array.byteOffset, array.byteLength);
|
|
5734
6155
|
}
|
|
5735
6156
|
|
|
5736
6157
|
function getLength(...arrayLikes) {
|
|
@@ -5746,30 +6167,39 @@
|
|
|
5746
6167
|
uncompressedSize,
|
|
5747
6168
|
compressedSize,
|
|
5748
6169
|
lastModDate,
|
|
6170
|
+
rawLastModDate,
|
|
5749
6171
|
rawFilename,
|
|
5750
6172
|
zip64CompressedSize,
|
|
5751
6173
|
zip64UncompressedSize,
|
|
5752
6174
|
extraFieldLength
|
|
5753
6175
|
}) {
|
|
5754
|
-
const
|
|
6176
|
+
const headerRecord = createRecordWriter(HEADER_SIZE - 4);
|
|
6177
|
+
const headerArray = headerRecord.array;
|
|
5755
6178
|
const headerView = getDataView(headerArray);
|
|
5756
|
-
|
|
5757
|
-
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
|
|
6179
|
+
headerRecord.uint16(version);
|
|
6180
|
+
headerRecord.uint16(bitFlag);
|
|
6181
|
+
headerRecord.uint16(compressionMethod);
|
|
6182
|
+
if (rawLastModDate === UNDEFINED_VALUE) {
|
|
6183
|
+
const dateArray = new Uint32Array(1);
|
|
6184
|
+
const dateView = getDataView(dateArray);
|
|
6185
|
+
setUint16(dateView, 0, (((lastModDate.getHours() << 6) | lastModDate.getMinutes()) << 5) | lastModDate.getSeconds() / 2);
|
|
6186
|
+
setUint16(dateView, 2, ((((lastModDate.getFullYear() - 1980) << 4) | (lastModDate.getMonth() + 1)) << 5) | lastModDate.getDate());
|
|
6187
|
+
rawLastModDate = dateArray[0];
|
|
6188
|
+
}
|
|
6189
|
+
headerRecord.uint32(rawLastModDate);
|
|
6190
|
+
headerRecord.skip(4);
|
|
5765
6191
|
if (zip64CompressedSize || compressedSize !== UNDEFINED_VALUE) {
|
|
5766
|
-
|
|
6192
|
+
headerRecord.uint32(zip64CompressedSize ? MAX_32_BITS : compressedSize);
|
|
6193
|
+
} else {
|
|
6194
|
+
headerRecord.skip(4);
|
|
5767
6195
|
}
|
|
5768
6196
|
if (zip64UncompressedSize || uncompressedSize !== UNDEFINED_VALUE) {
|
|
5769
|
-
|
|
6197
|
+
headerRecord.uint32(zip64UncompressedSize ? MAX_32_BITS : uncompressedSize);
|
|
6198
|
+
} else {
|
|
6199
|
+
headerRecord.skip(4);
|
|
5770
6200
|
}
|
|
5771
|
-
|
|
5772
|
-
|
|
6201
|
+
headerRecord.uint16(getLength(rawFilename));
|
|
6202
|
+
headerRecord.uint16(extraFieldLength);
|
|
5773
6203
|
return {
|
|
5774
6204
|
headerArray,
|
|
5775
6205
|
headerView,
|
|
@@ -5831,12 +6261,15 @@
|
|
|
5831
6261
|
*/
|
|
5832
6262
|
|
|
5833
6263
|
|
|
6264
|
+
const ERR_ENTRY_EXISTS = "Entry filename already exists";
|
|
6265
|
+
const ERR_READABLE_CONSUMED = "Readable stream already consumed";
|
|
6266
|
+
|
|
5834
6267
|
class ZipEntry {
|
|
5835
6268
|
|
|
5836
6269
|
constructor(fs, name, params, parent) {
|
|
5837
6270
|
const zipEntry = this;
|
|
5838
6271
|
if (fs.root && parent && parent.getChildByName(name)) {
|
|
5839
|
-
throw new Error(
|
|
6272
|
+
throw new Error(ERR_ENTRY_EXISTS);
|
|
5840
6273
|
}
|
|
5841
6274
|
if (!params) {
|
|
5842
6275
|
params = {};
|
|
@@ -5846,13 +6279,17 @@
|
|
|
5846
6279
|
name,
|
|
5847
6280
|
data: params.data,
|
|
5848
6281
|
options: params.options,
|
|
5849
|
-
id: fs.
|
|
6282
|
+
id: fs.entryIdCounter++,
|
|
5850
6283
|
parent,
|
|
5851
6284
|
children: [],
|
|
5852
6285
|
uncompressedSize: params.uncompressedSize || 0,
|
|
5853
6286
|
passThrough: params.passThrough
|
|
5854
6287
|
});
|
|
5855
|
-
|
|
6288
|
+
// detached entries (e.g. clones) are not registered until they are attached,
|
|
6289
|
+
// otherwise they could never be garbage collected
|
|
6290
|
+
if (parent || !fs.root) {
|
|
6291
|
+
fs.entries[zipEntry.id] = zipEntry;
|
|
6292
|
+
}
|
|
5856
6293
|
if (parent) {
|
|
5857
6294
|
zipEntry.parent.children.push(zipEntry);
|
|
5858
6295
|
}
|
|
@@ -5890,7 +6327,7 @@
|
|
|
5890
6327
|
rename(name) {
|
|
5891
6328
|
const parent = this.parent;
|
|
5892
6329
|
if (parent && parent.getChildByName(name)) {
|
|
5893
|
-
throw new Error(
|
|
6330
|
+
throw new Error(ERR_ENTRY_EXISTS);
|
|
5894
6331
|
} else {
|
|
5895
6332
|
this.name = name;
|
|
5896
6333
|
}
|
|
@@ -5929,16 +6366,17 @@
|
|
|
5929
6366
|
}
|
|
5930
6367
|
|
|
5931
6368
|
isPasswordProtected() {
|
|
5932
|
-
return this.data.encrypted;
|
|
6369
|
+
return Boolean(this.data && this.data.encrypted);
|
|
5933
6370
|
}
|
|
5934
6371
|
|
|
5935
6372
|
async checkPassword(password, options = {}) {
|
|
5936
6373
|
const zipEntry = this;
|
|
5937
6374
|
if (zipEntry.isPasswordProtected()) {
|
|
5938
|
-
options.password = password;
|
|
5939
|
-
options.checkPasswordOnly = true;
|
|
5940
6375
|
try {
|
|
5941
|
-
await zipEntry.data.getData(null, options
|
|
6376
|
+
await zipEntry.data.getData(null, Object.assign({}, options, {
|
|
6377
|
+
password,
|
|
6378
|
+
checkPasswordOnly: true
|
|
6379
|
+
}));
|
|
5942
6380
|
return true;
|
|
5943
6381
|
} catch (error) {
|
|
5944
6382
|
if (error.message == ERR_INVALID_PASSWORD) {
|
|
@@ -5972,8 +6410,15 @@
|
|
|
5972
6410
|
return this.getData({ writable }, options);
|
|
5973
6411
|
}
|
|
5974
6412
|
|
|
5975
|
-
getArrayBuffer(options) {
|
|
5976
|
-
|
|
6413
|
+
async getArrayBuffer(options) {
|
|
6414
|
+
// this.data may be a string, a Uint8Array, a data URI or a Blob depending on how the entry
|
|
6415
|
+
// was created, so retrieve the bytes through getUint8Array rather than assuming a Blob; this
|
|
6416
|
+
// also applies the options (password, signal, onprogress...) like the other getData() getters
|
|
6417
|
+
const array = await this.getUint8Array(options);
|
|
6418
|
+
// return the backing buffer directly when the array spans it, slicing the exact bytes otherwise
|
|
6419
|
+
return array.byteOffset || array.byteLength != array.buffer.byteLength
|
|
6420
|
+
? array.buffer.slice(array.byteOffset, array.byteOffset + array.byteLength)
|
|
6421
|
+
: array.buffer;
|
|
5977
6422
|
}
|
|
5978
6423
|
|
|
5979
6424
|
replaceBlob(blob) {
|
|
@@ -6015,7 +6460,7 @@
|
|
|
6015
6460
|
replaceReadable(readable) {
|
|
6016
6461
|
Object.assign(this, {
|
|
6017
6462
|
data: null,
|
|
6018
|
-
Reader:
|
|
6463
|
+
Reader: getReadableReader(readable),
|
|
6019
6464
|
Writer: null,
|
|
6020
6465
|
reader: null
|
|
6021
6466
|
});
|
|
@@ -6031,7 +6476,7 @@
|
|
|
6031
6476
|
|
|
6032
6477
|
clone(deepClone) {
|
|
6033
6478
|
const zipEntry = this;
|
|
6034
|
-
const clonedEntry = new ZipDirectoryEntry(zipEntry.fs, zipEntry.name);
|
|
6479
|
+
const clonedEntry = new ZipDirectoryEntry(zipEntry.fs, zipEntry.name, zipEntry);
|
|
6035
6480
|
if (deepClone) {
|
|
6036
6481
|
clonedEntry.children = zipEntry.children.map(child => {
|
|
6037
6482
|
const childClone = child.clone(deepClone);
|
|
@@ -6105,7 +6550,7 @@
|
|
|
6105
6550
|
|
|
6106
6551
|
addReadable(name, readable, options = {}) {
|
|
6107
6552
|
return addChild(this, name, {
|
|
6108
|
-
Reader:
|
|
6553
|
+
Reader: getReadableReader(readable),
|
|
6109
6554
|
options
|
|
6110
6555
|
});
|
|
6111
6556
|
}
|
|
@@ -6119,6 +6564,7 @@
|
|
|
6119
6564
|
}
|
|
6120
6565
|
|
|
6121
6566
|
addFile(file, options = {}) {
|
|
6567
|
+
options = Object.assign({}, options);
|
|
6122
6568
|
if (!options.lastModDate) {
|
|
6123
6569
|
options.lastModDate = new Date(file.lastModified);
|
|
6124
6570
|
}
|
|
@@ -6185,11 +6631,15 @@
|
|
|
6185
6631
|
try {
|
|
6186
6632
|
const path = entry.filename.split("/");
|
|
6187
6633
|
const name = path.pop();
|
|
6188
|
-
path.forEach(
|
|
6634
|
+
path.forEach(pathPart => {
|
|
6189
6635
|
const previousParent = parent;
|
|
6190
6636
|
parent = parent.getChildByName(pathPart);
|
|
6191
|
-
if (
|
|
6192
|
-
|
|
6637
|
+
if (parent) {
|
|
6638
|
+
if (!parent.directory) {
|
|
6639
|
+
throw new Error(ERR_ENTRY_EXISTS);
|
|
6640
|
+
}
|
|
6641
|
+
} else {
|
|
6642
|
+
parent = new ZipDirectoryEntry(this.fs, pathPart, { data: null }, previousParent);
|
|
6193
6643
|
importedEntries.push(parent);
|
|
6194
6644
|
}
|
|
6195
6645
|
});
|
|
@@ -6200,6 +6650,22 @@
|
|
|
6200
6650
|
uncompressedSize: entry.uncompressedSize,
|
|
6201
6651
|
passThrough: options.passThrough
|
|
6202
6652
|
}));
|
|
6653
|
+
} else {
|
|
6654
|
+
let directoryEntry = parent;
|
|
6655
|
+
if (name) {
|
|
6656
|
+
directoryEntry = parent.getChildByName(name);
|
|
6657
|
+
if (directoryEntry) {
|
|
6658
|
+
if (!directoryEntry.directory) {
|
|
6659
|
+
throw new Error(ERR_ENTRY_EXISTS);
|
|
6660
|
+
}
|
|
6661
|
+
} else {
|
|
6662
|
+
directoryEntry = new ZipDirectoryEntry(this.fs, name, { data: null }, parent);
|
|
6663
|
+
importedEntries.push(directoryEntry);
|
|
6664
|
+
}
|
|
6665
|
+
}
|
|
6666
|
+
if (directoryEntry != this && !directoryEntry.data) {
|
|
6667
|
+
directoryEntry.data = entry;
|
|
6668
|
+
}
|
|
6203
6669
|
}
|
|
6204
6670
|
} catch (error) {
|
|
6205
6671
|
try {
|
|
@@ -6215,14 +6681,15 @@
|
|
|
6215
6681
|
return importedEntries;
|
|
6216
6682
|
}
|
|
6217
6683
|
|
|
6218
|
-
async exportZip(writer, options) {
|
|
6684
|
+
async exportZip(writer, options = {}) {
|
|
6219
6685
|
const zipEntry = this;
|
|
6686
|
+
options = Object.assign({}, options);
|
|
6220
6687
|
if (options.bufferedWrite === UNDEFINED_VALUE) {
|
|
6221
6688
|
options.bufferedWrite = true;
|
|
6222
6689
|
}
|
|
6223
|
-
await Promise.all([initReaders(zipEntry, options.readerOptions), initStream(writer)]);
|
|
6690
|
+
const [readers] = await Promise.all([initReaders(zipEntry, options.readerOptions), initStream(writer)]);
|
|
6224
6691
|
const zipWriter = new ZipWriter(writer, options);
|
|
6225
|
-
await exportZip(zipWriter, zipEntry, getTotalSize([zipEntry], "uncompressedSize"), options);
|
|
6692
|
+
await exportZip(zipWriter, zipEntry, getTotalSize([zipEntry], "uncompressedSize"), options, readers);
|
|
6226
6693
|
await zipWriter.close();
|
|
6227
6694
|
return writer.getData ? writer.getData() : writer.writable;
|
|
6228
6695
|
}
|
|
@@ -6268,7 +6735,15 @@
|
|
|
6268
6735
|
|
|
6269
6736
|
remove(entry) {
|
|
6270
6737
|
detach(entry);
|
|
6271
|
-
|
|
6738
|
+
const removedEntries = [entry];
|
|
6739
|
+
while (removedEntries.length) {
|
|
6740
|
+
const removedEntry = removedEntries.pop();
|
|
6741
|
+
this.entries[removedEntry.id] = null;
|
|
6742
|
+
for (const child of removedEntry.children) {
|
|
6743
|
+
removedEntries.push(child);
|
|
6744
|
+
}
|
|
6745
|
+
}
|
|
6746
|
+
entry.parent = UNDEFINED_VALUE;
|
|
6272
6747
|
}
|
|
6273
6748
|
|
|
6274
6749
|
move(entry, destination) {
|
|
@@ -6278,12 +6753,17 @@
|
|
|
6278
6753
|
if (destination.directory) {
|
|
6279
6754
|
if (!destination.isDescendantOf(entry)) {
|
|
6280
6755
|
if (entry != destination) {
|
|
6281
|
-
|
|
6282
|
-
|
|
6756
|
+
const existingChild = destination.getChildByName(entry.name);
|
|
6757
|
+
if (existingChild) {
|
|
6758
|
+
if (existingChild != entry) {
|
|
6759
|
+
throw new Error(ERR_ENTRY_EXISTS);
|
|
6760
|
+
}
|
|
6761
|
+
} else {
|
|
6762
|
+
detach(entry);
|
|
6763
|
+
entry.parent = destination;
|
|
6764
|
+
destination.children.push(entry);
|
|
6765
|
+
registerEntries(this, entry);
|
|
6283
6766
|
}
|
|
6284
|
-
detach(entry);
|
|
6285
|
-
entry.parent = destination;
|
|
6286
|
-
destination.children.push(entry);
|
|
6287
6767
|
}
|
|
6288
6768
|
} else {
|
|
6289
6769
|
throw new Error("Entry is a ancestor of target entry");
|
|
@@ -6300,6 +6780,11 @@
|
|
|
6300
6780
|
for (let index = 0; node && index < path.length; index++) {
|
|
6301
6781
|
node = node.getChildByName(path[index]);
|
|
6302
6782
|
}
|
|
6783
|
+
if (!node) {
|
|
6784
|
+
// exclude detached entries, e.g. clones registered in `entries` but not attached
|
|
6785
|
+
node = this.entries.find(entry => entry && (entry == this.root || entry.isDescendantOf(this.root)) &&
|
|
6786
|
+
entry.getRelativeName() == fullname);
|
|
6787
|
+
}
|
|
6303
6788
|
return node;
|
|
6304
6789
|
}
|
|
6305
6790
|
|
|
@@ -6381,6 +6866,7 @@
|
|
|
6381
6866
|
}
|
|
6382
6867
|
|
|
6383
6868
|
importZip(reader, options) {
|
|
6869
|
+
resetFS(this);
|
|
6384
6870
|
return this.root.importZip(reader, options);
|
|
6385
6871
|
}
|
|
6386
6872
|
|
|
@@ -6400,6 +6886,10 @@
|
|
|
6400
6886
|
return this.root.exportWritable(writable, options);
|
|
6401
6887
|
}
|
|
6402
6888
|
|
|
6889
|
+
exportZip(writer, options) {
|
|
6890
|
+
return this.root.exportZip(writer, options);
|
|
6891
|
+
}
|
|
6892
|
+
|
|
6403
6893
|
isPasswordProtected() {
|
|
6404
6894
|
return this.root.isPasswordProtected();
|
|
6405
6895
|
}
|
|
@@ -6413,15 +6903,26 @@
|
|
|
6413
6903
|
|
|
6414
6904
|
function getTotalSize(entries, propertyName) {
|
|
6415
6905
|
let size = 0;
|
|
6416
|
-
|
|
6906
|
+
const pendingEntries = Array.from(entries);
|
|
6907
|
+
while (pendingEntries.length) {
|
|
6908
|
+
const entry = pendingEntries.pop();
|
|
6909
|
+
size += entry[propertyName] || 0;
|
|
6910
|
+
for (const child of entry.children) {
|
|
6911
|
+
pendingEntries.push(child);
|
|
6912
|
+
}
|
|
6913
|
+
}
|
|
6417
6914
|
return size;
|
|
6915
|
+
}
|
|
6418
6916
|
|
|
6419
|
-
|
|
6420
|
-
|
|
6421
|
-
|
|
6422
|
-
|
|
6917
|
+
function getReadableReader(readable) {
|
|
6918
|
+
let consumed;
|
|
6919
|
+
return function () {
|
|
6920
|
+
if (consumed) {
|
|
6921
|
+
throw new Error(ERR_READABLE_CONSUMED);
|
|
6423
6922
|
}
|
|
6424
|
-
|
|
6923
|
+
consumed = true;
|
|
6924
|
+
return { readable };
|
|
6925
|
+
};
|
|
6425
6926
|
}
|
|
6426
6927
|
|
|
6427
6928
|
function getZipBlobReader(options) {
|
|
@@ -6436,7 +6937,7 @@
|
|
|
6436
6937
|
async init() {
|
|
6437
6938
|
const zipBlobReader = this;
|
|
6438
6939
|
zipBlobReader.size = zipBlobReader.entry.uncompressedSize;
|
|
6439
|
-
const data = await zipBlobReader.entry.getData(new BlobWriter(), Object.assign({},
|
|
6940
|
+
const data = await zipBlobReader.entry.getData(new BlobWriter(), Object.assign({}, options, zipBlobReader.options));
|
|
6440
6941
|
zipBlobReader.data = data;
|
|
6441
6942
|
zipBlobReader.blobReader = new BlobReader(data);
|
|
6442
6943
|
super.init();
|
|
@@ -6449,29 +6950,41 @@
|
|
|
6449
6950
|
}
|
|
6450
6951
|
|
|
6451
6952
|
async function initReaders(entry, options) {
|
|
6452
|
-
|
|
6453
|
-
|
|
6953
|
+
const fileEntries = [];
|
|
6954
|
+
const pendingEntries = [entry];
|
|
6955
|
+
// per-export map so concurrent exports do not share (and lock) the same readers
|
|
6956
|
+
const readers = new Map();
|
|
6957
|
+
while (pendingEntries.length) {
|
|
6958
|
+
const pendingEntry = pendingEntries.pop();
|
|
6959
|
+
for (const child of pendingEntry.children) {
|
|
6454
6960
|
if (child.directory) {
|
|
6455
|
-
|
|
6961
|
+
pendingEntries.push(child);
|
|
6456
6962
|
} else {
|
|
6457
|
-
|
|
6458
|
-
try {
|
|
6459
|
-
await initStream(reader);
|
|
6460
|
-
} catch (error) {
|
|
6461
|
-
try {
|
|
6462
|
-
error.entryId = child.id;
|
|
6463
|
-
error.cause = {
|
|
6464
|
-
entry: child
|
|
6465
|
-
};
|
|
6466
|
-
} catch {
|
|
6467
|
-
// ignored
|
|
6468
|
-
}
|
|
6469
|
-
throw error;
|
|
6470
|
-
}
|
|
6471
|
-
child.uncompressedSize = reader.size;
|
|
6963
|
+
fileEntries.push(child);
|
|
6472
6964
|
}
|
|
6473
|
-
}
|
|
6965
|
+
}
|
|
6474
6966
|
}
|
|
6967
|
+
await Promise.all(fileEntries.map(async child => {
|
|
6968
|
+
const reader = child.reader = new child.Reader(child.data, options);
|
|
6969
|
+
readers.set(child, reader);
|
|
6970
|
+
try {
|
|
6971
|
+
await initStream(reader);
|
|
6972
|
+
} catch (error) {
|
|
6973
|
+
try {
|
|
6974
|
+
error.entryId = child.id;
|
|
6975
|
+
error.cause = {
|
|
6976
|
+
entry: child
|
|
6977
|
+
};
|
|
6978
|
+
} catch {
|
|
6979
|
+
// ignored
|
|
6980
|
+
}
|
|
6981
|
+
throw error;
|
|
6982
|
+
}
|
|
6983
|
+
if (reader.size !== UNDEFINED_VALUE) {
|
|
6984
|
+
child.uncompressedSize = reader.size;
|
|
6985
|
+
}
|
|
6986
|
+
}));
|
|
6987
|
+
return readers;
|
|
6475
6988
|
}
|
|
6476
6989
|
|
|
6477
6990
|
function detach(entry) {
|
|
@@ -6485,7 +6998,7 @@
|
|
|
6485
6998
|
}
|
|
6486
6999
|
}
|
|
6487
7000
|
|
|
6488
|
-
async function exportZip(zipWriter, entry, totalSize, options) {
|
|
7001
|
+
async function exportZip(zipWriter, entry, totalSize, options, readers) {
|
|
6489
7002
|
const selectedEntry = entry;
|
|
6490
7003
|
const entryOffsets = new Map();
|
|
6491
7004
|
await process(zipWriter, entry);
|
|
@@ -6495,7 +7008,11 @@
|
|
|
6495
7008
|
|
|
6496
7009
|
async function exportChild() {
|
|
6497
7010
|
if (options.bufferedWrite) {
|
|
6498
|
-
await Promise.allSettled(entry.children.map(processChild));
|
|
7011
|
+
const results = await Promise.allSettled(entry.children.map(processChild));
|
|
7012
|
+
const errorResult = results.find(result => result.status == "rejected");
|
|
7013
|
+
if (errorResult) {
|
|
7014
|
+
throw errorResult.reason;
|
|
7015
|
+
}
|
|
6499
7016
|
} else {
|
|
6500
7017
|
for (const child of entry.children) {
|
|
6501
7018
|
await processChild(child);
|
|
@@ -6550,7 +7067,7 @@
|
|
|
6550
7067
|
});
|
|
6551
7068
|
}
|
|
6552
7069
|
}
|
|
6553
|
-
await zipWriter.add(name, child
|
|
7070
|
+
await zipWriter.add(name, readers.get(child), Object.assign({}, options, zipEntryOptions, childOptions, {
|
|
6554
7071
|
directory: child.directory,
|
|
6555
7072
|
onprogress: async indexProgress => {
|
|
6556
7073
|
if (options.onprogress) {
|
|
@@ -6599,7 +7116,7 @@
|
|
|
6599
7116
|
}
|
|
6600
7117
|
} catch (error) {
|
|
6601
7118
|
const message = error.message + (handle ? " (" + handle.name + ")" : "");
|
|
6602
|
-
throw new Error(message);
|
|
7119
|
+
throw new Error(message, { cause: error });
|
|
6603
7120
|
}
|
|
6604
7121
|
}
|
|
6605
7122
|
return addedEntries;
|
|
@@ -6659,9 +7176,21 @@
|
|
|
6659
7176
|
|
|
6660
7177
|
function resetFS(fs) {
|
|
6661
7178
|
fs.entries = [];
|
|
7179
|
+
fs.entryIdCounter = 0;
|
|
6662
7180
|
fs.root = new ZipDirectoryEntry(fs);
|
|
6663
7181
|
}
|
|
6664
7182
|
|
|
7183
|
+
function registerEntries(fs, entry) {
|
|
7184
|
+
const pendingEntries = [entry];
|
|
7185
|
+
while (pendingEntries.length) {
|
|
7186
|
+
const pendingEntry = pendingEntries.pop();
|
|
7187
|
+
fs.entries[pendingEntry.id] = pendingEntry;
|
|
7188
|
+
for (const child of pendingEntry.children) {
|
|
7189
|
+
pendingEntries.push(child);
|
|
7190
|
+
}
|
|
7191
|
+
}
|
|
7192
|
+
}
|
|
7193
|
+
|
|
6665
7194
|
function addChild(parent, name, params, directory) {
|
|
6666
7195
|
if (parent.directory) {
|
|
6667
7196
|
return directory ? new ZipDirectoryEntry(parent.fs, name, params, parent) : new ZipFileEntry(parent.fs, name, params, parent);
|