@zip.js/zip.js 2.7.70 → 2.7.72
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/deno.json +10 -2
- package/dist/z-worker-fflate.js +1 -1
- package/dist/z-worker-pako.js +1 -1
- package/dist/z-worker.js +1 -1
- package/dist/zip-fs-full.js +269 -107
- package/dist/zip-fs-full.min.js +1 -1
- package/dist/zip-fs.js +269 -107
- package/dist/zip-fs.min.js +1 -1
- package/dist/zip-full.js +268 -106
- package/dist/zip-full.min.js +1 -1
- package/dist/zip-no-worker-deflate.min.js +1 -1
- package/dist/zip-no-worker-inflate.min.js +1 -1
- package/dist/zip-no-worker.min.js +1 -1
- package/dist/zip.js +268 -106
- package/dist/zip.min.js +1 -1
- package/eslint.config.mjs +1 -1
- package/index-fflate.js +1 -0
- package/index.cjs +269 -107
- package/index.d.ts +22 -1
- package/index.js +1 -0
- package/index.min.js +1 -1
- package/lib/core/constants.js +8 -1
- package/lib/core/options.js +89 -0
- package/lib/core/streams/zip-crypto-stream.js +1 -1
- package/lib/core/streams/zip-entry-stream.js +5 -20
- package/lib/core/zip-entry.js +17 -6
- package/lib/core/zip-fs-core.js +1 -1
- package/lib/core/zip-reader.js +173 -46
- package/lib/core/zip-writer.js +69 -37
- package/lib/z-worker-inline.js +1 -1
- package/package.json +4 -4
package/lib/core/constants.js
CHANGED
|
@@ -45,6 +45,10 @@ const ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH = 20;
|
|
|
45
45
|
const ZIP64_END_OF_CENTRAL_DIR_LENGTH = 56;
|
|
46
46
|
const ZIP64_END_OF_CENTRAL_DIR_TOTAL_LENGTH = END_OF_CENTRAL_DIR_LENGTH + ZIP64_END_OF_CENTRAL_DIR_LOCATOR_LENGTH + ZIP64_END_OF_CENTRAL_DIR_LENGTH;
|
|
47
47
|
|
|
48
|
+
const DATA_DESCRIPTOR_RECORD_LENGTH = 12;
|
|
49
|
+
const DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH = 20;
|
|
50
|
+
const DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH = 4;
|
|
51
|
+
|
|
48
52
|
const EXTRAFIELD_TYPE_ZIP64 = 0x0001;
|
|
49
53
|
const EXTRAFIELD_TYPE_AES = 0x9901;
|
|
50
54
|
const EXTRAFIELD_TYPE_NTFS = 0x000a;
|
|
@@ -73,7 +77,7 @@ const VERSION_AES = 0x33;
|
|
|
73
77
|
|
|
74
78
|
const DIRECTORY_SIGNATURE = "/";
|
|
75
79
|
|
|
76
|
-
const HEADER_SIZE =
|
|
80
|
+
const HEADER_SIZE = 30;
|
|
77
81
|
const HEADER_OFFSET_SIGNATURE = 10;
|
|
78
82
|
const HEADER_OFFSET_COMPRESSED_SIZE = 14;
|
|
79
83
|
const HEADER_OFFSET_UNCOMPRESSED_SIZE = 18;
|
|
@@ -99,6 +103,9 @@ export {
|
|
|
99
103
|
END_OF_CENTRAL_DIR_SIGNATURE,
|
|
100
104
|
ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIGNATURE,
|
|
101
105
|
ZIP64_END_OF_CENTRAL_DIR_SIGNATURE,
|
|
106
|
+
DATA_DESCRIPTOR_RECORD_LENGTH,
|
|
107
|
+
DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH,
|
|
108
|
+
DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH,
|
|
102
109
|
EXTRAFIELD_TYPE_ZIP64,
|
|
103
110
|
EXTRAFIELD_TYPE_AES,
|
|
104
111
|
EXTRAFIELD_TYPE_NTFS,
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (c) 2022 Gildas Lormeau. All rights reserved.
|
|
3
|
+
|
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
|
6
|
+
|
|
7
|
+
1. Redistributions of source code must retain the above copyright notice,
|
|
8
|
+
this list of conditions and the following disclaimer.
|
|
9
|
+
|
|
10
|
+
2. Redistributions in binary form must reproduce the above copyright
|
|
11
|
+
notice, this list of conditions and the following disclaimer in
|
|
12
|
+
the documentation and/or other materials provided with the distribution.
|
|
13
|
+
|
|
14
|
+
3. The names of the authors may not be used to endorse or promote products
|
|
15
|
+
derived from this software without specific prior written permission.
|
|
16
|
+
|
|
17
|
+
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
|
18
|
+
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
|
20
|
+
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
21
|
+
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
22
|
+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
|
23
|
+
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
24
|
+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
25
|
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
26
|
+
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
const OPTION_FILENAME_ENCODING = "filenameEncoding";
|
|
30
|
+
const OPTION_COMMENT_ENCODING = "commentEncoding";
|
|
31
|
+
const OPTION_DECODE_TEXT = "decodeText";
|
|
32
|
+
const OPTION_EXTRACT_PREPENDED_DATA = "extractPrependedData";
|
|
33
|
+
const OPTION_EXTRACT_APPENDED_DATA = "extractAppendedData";
|
|
34
|
+
const OPTION_PASSWORD = "password";
|
|
35
|
+
const OPTION_RAW_PASSWORD = "rawPassword";
|
|
36
|
+
const OPTION_PASS_THROUGH = "passThrough";
|
|
37
|
+
const OPTION_SIGNAL = "signal";
|
|
38
|
+
const OPTION_CHECK_PASSWORD_ONLY = "checkPasswordOnly";
|
|
39
|
+
const OPTION_CHECK_OVERLAPPING_ENTRY_ONLY = "checkOverlappingEntryOnly";
|
|
40
|
+
const OPTION_CHECK_OVERLAPPING_ENTRY = "checkOverlappingEntry";
|
|
41
|
+
const OPTION_CHECK_SIGNATURE = "checkSignature";
|
|
42
|
+
const OPTION_USE_WEB_WORKERS = "useWebWorkers";
|
|
43
|
+
const OPTION_USE_COMPRESSION_STREAM = "useCompressionStream";
|
|
44
|
+
const OPTION_TRANSFER_STREAMS = "transferStreams";
|
|
45
|
+
const OPTION_PREVENT_CLOSE = "preventClose";
|
|
46
|
+
const OPTION_ENCRYPTION_STRENGTH = "encryptionStrength";
|
|
47
|
+
const OPTION_EXTENDED_TIMESTAMP = "extendedTimestamp";
|
|
48
|
+
const OPTION_KEEP_ORDER = "keepOrder";
|
|
49
|
+
const OPTION_LEVEL = "level";
|
|
50
|
+
const OPTION_BUFFERED_WRITE = "bufferedWrite";
|
|
51
|
+
const OPTION_DATA_DESCRIPTOR_SIGNATURE = "dataDescriptorSignature";
|
|
52
|
+
const OPTION_USE_UNICODE_FILE_NAMES = "useUnicodeFileNames";
|
|
53
|
+
const OPTION_DATA_DESCRIPTOR = "dataDescriptor";
|
|
54
|
+
const OPTION_SUPPORT_ZIP64_SPLIT_FILE = "supportZip64SplitFile";
|
|
55
|
+
const OPTION_ENCODE_TEXT = "encodeText";
|
|
56
|
+
const OPTION_OFFSET = "offset";
|
|
57
|
+
const OPTION_USDZ = "usdz";
|
|
58
|
+
|
|
59
|
+
export {
|
|
60
|
+
OPTION_FILENAME_ENCODING,
|
|
61
|
+
OPTION_COMMENT_ENCODING,
|
|
62
|
+
OPTION_DECODE_TEXT,
|
|
63
|
+
OPTION_EXTRACT_PREPENDED_DATA,
|
|
64
|
+
OPTION_EXTRACT_APPENDED_DATA,
|
|
65
|
+
OPTION_PASSWORD,
|
|
66
|
+
OPTION_RAW_PASSWORD,
|
|
67
|
+
OPTION_PASS_THROUGH,
|
|
68
|
+
OPTION_SIGNAL,
|
|
69
|
+
OPTION_CHECK_PASSWORD_ONLY,
|
|
70
|
+
OPTION_CHECK_OVERLAPPING_ENTRY_ONLY,
|
|
71
|
+
OPTION_CHECK_OVERLAPPING_ENTRY,
|
|
72
|
+
OPTION_CHECK_SIGNATURE,
|
|
73
|
+
OPTION_USE_WEB_WORKERS,
|
|
74
|
+
OPTION_USE_COMPRESSION_STREAM,
|
|
75
|
+
OPTION_TRANSFER_STREAMS,
|
|
76
|
+
OPTION_PREVENT_CLOSE,
|
|
77
|
+
OPTION_ENCRYPTION_STRENGTH,
|
|
78
|
+
OPTION_EXTENDED_TIMESTAMP,
|
|
79
|
+
OPTION_KEEP_ORDER,
|
|
80
|
+
OPTION_LEVEL,
|
|
81
|
+
OPTION_BUFFERED_WRITE,
|
|
82
|
+
OPTION_DATA_DESCRIPTOR_SIGNATURE,
|
|
83
|
+
OPTION_USE_UNICODE_FILE_NAMES,
|
|
84
|
+
OPTION_DATA_DESCRIPTOR,
|
|
85
|
+
OPTION_SUPPORT_ZIP64_SPLIT_FILE,
|
|
86
|
+
OPTION_ENCODE_TEXT,
|
|
87
|
+
OPTION_OFFSET,
|
|
88
|
+
OPTION_USDZ
|
|
89
|
+
};
|
|
@@ -131,7 +131,7 @@ function createKeys(target, password) {
|
|
|
131
131
|
Object.assign(target, {
|
|
132
132
|
keys,
|
|
133
133
|
crcKey0: new Crc32(keys[0]),
|
|
134
|
-
crcKey2: new Crc32(keys[2])
|
|
134
|
+
crcKey2: new Crc32(keys[2])
|
|
135
135
|
});
|
|
136
136
|
for (let index = 0; index < password.length; index++) {
|
|
137
137
|
updateKeys(target, password.charCodeAt(index));
|
|
@@ -54,7 +54,7 @@ class DeflateStream extends TransformStream {
|
|
|
54
54
|
const { compressed, encrypted, useCompressionStream, zipCrypto, signed, level } = options;
|
|
55
55
|
const stream = this;
|
|
56
56
|
let crc32Stream, encryptionStream;
|
|
57
|
-
let readable =
|
|
57
|
+
let readable = super.readable;
|
|
58
58
|
if ((!encrypted || zipCrypto) && signed) {
|
|
59
59
|
crc32Stream = new Crc32Stream();
|
|
60
60
|
readable = pipeThrough(readable, crc32Stream);
|
|
@@ -89,7 +89,7 @@ class InflateStream extends TransformStream {
|
|
|
89
89
|
super({});
|
|
90
90
|
const { zipCrypto, encrypted, signed, signature, compressed, useCompressionStream } = options;
|
|
91
91
|
let crc32Stream, decryptionStream;
|
|
92
|
-
let readable =
|
|
92
|
+
let readable = super.readable;
|
|
93
93
|
if (encrypted) {
|
|
94
94
|
if (zipCrypto) {
|
|
95
95
|
readable = pipeThrough(readable, new ZipCryptoDecryptionStream(options));
|
|
@@ -125,16 +125,6 @@ export {
|
|
|
125
125
|
ERR_ABORT_CHECK_PASSWORD
|
|
126
126
|
};
|
|
127
127
|
|
|
128
|
-
function filterEmptyChunks(readable) {
|
|
129
|
-
return pipeThrough(readable, new TransformStream({
|
|
130
|
-
transform(chunk, controller) {
|
|
131
|
-
if (chunk && chunk.length) {
|
|
132
|
-
controller.enqueue(chunk);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}));
|
|
136
|
-
}
|
|
137
|
-
|
|
138
128
|
function setReadable(stream, readable, flush) {
|
|
139
129
|
readable = pipeThrough(readable, new TransformStream({ flush }));
|
|
140
130
|
Object.defineProperty(stream, "readable", {
|
|
@@ -149,16 +139,11 @@ function pipeThroughCommpressionStream(readable, useCompressionStream, options,
|
|
|
149
139
|
const CompressionStream = useCompressionStream && CodecStreamNative ? CodecStreamNative : CodecStream;
|
|
150
140
|
readable = pipeThrough(readable, new CompressionStream(COMPRESSION_FORMAT, options));
|
|
151
141
|
// eslint-disable-next-line no-unused-vars
|
|
152
|
-
} catch (
|
|
142
|
+
} catch (error) {
|
|
153
143
|
if (useCompressionStream) {
|
|
154
|
-
|
|
155
|
-
readable = pipeThrough(readable, new CodecStream(COMPRESSION_FORMAT, options));
|
|
156
|
-
// eslint-disable-next-line no-unused-vars
|
|
157
|
-
} catch (_) {
|
|
158
|
-
return readable;
|
|
159
|
-
}
|
|
144
|
+
readable = pipeThrough(readable, new CodecStream(COMPRESSION_FORMAT, options));
|
|
160
145
|
} else {
|
|
161
|
-
|
|
146
|
+
throw error;
|
|
162
147
|
}
|
|
163
148
|
}
|
|
164
149
|
return readable;
|
package/lib/core/zip-entry.js
CHANGED
|
@@ -30,8 +30,8 @@ const PROPERTY_NAME_FILENAME = "filename";
|
|
|
30
30
|
const PROPERTY_NAME_RAW_FILENAME = "rawFilename";
|
|
31
31
|
const PROPERTY_NAME_COMMENT = "comment";
|
|
32
32
|
const PROPERTY_NAME_RAW_COMMENT = "rawComment";
|
|
33
|
-
const
|
|
34
|
-
const
|
|
33
|
+
const PROPERTY_NAME_UNCOMPRESSED_SIZE = "uncompressedSize";
|
|
34
|
+
const PROPERTY_NAME_COMPRESSED_SIZE = "compressedSize";
|
|
35
35
|
const PROPERTY_NAME_OFFSET = "offset";
|
|
36
36
|
const PROPERTY_NAME_DISK_NUMBER_START = "diskNumberStart";
|
|
37
37
|
const PROPERTY_NAME_LAST_MODIFICATION_DATE = "lastModDate";
|
|
@@ -52,12 +52,20 @@ const PROPERTY_NAME_VERSION_MADE_BY = "versionMadeBy";
|
|
|
52
52
|
const PROPERTY_NAME_ZIPCRYPTO = "zipCrypto";
|
|
53
53
|
const PROPERTY_NAME_DIRECTORY = "directory";
|
|
54
54
|
const PROPERTY_NAME_EXECUTABLE = "executable";
|
|
55
|
+
const PROPERTY_NAME_COMPRESSION_METHOD = "compressionMethod";
|
|
56
|
+
const PROPERTY_NAME_SIGNATURE = "signature";
|
|
57
|
+
const PROPERTY_NAME_EXTRA_FIELD = "extraField";
|
|
55
58
|
|
|
56
59
|
const PROPERTY_NAMES = [
|
|
57
|
-
PROPERTY_NAME_FILENAME, PROPERTY_NAME_RAW_FILENAME,
|
|
60
|
+
PROPERTY_NAME_FILENAME, PROPERTY_NAME_RAW_FILENAME, PROPERTY_NAME_COMPRESSED_SIZE, PROPERTY_NAME_UNCOMPRESSED_SIZE,
|
|
58
61
|
PROPERTY_NAME_LAST_MODIFICATION_DATE, PROPERTY_NAME_RAW_LAST_MODIFICATION_DATE, PROPERTY_NAME_COMMENT, PROPERTY_NAME_RAW_COMMENT,
|
|
59
62
|
PROPERTY_NAME_LAST_ACCESS_DATE, PROPERTY_NAME_CREATION_DATE, PROPERTY_NAME_OFFSET, PROPERTY_NAME_DISK_NUMBER_START,
|
|
60
|
-
PROPERTY_NAME_DISK_NUMBER_START, PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTE, PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTES,
|
|
63
|
+
PROPERTY_NAME_DISK_NUMBER_START, PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTE, PROPERTY_NAME_INTERNAL_FILE_ATTRIBUTES,
|
|
64
|
+
PROPERTY_NAME_EXTERNAL_FILE_ATTRIBUTE, PROPERTY_NAME_EXTERNAL_FILE_ATTRIBUTES, PROPERTY_NAME_MS_DOS_COMPATIBLE, PROPERTY_NAME_ZIP64,
|
|
65
|
+
PROPERTY_NAME_ENCRYPTED, PROPERTY_NAME_VERSION, PROPERTY_NAME_VERSION_MADE_BY, PROPERTY_NAME_ZIPCRYPTO, PROPERTY_NAME_DIRECTORY,
|
|
66
|
+
PROPERTY_NAME_EXECUTABLE, PROPERTY_NAME_COMPRESSION_METHOD, PROPERTY_NAME_SIGNATURE, PROPERTY_NAME_EXTRA_FIELD,
|
|
67
|
+
"bitFlag", "filenameUTF8", "commentUTF8", "rawExtraField", "extraFieldZip64", "extraFieldUnicodePath", "extraFieldUnicodeComment",
|
|
68
|
+
"extraFieldAES", "extraFieldNTFS", "extraFieldExtendedTimestamp"];
|
|
61
69
|
|
|
62
70
|
class Entry {
|
|
63
71
|
|
|
@@ -72,8 +80,8 @@ export {
|
|
|
72
80
|
PROPERTY_NAME_RAW_FILENAME,
|
|
73
81
|
PROPERTY_NAME_COMMENT,
|
|
74
82
|
PROPERTY_NAME_RAW_COMMENT,
|
|
75
|
-
|
|
76
|
-
|
|
83
|
+
PROPERTY_NAME_UNCOMPRESSED_SIZE,
|
|
84
|
+
PROPERTY_NAME_COMPRESSED_SIZE,
|
|
77
85
|
PROPERTY_NAME_OFFSET,
|
|
78
86
|
PROPERTY_NAME_DISK_NUMBER_START,
|
|
79
87
|
PROPERTY_NAME_LAST_MODIFICATION_DATE,
|
|
@@ -94,5 +102,8 @@ export {
|
|
|
94
102
|
PROPERTY_NAME_ZIPCRYPTO,
|
|
95
103
|
PROPERTY_NAME_DIRECTORY,
|
|
96
104
|
PROPERTY_NAME_EXECUTABLE,
|
|
105
|
+
PROPERTY_NAME_COMPRESSION_METHOD,
|
|
106
|
+
PROPERTY_NAME_SIGNATURE,
|
|
107
|
+
PROPERTY_NAME_EXTRA_FIELD,
|
|
97
108
|
Entry
|
|
98
109
|
};
|
package/lib/core/zip-fs-core.js
CHANGED
package/lib/core/zip-reader.js
CHANGED
|
@@ -36,6 +36,10 @@ import {
|
|
|
36
36
|
COMPRESSION_METHOD_STORE,
|
|
37
37
|
COMPRESSION_METHOD_AES,
|
|
38
38
|
SPLIT_ZIP_FILE_SIGNATURE,
|
|
39
|
+
DATA_DESCRIPTOR_RECORD_SIGNATURE,
|
|
40
|
+
DATA_DESCRIPTOR_RECORD_LENGTH,
|
|
41
|
+
DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH,
|
|
42
|
+
DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH,
|
|
39
43
|
LOCAL_FILE_HEADER_SIGNATURE,
|
|
40
44
|
CENTRAL_FILE_HEADER_SIGNATURE,
|
|
41
45
|
END_OF_CENTRAL_DIR_SIGNATURE,
|
|
@@ -93,8 +97,8 @@ import {
|
|
|
93
97
|
PROPERTY_NAME_FILENAME,
|
|
94
98
|
PROPERTY_NAME_RAW_COMMENT,
|
|
95
99
|
PROPERTY_NAME_COMMENT,
|
|
96
|
-
|
|
97
|
-
|
|
100
|
+
PROPERTY_NAME_UNCOMPRESSED_SIZE,
|
|
101
|
+
PROPERTY_NAME_COMPRESSED_SIZE,
|
|
98
102
|
PROPERTY_NAME_OFFSET,
|
|
99
103
|
PROPERTY_NAME_DISK_NUMBER_START,
|
|
100
104
|
PROPERTY_NAME_LAST_MODIFICATION_DATE,
|
|
@@ -105,6 +109,25 @@ import {
|
|
|
105
109
|
PROPERTY_NAME_RAW_CREATION_DATE,
|
|
106
110
|
Entry
|
|
107
111
|
} from "./zip-entry.js";
|
|
112
|
+
import {
|
|
113
|
+
OPTION_FILENAME_ENCODING,
|
|
114
|
+
OPTION_COMMENT_ENCODING,
|
|
115
|
+
OPTION_DECODE_TEXT,
|
|
116
|
+
OPTION_EXTRACT_PREPENDED_DATA,
|
|
117
|
+
OPTION_EXTRACT_APPENDED_DATA,
|
|
118
|
+
OPTION_PASSWORD,
|
|
119
|
+
OPTION_RAW_PASSWORD,
|
|
120
|
+
OPTION_PASS_THROUGH,
|
|
121
|
+
OPTION_SIGNAL,
|
|
122
|
+
OPTION_CHECK_PASSWORD_ONLY,
|
|
123
|
+
OPTION_CHECK_OVERLAPPING_ENTRY_ONLY,
|
|
124
|
+
OPTION_CHECK_OVERLAPPING_ENTRY,
|
|
125
|
+
OPTION_CHECK_SIGNATURE,
|
|
126
|
+
OPTION_USE_WEB_WORKERS,
|
|
127
|
+
OPTION_USE_COMPRESSION_STREAM,
|
|
128
|
+
OPTION_TRANSFER_STREAMS,
|
|
129
|
+
OPTION_PREVENT_CLOSE
|
|
130
|
+
} from "./options.js";
|
|
108
131
|
|
|
109
132
|
const ERR_BAD_FORMAT = "File format is not recognized";
|
|
110
133
|
const ERR_EOCDR_NOT_FOUND = "End of central directory not found";
|
|
@@ -116,11 +139,12 @@ const ERR_ENCRYPTED = "File contains encrypted entry";
|
|
|
116
139
|
const ERR_UNSUPPORTED_ENCRYPTION = "Encryption method not supported";
|
|
117
140
|
const ERR_UNSUPPORTED_COMPRESSION = "Compression method not supported";
|
|
118
141
|
const ERR_SPLIT_ZIP_FILE = "Split zip file";
|
|
142
|
+
const ERR_OVERLAPPING_ENTRY = "Overlapping entry found";
|
|
119
143
|
const CHARSET_UTF8 = "utf-8";
|
|
120
144
|
const CHARSET_CP437 = "cp437";
|
|
121
145
|
const ZIP64_PROPERTIES = [
|
|
122
|
-
[
|
|
123
|
-
[
|
|
146
|
+
[PROPERTY_NAME_UNCOMPRESSED_SIZE, MAX_32_BITS],
|
|
147
|
+
[PROPERTY_NAME_COMPRESSED_SIZE, MAX_32_BITS],
|
|
124
148
|
[PROPERTY_NAME_OFFSET, MAX_32_BITS],
|
|
125
149
|
[PROPERTY_NAME_DISK_NUMBER_START, MAX_16_BITS]
|
|
126
150
|
];
|
|
@@ -141,7 +165,8 @@ class ZipReader {
|
|
|
141
165
|
Object.assign(this, {
|
|
142
166
|
reader: new GenericReader(reader),
|
|
143
167
|
options,
|
|
144
|
-
config: getConfiguration()
|
|
168
|
+
config: getConfiguration(),
|
|
169
|
+
readRanges: []
|
|
145
170
|
});
|
|
146
171
|
}
|
|
147
172
|
|
|
@@ -191,7 +216,9 @@ class ZipReader {
|
|
|
191
216
|
if (getUint32(endOfDirectoryView, 0) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE && directoryDataOffset != expectedDirectoryDataOffset) {
|
|
192
217
|
const originalDirectoryDataOffset = directoryDataOffset;
|
|
193
218
|
directoryDataOffset = expectedDirectoryDataOffset;
|
|
194
|
-
|
|
219
|
+
if (directoryDataOffset > originalDirectoryDataOffset) {
|
|
220
|
+
prependedDataLength = directoryDataOffset - originalDirectoryDataOffset;
|
|
221
|
+
}
|
|
195
222
|
endOfDirectoryArray = await readUint8Array(reader, directoryDataOffset, ZIP64_END_OF_CENTRAL_DIR_LENGTH, -1);
|
|
196
223
|
endOfDirectoryView = getDataView(endOfDirectoryArray);
|
|
197
224
|
}
|
|
@@ -231,7 +258,9 @@ class ZipReader {
|
|
|
231
258
|
if (getUint32(directoryView, offset) != CENTRAL_FILE_HEADER_SIGNATURE && directoryDataOffset != expectedDirectoryDataOffset) {
|
|
232
259
|
const originalDirectoryDataOffset = directoryDataOffset;
|
|
233
260
|
directoryDataOffset = expectedDirectoryDataOffset;
|
|
234
|
-
|
|
261
|
+
if (directoryDataOffset > originalDirectoryDataOffset) {
|
|
262
|
+
prependedDataLength += directoryDataOffset - originalDirectoryDataOffset;
|
|
263
|
+
}
|
|
235
264
|
directoryArray = await readUint8Array(reader, directoryDataOffset, directoryDataLength, diskNumber);
|
|
236
265
|
directoryView = getDataView(directoryArray);
|
|
237
266
|
}
|
|
@@ -245,8 +274,8 @@ class ZipReader {
|
|
|
245
274
|
if (directoryDataOffset < 0 || directoryDataOffset >= reader.size) {
|
|
246
275
|
throw new Error(ERR_BAD_FORMAT);
|
|
247
276
|
}
|
|
248
|
-
const filenameEncoding = getOptionValue(zipReader, options,
|
|
249
|
-
const commentEncoding = getOptionValue(zipReader, options,
|
|
277
|
+
const filenameEncoding = getOptionValue(zipReader, options, OPTION_FILENAME_ENCODING);
|
|
278
|
+
const commentEncoding = getOptionValue(zipReader, options, OPTION_COMMENT_ENCODING);
|
|
250
279
|
for (let indexFile = 0; indexFile < filesLength; indexFile++) {
|
|
251
280
|
const fileEntry = new ZipEntry(reader, config, zipReader.options);
|
|
252
281
|
if (getUint32(directoryView, offset) != CENTRAL_FILE_HEADER_SIGNATURE) {
|
|
@@ -292,7 +321,7 @@ class ZipReader {
|
|
|
292
321
|
});
|
|
293
322
|
fileEntry.internalFileAttribute = fileEntry.internalFileAttributes;
|
|
294
323
|
fileEntry.externalFileAttribute = fileEntry.externalFileAttributes;
|
|
295
|
-
const decode = getOptionValue(zipReader, options,
|
|
324
|
+
const decode = getOptionValue(zipReader, options, OPTION_DECODE_TEXT) || decodeText;
|
|
296
325
|
const rawFilenameEncoding = filenameUTF8 ? CHARSET_UTF8 : filenameEncoding || CHARSET_CP437;
|
|
297
326
|
const rawCommentEncoding = commentUTF8 ? CHARSET_UTF8 : commentEncoding || CHARSET_CP437;
|
|
298
327
|
let filename = decode(rawFilename, rawFilenameEncoding);
|
|
@@ -313,12 +342,12 @@ class ZipReader {
|
|
|
313
342
|
readCommonFooter(fileEntry, fileEntry, directoryView, offset + 6);
|
|
314
343
|
fileEntry.zipCrypto = fileEntry.encrypted && !fileEntry.extraFieldAES;
|
|
315
344
|
const entry = new Entry(fileEntry);
|
|
316
|
-
entry.getData = (writer, options) => fileEntry.getData(writer, entry, options);
|
|
345
|
+
entry.getData = (writer, options) => fileEntry.getData(writer, entry, zipReader.readRanges, options);
|
|
317
346
|
entry.arrayBuffer = async options => {
|
|
318
347
|
const writer = new TransformStream();
|
|
319
348
|
const [arrayBuffer] = await Promise.all([
|
|
320
349
|
new Response(writer.readable).arrayBuffer(),
|
|
321
|
-
fileEntry.getData(writer, entry, options)]);
|
|
350
|
+
fileEntry.getData(writer, entry, zipReader.readRanges, options)]);
|
|
322
351
|
return arrayBuffer;
|
|
323
352
|
};
|
|
324
353
|
offset = endOffset;
|
|
@@ -333,8 +362,8 @@ class ZipReader {
|
|
|
333
362
|
}
|
|
334
363
|
yield entry;
|
|
335
364
|
}
|
|
336
|
-
const extractPrependedData = getOptionValue(zipReader, options,
|
|
337
|
-
const extractAppendedData = getOptionValue(zipReader, options,
|
|
365
|
+
const extractPrependedData = getOptionValue(zipReader, options, OPTION_EXTRACT_PREPENDED_DATA);
|
|
366
|
+
const extractAppendedData = getOptionValue(zipReader, options, OPTION_EXTRACT_APPENDED_DATA);
|
|
338
367
|
if (extractPrependedData) {
|
|
339
368
|
zipReader.prependedData = startOffset > 0 ? await readUint8Array(reader, 0, startOffset) : new Uint8Array();
|
|
340
369
|
}
|
|
@@ -400,7 +429,8 @@ export {
|
|
|
400
429
|
ERR_INVALID_SIGNATURE,
|
|
401
430
|
ERR_INVALID_PASSWORD,
|
|
402
431
|
ERR_INVALID_UNCOMPRESSED_SIZE,
|
|
403
|
-
ERR_SPLIT_ZIP_FILE
|
|
432
|
+
ERR_SPLIT_ZIP_FILE,
|
|
433
|
+
ERR_OVERLAPPING_ENTRY
|
|
404
434
|
};
|
|
405
435
|
|
|
406
436
|
class ZipEntry {
|
|
@@ -413,13 +443,14 @@ class ZipEntry {
|
|
|
413
443
|
});
|
|
414
444
|
}
|
|
415
445
|
|
|
416
|
-
async getData(writer, fileEntry, options = {}) {
|
|
446
|
+
async getData(writer, fileEntry, readRanges, options = {}) {
|
|
417
447
|
const zipEntry = this;
|
|
418
448
|
const {
|
|
419
449
|
reader,
|
|
420
450
|
offset,
|
|
421
451
|
diskNumberStart,
|
|
422
452
|
extraFieldAES,
|
|
453
|
+
extraFieldZip64,
|
|
423
454
|
compressionMethod,
|
|
424
455
|
config,
|
|
425
456
|
bitFlag,
|
|
@@ -428,13 +459,15 @@ class ZipEntry {
|
|
|
428
459
|
uncompressedSize,
|
|
429
460
|
compressedSize
|
|
430
461
|
} = zipEntry;
|
|
462
|
+
const {
|
|
463
|
+
dataDescriptor
|
|
464
|
+
} = bitFlag;
|
|
431
465
|
const localDirectory = fileEntry.localDirectory = {};
|
|
432
|
-
const
|
|
433
|
-
const dataArray = await readUint8Array(reader, offset, dataArraySize + 4, diskNumberStart);
|
|
466
|
+
const dataArray = await readUint8Array(reader, offset, HEADER_SIZE, diskNumberStart);
|
|
434
467
|
const dataView = getDataView(dataArray);
|
|
435
|
-
let password = getOptionValue(zipEntry, options,
|
|
436
|
-
let rawPassword = getOptionValue(zipEntry, options,
|
|
437
|
-
const passThrough = getOptionValue(zipEntry, options,
|
|
468
|
+
let password = getOptionValue(zipEntry, options, OPTION_PASSWORD);
|
|
469
|
+
let rawPassword = getOptionValue(zipEntry, options, OPTION_RAW_PASSWORD);
|
|
470
|
+
const passThrough = getOptionValue(zipEntry, options, OPTION_PASS_THROUGH);
|
|
438
471
|
password = password && password.length && password;
|
|
439
472
|
rawPassword = rawPassword && rawPassword.length && rawPassword;
|
|
440
473
|
if (extraFieldAES) {
|
|
@@ -449,14 +482,17 @@ class ZipEntry {
|
|
|
449
482
|
throw new Error(ERR_LOCAL_FILE_HEADER_NOT_FOUND);
|
|
450
483
|
}
|
|
451
484
|
readCommonHeader(localDirectory, dataView, 4);
|
|
452
|
-
|
|
453
|
-
|
|
485
|
+
const {
|
|
486
|
+
extraFieldLength,
|
|
487
|
+
filenameLength,
|
|
488
|
+
lastAccessDate,
|
|
489
|
+
creationDate
|
|
490
|
+
} = localDirectory;
|
|
491
|
+
localDirectory.rawExtraField = extraFieldLength ?
|
|
492
|
+
await readUint8Array(reader, offset + HEADER_SIZE + filenameLength, extraFieldLength, diskNumberStart) :
|
|
454
493
|
new Uint8Array();
|
|
455
494
|
readCommonFooter(zipEntry, localDirectory, dataView, 4, true);
|
|
456
|
-
Object.assign(fileEntry, {
|
|
457
|
-
lastAccessDate: localDirectory.lastAccessDate,
|
|
458
|
-
creationDate: localDirectory.creationDate
|
|
459
|
-
});
|
|
495
|
+
Object.assign(fileEntry, { lastAccessDate, creationDate });
|
|
460
496
|
const encrypted = zipEntry.encrypted && localDirectory.encrypted && !passThrough;
|
|
461
497
|
const zipCrypto = encrypted && !extraFieldAES;
|
|
462
498
|
if (!passThrough) {
|
|
@@ -469,7 +505,7 @@ class ZipEntry {
|
|
|
469
505
|
throw new Error(ERR_ENCRYPTED);
|
|
470
506
|
}
|
|
471
507
|
}
|
|
472
|
-
const dataOffset = offset +
|
|
508
|
+
const dataOffset = offset + HEADER_SIZE + filenameLength + extraFieldLength;
|
|
473
509
|
const size = compressedSize;
|
|
474
510
|
const readable = reader.readable;
|
|
475
511
|
Object.assign(readable, {
|
|
@@ -477,14 +513,13 @@ class ZipEntry {
|
|
|
477
513
|
offset: dataOffset,
|
|
478
514
|
size
|
|
479
515
|
});
|
|
480
|
-
const signal = getOptionValue(zipEntry, options,
|
|
481
|
-
const checkPasswordOnly = getOptionValue(zipEntry, options,
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
const { writable } = writer;
|
|
516
|
+
const signal = getOptionValue(zipEntry, options, OPTION_SIGNAL);
|
|
517
|
+
const checkPasswordOnly = getOptionValue(zipEntry, options, OPTION_CHECK_PASSWORD_ONLY);
|
|
518
|
+
let checkOverlappingEntry = getOptionValue(zipEntry, options, OPTION_CHECK_OVERLAPPING_ENTRY);
|
|
519
|
+
const checkOverlappingEntryOnly = getOptionValue(zipEntry, options, OPTION_CHECK_OVERLAPPING_ENTRY_ONLY);
|
|
520
|
+
if (checkOverlappingEntryOnly) {
|
|
521
|
+
checkOverlappingEntry = true;
|
|
522
|
+
}
|
|
488
523
|
const { onstart, onprogress, onend } = options;
|
|
489
524
|
const workerOptions = {
|
|
490
525
|
options: {
|
|
@@ -493,23 +528,50 @@ class ZipEntry {
|
|
|
493
528
|
rawPassword,
|
|
494
529
|
zipCrypto,
|
|
495
530
|
encryptionStrength: extraFieldAES && extraFieldAES.strength,
|
|
496
|
-
signed: getOptionValue(zipEntry, options,
|
|
497
|
-
passwordVerification: zipCrypto && (
|
|
531
|
+
signed: getOptionValue(zipEntry, options, OPTION_CHECK_SIGNATURE) && !passThrough,
|
|
532
|
+
passwordVerification: zipCrypto && (dataDescriptor ? ((rawLastModDate >>> 8) & 0xFF) : ((signature >>> 24) & 0xFF)),
|
|
498
533
|
outputSize: uncompressedSize,
|
|
499
534
|
signature,
|
|
500
535
|
compressed: compressionMethod != 0 && !passThrough,
|
|
501
536
|
encrypted: zipEntry.encrypted && !passThrough,
|
|
502
|
-
useWebWorkers: getOptionValue(zipEntry, options,
|
|
503
|
-
useCompressionStream: getOptionValue(zipEntry, options,
|
|
504
|
-
transferStreams: getOptionValue(zipEntry, options,
|
|
537
|
+
useWebWorkers: getOptionValue(zipEntry, options, OPTION_USE_WEB_WORKERS),
|
|
538
|
+
useCompressionStream: getOptionValue(zipEntry, options, OPTION_USE_COMPRESSION_STREAM),
|
|
539
|
+
transferStreams: getOptionValue(zipEntry, options, OPTION_TRANSFER_STREAMS),
|
|
505
540
|
checkPasswordOnly
|
|
506
541
|
},
|
|
507
542
|
config,
|
|
508
543
|
streamOptions: { signal, size, onstart, onprogress, onend }
|
|
509
544
|
};
|
|
545
|
+
if (checkOverlappingEntry) {
|
|
546
|
+
await detectOverlappingEntry({
|
|
547
|
+
reader,
|
|
548
|
+
fileEntry,
|
|
549
|
+
offset,
|
|
550
|
+
diskNumberStart,
|
|
551
|
+
signature,
|
|
552
|
+
compressedSize,
|
|
553
|
+
uncompressedSize,
|
|
554
|
+
dataOffset,
|
|
555
|
+
dataDescriptor: dataDescriptor || localDirectory.bitFlag.dataDescriptor,
|
|
556
|
+
extraFieldZip64: extraFieldZip64 || localDirectory.extraFieldZip64,
|
|
557
|
+
readRanges
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
let writable;
|
|
510
561
|
try {
|
|
511
|
-
|
|
512
|
-
|
|
562
|
+
if (!checkOverlappingEntryOnly) {
|
|
563
|
+
if (checkPasswordOnly) {
|
|
564
|
+
writer = new WritableStream();
|
|
565
|
+
}
|
|
566
|
+
writer = new GenericWriter(writer);
|
|
567
|
+
await initStream(writer, passThrough ? compressedSize : uncompressedSize);
|
|
568
|
+
({ writable } = writer);
|
|
569
|
+
const { outputSize } = await runWorker({ readable, writable }, workerOptions);
|
|
570
|
+
writer.size += outputSize;
|
|
571
|
+
if (outputSize != (passThrough ? compressedSize : uncompressedSize)) {
|
|
572
|
+
throw new Error(ERR_INVALID_UNCOMPRESSED_SIZE);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
513
575
|
} catch (error) {
|
|
514
576
|
if (error.outputSize !== UNDEFINED_VALUE) {
|
|
515
577
|
writer.size += error.outputSize;
|
|
@@ -518,12 +580,12 @@ class ZipEntry {
|
|
|
518
580
|
throw error;
|
|
519
581
|
}
|
|
520
582
|
} finally {
|
|
521
|
-
const preventClose = getOptionValue(zipEntry, options,
|
|
522
|
-
if (!preventClose && !writable.locked) {
|
|
583
|
+
const preventClose = getOptionValue(zipEntry, options, OPTION_PREVENT_CLOSE);
|
|
584
|
+
if (!preventClose && writable && !writable.locked) {
|
|
523
585
|
await writable.getWriter().close();
|
|
524
586
|
}
|
|
525
587
|
}
|
|
526
|
-
return checkPasswordOnly ? UNDEFINED_VALUE : writer.getData ? writer.getData() : writable;
|
|
588
|
+
return checkPasswordOnly || checkOverlappingEntryOnly ? UNDEFINED_VALUE : writer.getData ? writer.getData() : writable;
|
|
527
589
|
}
|
|
528
590
|
}
|
|
529
591
|
|
|
@@ -731,6 +793,71 @@ function readExtraFieldExtendedTimestamp(extraFieldExtendedTimestamp, directory,
|
|
|
731
793
|
});
|
|
732
794
|
}
|
|
733
795
|
|
|
796
|
+
async function detectOverlappingEntry({
|
|
797
|
+
reader,
|
|
798
|
+
fileEntry,
|
|
799
|
+
offset,
|
|
800
|
+
diskNumberStart,
|
|
801
|
+
signature,
|
|
802
|
+
compressedSize,
|
|
803
|
+
uncompressedSize,
|
|
804
|
+
dataOffset,
|
|
805
|
+
dataDescriptor,
|
|
806
|
+
extraFieldZip64,
|
|
807
|
+
readRanges
|
|
808
|
+
}) {
|
|
809
|
+
let diskOffset = 0;
|
|
810
|
+
if (diskNumberStart) {
|
|
811
|
+
for (let indexReader = 0; indexReader < diskNumberStart; indexReader++) {
|
|
812
|
+
const diskReader = reader.readers[indexReader];
|
|
813
|
+
diskOffset += diskReader.size;
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
let dataDescriptorLength = 0;
|
|
817
|
+
if (dataDescriptor) {
|
|
818
|
+
if (extraFieldZip64) {
|
|
819
|
+
dataDescriptorLength = DATA_DESCRIPTOR_RECORD_ZIP_64_LENGTH;
|
|
820
|
+
} else {
|
|
821
|
+
dataDescriptorLength = DATA_DESCRIPTOR_RECORD_LENGTH;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
if (dataDescriptorLength) {
|
|
825
|
+
const dataDescriptorArray = await readUint8Array(reader, dataOffset + compressedSize, dataDescriptorLength + DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH, diskNumberStart);
|
|
826
|
+
const dataDescriptorSignature = getUint32(getDataView(dataDescriptorArray), 0) == DATA_DESCRIPTOR_RECORD_SIGNATURE;
|
|
827
|
+
if (dataDescriptorSignature) {
|
|
828
|
+
const readSignature = getUint32(getDataView(dataDescriptorArray), 4);
|
|
829
|
+
let readCompressedSize;
|
|
830
|
+
let readUncompressedSize;
|
|
831
|
+
if (extraFieldZip64) {
|
|
832
|
+
readCompressedSize = getBigUint64(getDataView(dataDescriptorArray), 8);
|
|
833
|
+
readUncompressedSize = getBigUint64(getDataView(dataDescriptorArray), 16);
|
|
834
|
+
} else {
|
|
835
|
+
readCompressedSize = getUint32(getDataView(dataDescriptorArray), 8);
|
|
836
|
+
readUncompressedSize = getUint32(getDataView(dataDescriptorArray), 12);
|
|
837
|
+
}
|
|
838
|
+
const matchSignature = (fileEntry.encrypted && !fileEntry.zipCrypto) || readSignature == signature;
|
|
839
|
+
if (matchSignature &&
|
|
840
|
+
readCompressedSize == compressedSize &&
|
|
841
|
+
readUncompressedSize == uncompressedSize) {
|
|
842
|
+
dataDescriptorLength += DATA_DESCRIPTOR_RECORD_SIGNATURE_LENGTH;
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
const range = {
|
|
847
|
+
start: diskOffset + offset,
|
|
848
|
+
end: diskOffset + dataOffset + compressedSize + dataDescriptorLength,
|
|
849
|
+
fileEntry
|
|
850
|
+
};
|
|
851
|
+
for (const otherRange of readRanges) {
|
|
852
|
+
if (otherRange.fileEntry != fileEntry && range.start >= otherRange.start && range.start < otherRange.end) {
|
|
853
|
+
const error = new Error(ERR_OVERLAPPING_ENTRY);
|
|
854
|
+
error.overlappingEntry = otherRange.fileEntry;
|
|
855
|
+
throw error;
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
readRanges.push(range);
|
|
859
|
+
}
|
|
860
|
+
|
|
734
861
|
async function seekSignature(reader, signature, startOffset, minimumBytes, maximumLength) {
|
|
735
862
|
const signatureArray = new Uint8Array(4);
|
|
736
863
|
const signatureView = getDataView(signatureArray);
|