Show More
Commit Description:
add model solution
Commit Description:
add model solution
References:
File last commit:
Show/Diff file:
Action:
node_modules/jszip/lib/zipEntries.js
| 262 lines
| 11.6 KiB
| application/javascript
| JavascriptLexer
|
r789 | 'use strict'; | |||
var readerFor = require('./reader/readerFor'); | ||||
var utils = require('./utils'); | ||||
var sig = require('./signature'); | ||||
var ZipEntry = require('./zipEntry'); | ||||
var utf8 = require('./utf8'); | ||||
var support = require('./support'); | ||||
// class ZipEntries {{{ | ||||
/** | ||||
* All the entries in the zip file. | ||||
* @constructor | ||||
* @param {Object} loadOptions Options for loading the stream. | ||||
*/ | ||||
function ZipEntries(loadOptions) { | ||||
this.files = []; | ||||
this.loadOptions = loadOptions; | ||||
} | ||||
ZipEntries.prototype = { | ||||
/** | ||||
* Check that the reader is on the specified signature. | ||||
* @param {string} expectedSignature the expected signature. | ||||
* @throws {Error} if it is an other signature. | ||||
*/ | ||||
checkSignature: function(expectedSignature) { | ||||
if (!this.reader.readAndCheckSignature(expectedSignature)) { | ||||
this.reader.index -= 4; | ||||
var signature = this.reader.readString(4); | ||||
throw new Error("Corrupted zip or bug: unexpected signature " + "(" + utils.pretty(signature) + ", expected " + utils.pretty(expectedSignature) + ")"); | ||||
} | ||||
}, | ||||
/** | ||||
* Check if the given signature is at the given index. | ||||
* @param {number} askedIndex the index to check. | ||||
* @param {string} expectedSignature the signature to expect. | ||||
* @return {boolean} true if the signature is here, false otherwise. | ||||
*/ | ||||
isSignature: function(askedIndex, expectedSignature) { | ||||
var currentIndex = this.reader.index; | ||||
this.reader.setIndex(askedIndex); | ||||
var signature = this.reader.readString(4); | ||||
var result = signature === expectedSignature; | ||||
this.reader.setIndex(currentIndex); | ||||
return result; | ||||
}, | ||||
/** | ||||
* Read the end of the central directory. | ||||
*/ | ||||
readBlockEndOfCentral: function() { | ||||
this.diskNumber = this.reader.readInt(2); | ||||
this.diskWithCentralDirStart = this.reader.readInt(2); | ||||
this.centralDirRecordsOnThisDisk = this.reader.readInt(2); | ||||
this.centralDirRecords = this.reader.readInt(2); | ||||
this.centralDirSize = this.reader.readInt(4); | ||||
this.centralDirOffset = this.reader.readInt(4); | ||||
this.zipCommentLength = this.reader.readInt(2); | ||||
// warning : the encoding depends of the system locale | ||||
// On a linux machine with LANG=en_US.utf8, this field is utf8 encoded. | ||||
// On a windows machine, this field is encoded with the localized windows code page. | ||||
var zipComment = this.reader.readData(this.zipCommentLength); | ||||
var decodeParamType = support.uint8array ? "uint8array" : "array"; | ||||
// To get consistent behavior with the generation part, we will assume that | ||||
// this is utf8 encoded unless specified otherwise. | ||||
var decodeContent = utils.transformTo(decodeParamType, zipComment); | ||||
this.zipComment = this.loadOptions.decodeFileName(decodeContent); | ||||
}, | ||||
/** | ||||
* Read the end of the Zip 64 central directory. | ||||
* Not merged with the method readEndOfCentral : | ||||
* The end of central can coexist with its Zip64 brother, | ||||
* I don't want to read the wrong number of bytes ! | ||||
*/ | ||||
readBlockZip64EndOfCentral: function() { | ||||
this.zip64EndOfCentralSize = this.reader.readInt(8); | ||||
this.reader.skip(4); | ||||
// this.versionMadeBy = this.reader.readString(2); | ||||
// this.versionNeeded = this.reader.readInt(2); | ||||
this.diskNumber = this.reader.readInt(4); | ||||
this.diskWithCentralDirStart = this.reader.readInt(4); | ||||
this.centralDirRecordsOnThisDisk = this.reader.readInt(8); | ||||
this.centralDirRecords = this.reader.readInt(8); | ||||
this.centralDirSize = this.reader.readInt(8); | ||||
this.centralDirOffset = this.reader.readInt(8); | ||||
this.zip64ExtensibleData = {}; | ||||
var extraDataSize = this.zip64EndOfCentralSize - 44, | ||||
index = 0, | ||||
extraFieldId, | ||||
extraFieldLength, | ||||
extraFieldValue; | ||||
while (index < extraDataSize) { | ||||
extraFieldId = this.reader.readInt(2); | ||||
extraFieldLength = this.reader.readInt(4); | ||||
extraFieldValue = this.reader.readData(extraFieldLength); | ||||
this.zip64ExtensibleData[extraFieldId] = { | ||||
id: extraFieldId, | ||||
length: extraFieldLength, | ||||
value: extraFieldValue | ||||
}; | ||||
} | ||||
}, | ||||
/** | ||||
* Read the end of the Zip 64 central directory locator. | ||||
*/ | ||||
readBlockZip64EndOfCentralLocator: function() { | ||||
this.diskWithZip64CentralDirStart = this.reader.readInt(4); | ||||
this.relativeOffsetEndOfZip64CentralDir = this.reader.readInt(8); | ||||
this.disksCount = this.reader.readInt(4); | ||||
if (this.disksCount > 1) { | ||||
throw new Error("Multi-volumes zip are not supported"); | ||||
} | ||||
}, | ||||
/** | ||||
* Read the local files, based on the offset read in the central part. | ||||
*/ | ||||
readLocalFiles: function() { | ||||
var i, file; | ||||
for (i = 0; i < this.files.length; i++) { | ||||
file = this.files[i]; | ||||
this.reader.setIndex(file.localHeaderOffset); | ||||
this.checkSignature(sig.LOCAL_FILE_HEADER); | ||||
file.readLocalPart(this.reader); | ||||
file.handleUTF8(); | ||||
file.processAttributes(); | ||||
} | ||||
}, | ||||
/** | ||||
* Read the central directory. | ||||
*/ | ||||
readCentralDir: function() { | ||||
var file; | ||||
this.reader.setIndex(this.centralDirOffset); | ||||
while (this.reader.readAndCheckSignature(sig.CENTRAL_FILE_HEADER)) { | ||||
file = new ZipEntry({ | ||||
zip64: this.zip64 | ||||
}, this.loadOptions); | ||||
file.readCentralPart(this.reader); | ||||
this.files.push(file); | ||||
} | ||||
if (this.centralDirRecords !== this.files.length) { | ||||
if (this.centralDirRecords !== 0 && this.files.length === 0) { | ||||
// We expected some records but couldn't find ANY. | ||||
// This is really suspicious, as if something went wrong. | ||||
throw new Error("Corrupted zip or bug: expected " + this.centralDirRecords + " records in central dir, got " + this.files.length); | ||||
} else { | ||||
// We found some records but not all. | ||||
// Something is wrong but we got something for the user: no error here. | ||||
// console.warn("expected", this.centralDirRecords, "records in central dir, got", this.files.length); | ||||
} | ||||
} | ||||
}, | ||||
/** | ||||
* Read the end of central directory. | ||||
*/ | ||||
readEndOfCentral: function() { | ||||
var offset = this.reader.lastIndexOfSignature(sig.CENTRAL_DIRECTORY_END); | ||||
if (offset < 0) { | ||||
// Check if the content is a truncated zip or complete garbage. | ||||
// A "LOCAL_FILE_HEADER" is not required at the beginning (auto | ||||
// extractible zip for example) but it can give a good hint. | ||||
// If an ajax request was used without responseType, we will also | ||||
// get unreadable data. | ||||
var isGarbage = !this.isSignature(0, sig.LOCAL_FILE_HEADER); | ||||
if (isGarbage) { | ||||
throw new Error("Can't find end of central directory : is this a zip file ? " + | ||||
"If it is, see https://stuk.github.io/jszip/documentation/howto/read_zip.html"); | ||||
} else { | ||||
throw new Error("Corrupted zip: can't find end of central directory"); | ||||
} | ||||
} | ||||
this.reader.setIndex(offset); | ||||
var endOfCentralDirOffset = offset; | ||||
this.checkSignature(sig.CENTRAL_DIRECTORY_END); | ||||
this.readBlockEndOfCentral(); | ||||
/* extract from the zip spec : | ||||
4) If one of the fields in the end of central directory | ||||
record is too small to hold required data, the field | ||||
should be set to -1 (0xFFFF or 0xFFFFFFFF) and the | ||||
ZIP64 format record should be created. | ||||
5) The end of central directory record and the | ||||
Zip64 end of central directory locator record must | ||||
reside on the same disk when splitting or spanning | ||||
an archive. | ||||
*/ | ||||
if (this.diskNumber === utils.MAX_VALUE_16BITS || this.diskWithCentralDirStart === utils.MAX_VALUE_16BITS || this.centralDirRecordsOnThisDisk === utils.MAX_VALUE_16BITS || this.centralDirRecords === utils.MAX_VALUE_16BITS || this.centralDirSize === utils.MAX_VALUE_32BITS || this.centralDirOffset === utils.MAX_VALUE_32BITS) { | ||||
this.zip64 = true; | ||||
/* | ||||
Warning : the zip64 extension is supported, but ONLY if the 64bits integer read from | ||||
the zip file can fit into a 32bits integer. This cannot be solved : JavaScript represents | ||||
all numbers as 64-bit double precision IEEE 754 floating point numbers. | ||||
So, we have 53bits for integers and bitwise operations treat everything as 32bits. | ||||
see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/Bitwise_Operators | ||||
and http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf section 8.5 | ||||
*/ | ||||
// should look for a zip64 EOCD locator | ||||
offset = this.reader.lastIndexOfSignature(sig.ZIP64_CENTRAL_DIRECTORY_LOCATOR); | ||||
if (offset < 0) { | ||||
throw new Error("Corrupted zip: can't find the ZIP64 end of central directory locator"); | ||||
} | ||||
this.reader.setIndex(offset); | ||||
this.checkSignature(sig.ZIP64_CENTRAL_DIRECTORY_LOCATOR); | ||||
this.readBlockZip64EndOfCentralLocator(); | ||||
// now the zip64 EOCD record | ||||
if (!this.isSignature(this.relativeOffsetEndOfZip64CentralDir, sig.ZIP64_CENTRAL_DIRECTORY_END)) { | ||||
// console.warn("ZIP64 end of central directory not where expected."); | ||||
this.relativeOffsetEndOfZip64CentralDir = this.reader.lastIndexOfSignature(sig.ZIP64_CENTRAL_DIRECTORY_END); | ||||
if (this.relativeOffsetEndOfZip64CentralDir < 0) { | ||||
throw new Error("Corrupted zip: can't find the ZIP64 end of central directory"); | ||||
} | ||||
} | ||||
this.reader.setIndex(this.relativeOffsetEndOfZip64CentralDir); | ||||
this.checkSignature(sig.ZIP64_CENTRAL_DIRECTORY_END); | ||||
this.readBlockZip64EndOfCentral(); | ||||
} | ||||
var expectedEndOfCentralDirOffset = this.centralDirOffset + this.centralDirSize; | ||||
if (this.zip64) { | ||||
expectedEndOfCentralDirOffset += 20; // end of central dir 64 locator | ||||
expectedEndOfCentralDirOffset += 12 /* should not include the leading 12 bytes */ + this.zip64EndOfCentralSize; | ||||
} | ||||
var extraBytes = endOfCentralDirOffset - expectedEndOfCentralDirOffset; | ||||
if (extraBytes > 0) { | ||||
// console.warn(extraBytes, "extra bytes at beginning or within zipfile"); | ||||
if (this.isSignature(endOfCentralDirOffset, sig.CENTRAL_FILE_HEADER)) { | ||||
// The offsets seem wrong, but we have something at the specified offset. | ||||
// So… we keep it. | ||||
} else { | ||||
// the offset is wrong, update the "zero" of the reader | ||||
// this happens if data has been prepended (crx files for example) | ||||
this.reader.zero = extraBytes; | ||||
} | ||||
} else if (extraBytes < 0) { | ||||
throw new Error("Corrupted zip: missing " + Math.abs(extraBytes) + " bytes."); | ||||
} | ||||
}, | ||||
prepareReader: function(data) { | ||||
this.reader = readerFor(data); | ||||
}, | ||||
/** | ||||
* Read a zip file and create ZipEntries. | ||||
* @param {String|ArrayBuffer|Uint8Array|Buffer} data the binary string representing a zip file. | ||||
*/ | ||||
load: function(data) { | ||||
this.prepareReader(data); | ||||
this.readEndOfCentral(); | ||||
this.readCentralDir(); | ||||
this.readLocalFiles(); | ||||
} | ||||
}; | ||||
// }}} end of ZipEntries | ||||
module.exports = ZipEntries; | ||||