Skip to content

Commit 8176b02

Browse files
authored
Merge pull request #301 from RocketChat/zip64
[WIP] Zip64 files and forEach method
2 parents ff17ae8 + 0a4e8ca commit 8176b02

File tree

5 files changed

+151
-17
lines changed

5 files changed

+151
-17
lines changed

adm-zip.js

+8
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,14 @@ module.exports = function (/*String*/input) {
339339
return getEntry(name);
340340
},
341341

342+
getEntryCount: function() {
343+
return _zip.getEntryCount();
344+
},
345+
346+
forEach: function(callback) {
347+
return _zip.forEach(callback);
348+
},
349+
342350
/**
343351
* Extracts the given entry to the given targetPath
344352
* If the entry is a directory inside the archive, the entire directory and it's subdirectories will be extracted

headers/mainHeader.js

+29-11
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,37 @@ module.exports = function () {
3131

3232
loadFromBinary : function(/*Buffer*/data) {
3333
// data should be 22 bytes and start with "PK 05 06"
34-
if (data.length !== Constants.ENDHDR || data.readUInt32LE(0) !== Constants.ENDSIG)
34+
// or be 56+ bytes and start with "PK 06 06" for Zip64
35+
if ((data.length !== Constants.ENDHDR || data.readUInt32LE(0) !== Constants.ENDSIG) &&
36+
(data.length < Constants.ZIP64HDR || data.readUInt32LE(0) !== Constants.ZIP64SIG)) {
37+
3538
throw Utils.Errors.INVALID_END;
39+
}
40+
41+
if (data.readUInt32LE(0) === Constants.ENDSIG) {
42+
// number of entries on this volume
43+
_volumeEntries = data.readUInt16LE(Constants.ENDSUB);
44+
// total number of entries
45+
_totalEntries = data.readUInt16LE(Constants.ENDTOT);
46+
// central directory size in bytes
47+
_size = data.readUInt32LE(Constants.ENDSIZ);
48+
// offset of first CEN header
49+
_offset = data.readUInt32LE(Constants.ENDOFF);
50+
// zip file comment length
51+
_commentLength = data.readUInt16LE(Constants.ENDCOM);
52+
} else {
53+
// number of entries on this volume
54+
_volumeEntries = Utils.readBigUInt64LE(data, Constants.ZIP64SUB);
55+
// total number of entries
56+
_totalEntries = Utils.readBigUInt64LE(data, Constants.ZIP64TOT);
57+
// central directory size in bytes
58+
_size = Utils.readBigUInt64LE(data, Constants.ZIP64SIZ);
59+
// offset of first CEN header
60+
_offset = Utils.readBigUInt64LE(data, Constants.ZIP64OFF);
61+
62+
_commentLength = 0;
63+
}
3664

37-
// number of entries on this volume
38-
_volumeEntries = data.readUInt16LE(Constants.ENDSUB);
39-
// total number of entries
40-
_totalEntries = data.readUInt16LE(Constants.ENDTOT);
41-
// central directory size in bytes
42-
_size = data.readUInt32LE(Constants.ENDSIZ);
43-
// offset of first CEN header
44-
_offset = data.readUInt32LE(Constants.ENDOFF);
45-
// zip file comment length
46-
_commentLength = data.readUInt16LE(Constants.ENDCOM);
4765
},
4866

