You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							262 lines
						
					
					
						
							12 KiB
						
					
					
				
			
		
		
	
	
							262 lines
						
					
					
						
							12 KiB
						
					
					
				"use strict";
 | 
						|
var readerFor = require("./reader/readerFor");
 | 
						|
var utils = require("./utils");
 | 
						|
var sig = require("./signature");
 | 
						|
var ZipEntry = require("./zipEntry");
 | 
						|
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;
 |