blob: 02c317256e4850da6c558d63384cd9e49fa85548 [file] [log] [blame]
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" +
'}';
}
}
};