4967
toBinary : function() {

util/constants.js

+20
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,26 @@ module.exports = {
4747
ENDOFF : 16, // offset of first CEN header
4848
ENDCOM : 20, // zip file comment length
4949

50+
END64HDR : 20, // zip64 END header size
51+
END64SIG : 0x07064b50, // zip64 Locator signature, "PK\006\007"
52+
END64START : 4, // number of the disk with the start of the zip64
53+
END64OFF : 8, // relative offset of the zip64 end of central directory
54+
END64NUMDISKS : 16, // total number of disks
55+
56+
ZIP64SIG : 0x06064b50, // zip64 signature, "PK\006\006"
57+
ZIP64HDR : 56, // zip64 record minimum size
58+
ZIP64LEAD : 12, // leading bytes at the start of the record, not counted by the value stored in ZIP64SIZE
59+
ZIP64SIZE : 4, // zip64 size of the central directory record
60+
ZIP64VEM : 12, // zip64 version made by
61+
ZIP64VER : 14, // zip64 version needed to extract
62+
ZIP64DSK : 16, // zip64 number of this disk
63+
ZIP64DSKDIR : 20, // number of the disk with the start of the record directory
64+
ZIP64SUB : 24, // number of entries on this disk
65+
ZIP64TOT : 32, // total number of entries
66+
ZIP64SIZB : 40, // zip64 central directory size in bytes
67+
ZIP64OFF : 48, // offset of start of central directory with respect to the starting disk number
68+
ZIP64EXTRA : 56, // extensible data sector
69+
5070
/* Compression methods */
5171
STORED : 0, // no compression
5272
SHRUNK : 1, // shrunk

util/utils.js

+9
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@ module.exports = (function() {
4848
return files;
4949
}
5050

51+
function readBigUInt64LE(/*Buffer*/buffer, /*int*/index) {
52+
var slice = Buffer.from(buffer.slice(index, index + 8));
53+
slice.swap64();
54+
55+
return parseInt(`0x${ slice.toString('hex') }`);
56+
}
57+
5158
return {
5259
makeDir : function(/*String*/path) {
5360
mkdirSync(path);
@@ -203,6 +210,8 @@ module.exports = (function() {
203210
}
204211
},
205212

213+
readBigUInt64LE,
214+
206215
Constants : Constants,
207216
Errors : Errors
208217
}

zipFile.js

+85-6
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ module.exports = function (/*String|Buffer*/input, /*Number*/inputType) {
99
filename = "",
1010
fs = Utils.FileSystem.require(),
1111
inBuffer = null,
12-
mainHeader = new Headers.MainHeader();
12+
mainHeader = new Headers.MainHeader(),
13+
loadedEntries = false;
1314

1415
if (inputType === Utils.Constants.FILE) {
1516
// is a filename
@@ -22,9 +23,28 @@ module.exports = function (/*String|Buffer*/input, /*Number*/inputType) {
2223
readMainHeader();
2324
} else {
2425
// none. is a new file
26+
loadedEntries = true;
27+
}
28+
29+
function iterateEntries(callback) {
30+
const totalEntries = mainHeader.diskEntries; // total number of entries
31+
let index = mainHeader.offset; // offset of first CEN header
32+
33+
for (let i = 0; i < totalEntries; i++) {
34+
let tmp = index;
35+
const entry = new ZipEntry(inBuffer);
36+
37+
entry.header = inBuffer.slice(tmp, tmp += Utils.Constants.CENHDR);
38+
entry.entryName = inBuffer.slice(tmp, tmp += entry.header.fileNameLength);
39+
40+
index += entry.header.entryHeaderSize;
41+
42+
callback(entry);
43+
}
2544
}
2645

2746
function readEntries() {
47+
loadedEntries = true;
2848
entryTable = {};
2949
entryList = new Array(mainHeader.diskEntries); // total number of entries
3050
var index = mainHeader.offset; // offset of first CEN header
@@ -52,24 +72,45 @@ module.exports = function (/*String|Buffer*/input, /*Number*/inputType) {
5272

5373
function readMainHeader() {
5474
var i = inBuffer.length - Utils.Constants.ENDHDR, // END header size
55-
n = Math.max(0, i - 0xFFFF), // 0xFFFF is the max zip file comment length
56-
endOffset = -1; // Start offset of the END header
75+
max = Math.max(0, i - 0xFFFF), // 0xFFFF is the max zip file comment length
76+
n = max,
77+
endStart = inBuffer.length,
78+
endOffset = -1, // Start offset of the END header
79+
commentEnd = 0;
5780

5881
for (i; i >= n; i--) {
5982
if (inBuffer[i] !== 0x50) continue; // quick check that the byte is 'P'
6083
if (inBuffer.readUInt32LE(i) === Utils.Constants.ENDSIG) { // "PK\005\006"
6184
endOffset = i;
85+
commentEnd = i;
86+
endStart = i + Utils.Constants.ENDHDR;
87+
// We already found a regular signature, let's look just a bit further to check if there's any zip64 signature
88+
n = i - Utils.Constants.END64HDR;
89+
continue;
90+
}
91+
92+
if (inBuffer.readUInt32LE(i) === Utils.Constants.END64SIG) {
93+
// Found a zip64 signature, let's continue reading the whole zip64 record
94+
n = max;
95+
continue;
96+
}
97+
98+
if (inBuffer.readUInt32LE(i) == Utils.Constants.ZIP64SIG) {
99+
// Found the zip64 record, let's determine it's size
100+
endOffset = i;
101+
endStart = i + Utils.readBigUInt64LE(inBuffer, i + Utils.Constants.ZIP64SIZE) + Utils.Constants.ZIP64LEAD;
62102
break;
63103
}
64104
}
105+
65106
if (!~endOffset)
66107
throw Utils.Errors.INVALID_FORMAT;
67108

68-
mainHeader.loadFromBinary(inBuffer.slice(endOffset, endOffset + Utils.Constants.ENDHDR));
109+
mainHeader.loadFromBinary(inBuffer.slice(endOffset, endStart));
69110
if (mainHeader.commentLength) {
70-
_comment = inBuffer.slice(endOffset + Utils.Constants.ENDHDR);
111+
_comment = inBuffer.slice(commentEnd + Utils.Constants.ENDHDR);
71112
}
72-
readEntries();
113+
// readEntries();
73114
}
74115

75116
return {
@@ -78,6 +119,9 @@ module.exports = function (/*String|Buffer*/input, /*Number*/inputType) {
78119
* @return Array
79120
*/
80121
get entries() {
122+
if (!loadedEntries) {
123+
readEntries();
124+
}
81125
return entryList;
82126
},
83127

@@ -93,13 +137,33 @@ module.exports = function (/*String|Buffer*/input, /*Number*/inputType) {
93137
_comment = val;
94138
},
95139

140+
getEntryCount: function() {
141+
if (!loadedEntries) {
142+
return mainHeader.diskEntries;
143+
}
144+
145+
return entryList.length;
146+
},
147+
148+
forEach: function(callback) {
149+
if (!loadedEntries) {
150+
iterateEntries(callback);
151+
return;
152+
}
153+
154+
entryList.forEach(callback);
155+
},
156+
96157
/**
97158
* Returns a reference to the entry with the given name or null if entry is inexistent
98159
*
99160
* @param entryName
100161
* @return ZipEntry
101162
*/
102163
getEntry: function (/*String*/entryName) {
164+
if (!loadedEntries) {
165+
readEntries();
166+
}
103167
return entryTable[entryName] || null;
104168
},
105169

@@ -109,6 +173,9 @@ module.exports = function (/*String|Buffer*/input, /*Number*/inputType) {
109173
* @param entry
110174
*/
111175
setEntry: function (/*ZipEntry*/entry) {
176+
if (!loadedEntries) {
177+
readEntries();
178+
}
112179
entryList.push(entry);
113180
entryTable[entry.entryName] = entry;
114181
mainHeader.totalEntries = entryList.length;
@@ -121,6 +188,9 @@ module.exports = function (/*String|Buffer*/input, /*Number*/inputType) {
121188
* @param entryName
122189
*/
123190
deleteEntry: function (/*String*/entryName) {
191+
if (!loadedEntries) {
192+
readEntries();
193+
}
124194
var entry = entryTable[entryName];
125195
if (entry && entry.isDirectory) {
126196
var _self = this;
@@ -142,6 +212,9 @@ module.exports = function (/*String|Buffer*/input, /*Number*/inputType) {
142212
* @return Array
143213
*/
144214
getEntryChildren: function (/*ZipEntry*/entry) {
215+
if (!loadedEntries) {
216+
readEntries();
217+
}
145218
if (entry.isDirectory) {
146219
var list = [],
147220
name = entry.entryName,
@@ -163,6 +236,9 @@ module.exports = function (/*String|Buffer*/input, /*Number*/inputType) {
163236
* @return Buffer
164237
*/
165238
compressToBuffer: function () {
239+
if (!loadedEntries) {
240+
readEntries();
241+
}
166242
if (entryList.length > 1) {
167243
entryList.sort(function (a, b) {
168244
var nameA = a.entryName.toLowerCase();
@@ -237,6 +313,9 @@ module.exports = function (/*String|Buffer*/input, /*Number*/inputType) {
237313
},
238314

239315
toAsyncBuffer: function (/*Function*/onSuccess, /*Function*/onFail, /*Function*/onItemStart, /*Function*/onItemEnd) {
316+
if (!loadedEntries) {
317+
readEntries();
318+
}
240319
if (entryList.length > 1) {
241320
entryList.sort(function (a, b) {
242321
var nameA = a.entryName.toLowerCase();

0 commit comments

Comments
 (0)