var Utils = require("./util"), | |
Headers = require("./headers"), | |
Constants = Utils.Constants, | |
Methods = require("./methods"); | |
module.exports = function (/*Buffer*/input) { | |
var _entryHeader = new Headers.EntryHeader(), | |
_entryName = new Buffer(0), | |
_comment = new Buffer(0), | |
_isDirectory = false, | |
uncompressedData = null, | |
_extra = new Buffer(0); | |
function getCompressedDataFromZip() { | |
if (!input || !Buffer.isBuffer(input)) { | |
return new Buffer(0); | |
} | |
_entryHeader.loadDataHeaderFromBinary(input); | |
return input.slice(_entryHeader.realDataOffset, _entryHeader.realDataOffset + _entryHeader.compressedSize) | |
} | |
function crc32OK(data) { | |
// if bit 3 (0x08) of the general-purpose flags field is set, then the CRC-32 and file sizes are not known when the header is written | |
if (_entryHeader.flags & 0x8 != 0x8) { | |
if (Utils.crc32(data) != _entryHeader.crc) { | |
return false; | |
} | |
} else { | |
// @TODO: load and check data descriptor header | |
// The fields in the local header are filled with zero, and the CRC-32 and size are appended in a 12-byte structure | |
// (optionally preceded by a 4-byte signature) immediately after the compressed data: | |
} | |
return true; | |
} | |
function decompress(/*Boolean*/async, /*Function*/callback) { | |
if (_isDirectory) { | |
if (async && callback) { | |
callback(new Buffer(0), Utils.Errors.DIRECTORY_CONTENT_ERROR); //si added error. | |
} | |
return new Buffer(0); | |
} | |
var compressedData = getCompressedDataFromZip(); | |
if (compressedData.length == 0) { | |
if (async && callback) callback(compressedData, Utils.Errors.NO_DATA);//si added error. | |
return compressedData; | |
} | |
var data = new Buffer(_entryHeader.size); | |
data.fill(0); | |
switch (_entryHeader.method) { | |
case Utils.Constants.STORED: | |
compressedData.copy(data); | |
if (!crc32OK(data)) { | |
if (async && callback) callback(data, Utils.Errors.BAD_CRC);//si added error | |
return Utils.Errors.BAD_CRC; | |
} else {//si added otherwise did not seem to return data. | |
if (async && callback) callback(data); | |
return data; | |
} | |
break; | |
case Utils.Constants.DEFLATED: | |
var inflater = new Methods.Inflater(compressedData); | |
if (!async) { | |
inflater.inflate(data); | |
if (!crc32OK(data)) { | |
console.warn(Utils.Errors.BAD_CRC + " " + _entryName.toString()) | |
} | |
return data; | |
} else { | |
inflater.inflateAsync(function(result) { | |
result.copy(data, 0); | |
if (crc32OK(data)) { | |
if (callback) callback(data, Utils.Errors.BAD_CRC); //si added error | |
} else { //si added otherwise did not seem to return data. | |
if (callback) callback(data); | |
} | |
}) | |
} | |
break; | |
default: | |
if (async && callback) callback(new Buffer(0), Utils.Errors.UNKNOWN_METHOD); | |
return Utils.Errors.UNKNOWN_METHOD; | |
} | |
} | |
function compress(/*Boolean*/async, /*Function*/callback) { | |
if ((!uncompressedData || !uncompressedData.length) && Buffer.isBuffer(input)) { | |
// no data set or the data wasn't changed to require recompression | |
if (async && callback) callback(getCompressedDataFromZip()); | |
return getCompressedDataFromZip(); | |
} | |
if (uncompressedData.length && !_isDirectory) { | |
var compressedData; | |
// Local file header | |
switch (_entryHeader.method) { | |
case Utils.Constants.STORED: | |
_entryHeader.compressedSize = _entryHeader.size; | |
compressedData = new Buffer(uncompressedData.length); | |
uncompressedData.copy(compressedData); | |
if (async && callback) callback(compressedData); | |
return compressedData; | |
break; | |
default: | |
case Utils.Constants.DEFLATED: | |
var deflater = new Methods.Deflater(uncompressedData); | |
if (!async) { | |
var deflated = deflater.deflate(); | |
_entryHeader.compressedSize = deflated.length; | |
return deflated; | |
} else { | |
deflater.deflateAsync(function(data) { | |
compressedData = new Buffer(data.length); | |
_entryHeader.compressedSize = data.length; | |
data.copy(compressedData); | |
callback && callback(compressedData); | |
}) | |
} | |
deflater = null; | |
break; | |
} | |
} else { | |
if (async && callback) { | |
callback(new Buffer(0)); | |
} else { | |
return new Buffer(0); | |
} | |
} | |
} | |
return { | |
get entryName () { return _entryName.toString(); }, | |
get rawEntryName() { return _entryName; }, | |
set entryName (val) { | |
_entryName = Utils.toBuffer(val); | |
var lastChar = _entryName[_entryName.length - 1]; | |
_isDirectory = (lastChar == 47) || (lastChar == 92); | |
_entryHeader.fileNameLength = _entryName.length; | |
}, | |
get extra () { return _extra; }, | |
set extra (val) { | |
_extra = val; | |
_entryHeader.extraLength = val.length; | |
}, | |
get comment () { return _comment.toString(); }, | |
set comment (val) { | |
_comment = Utils.toBuffer(val); | |
_entryHeader.commentLength = _comment.length; | |
}, | |
get name () { var n = _entryName.toString(); return _isDirectory ? n.substr(n.length - 1).split("/").pop() : n.split("/").pop(); }, | |
get isDirectory () { return _isDirectory }, | |
getCompressedData : function() { | |
return compress(false, null) | |
}, | |
getCompressedDataAsync : function(/*Function*/callback) { | |
compress(true, callback) | |
}, | |
setData : function(value) { | |
uncompressedData = Utils.toBuffer(value); | |
if (!_isDirectory && uncompressedData.length) { | |
_entryHeader.size = uncompressedData.length; | |
_entryHeader.method = Utils.Constants.DEFLATED; | |
_entryHeader.crc = Utils.crc32(value); | |
} else { // folders and blank files should be stored | |
_entryHeader.method = Utils.Constants.STORED; | |
} | |
}, | |
getData : function() { | |
return decompress(false, null); | |
}, | |
getDataAsync : function(/*Function*/callback) { | |
decompress(true, callback) | |
}, | |
set header(/*Buffer*/data) { | |
_entryHeader.loadFromBinary(data); | |
}, | |
get header() { | |
return _entryHeader; | |
}, | |
packHeader : function() { | |
var header = _entryHeader.entryHeaderToBinary(); | |
// add | |
_entryName.copy(header, Utils.Constants.CENHDR); | |
if (_entryHeader.extraLength) { | |
_extra.copy(header, Utils.Constants.CENHDR + _entryName.length) | |
} | |
if (_entryHeader.commentLength) { | |
_comment.copy(header, Utils.Constants.CENHDR + _entryName.length + _entryHeader.extraLength, _comment.length); | |
} | |
return header; | |
}, | |
toString : function() { | |
return '{\n' + | |
'\t"entryName" : "' + _entryName.toString() + "\",\n" + | |
'\t"name" : "' + _entryName.toString().split("/").pop() + "\",\n" + | |
'\t"comment" : "' + _comment.toString() + "\",\n" + | |
'\t"isDirectory" : ' + _isDirectory + ",\n" + | |
'\t"header" : ' + _entryHeader.toString().replace(/\t/mg, "\t\t") + ",\n" + | |
'\t"compressedData" : <' + (input && input.length + " bytes buffer" || "null") + ">\n" + | |
'\t"data" : <' + (uncompressedData && uncompressedData.length + " bytes buffer" || "null") + ">\n" + | |
'}'; | |
} | |
} | |
}; |