var ZipEntry = require("./zipEntry"), Headers = require("./headers"), Utils = require("./util"); module.exports = function(/*String|Buffer*/input, /*Number*/inputType) { var entryList = [], entryTable = {}, _comment = new Buffer(0), filename = "", fs = require("fs"), inBuffer = null, mainHeader = new Headers.MainHeader(); if (inputType == Utils.Constants.FILE) { // is a filename filename = input; inBuffer = fs.readFileSync(filename); readMainHeader(); } else if (inputType == Utils.Constants.BUFFER) { // is a memory buffer inBuffer = input; readMainHeader(); } else { // none. is a new file } function readEntries() { entryTable = {}; entryList = new Array(mainHeader.diskEntries); // total number of entries var index = mainHeader.offset; // offset of first CEN header for(var i = 0; i < entryList.length; i++) { var tmp = index, entry = new ZipEntry(inBuffer); entry.header = inBuffer.slice(tmp, tmp += Utils.Constants.CENHDR); entry.entryName = inBuffer.slice(tmp, tmp += entry.header.fileNameLength); if (entry.header.extraLength) { entry.extra = inBuffer.slice(tmp, tmp += entry.header.extraLength); } if (entry.header.commentLength) entry.comment = inBuffer.slice(tmp, tmp + entry.header.commentLength); index += entry.header.entryHeaderSize; entryList[i] = entry; entryTable[entry.entryName] = entry; } } function readMainHeader() { var i = inBuffer.length - Utils.Constants.ENDHDR, // END header size n = Math.max(0, i - 0xFFFF), // 0xFFFF is the max zip file comment length endOffset = 0; // Start offset of the END header for (i; i >= n; i--) { if (inBuffer[i] != 0x50) continue; // quick check that the byte is 'P' if (inBuffer.readUInt32LE(i) == Utils.Constants.ENDSIG) { // "PK\005\006" endOffset = i; break; } } if (!endOffset) throw Utils.Errors.INVALID_FORMAT; mainHeader.loadFromBinary(inBuffer.slice(endOffset, endOffset + Utils.Constants.ENDHDR)); if (mainHeader.commentLength) { _comment = inBuffer.slice(endOffset + Utils.Constants.ENDHDR); } readEntries(); } return { /** * Returns an array of ZipEntry objects existent in the current opened archive * @return Array */ get entries () { return entryList; }, /** * Archive comment * @return {String} */ get comment () { return _comment.toString(); }, set comment(val) { mainHeader.commentLength = val.length; _comment = val; }, /** * Returns a reference to the entry with the given name or null if entry is inexistent * * @param entryName * @return ZipEntry */ getEntry : function(/*String*/entryName) { return entryTable[entryName] || null; }, /** * Adds the given entry to the entry list * * @param entry */ setEntry : function(/*ZipEntry*/entry) { entryList.push(entry); entryTable[entry.entryName] = entry; mainHeader.totalEntries = entryList.length; }, /** * Removes the entry with the given name from the entry list. * * If the entry is a directory, then all nested files and directories will be removed * @param entryName */ deleteEntry : function(/*String*/entryName) { var entry = entryTable[entryName]; if (entry && entry.isDirectory) { var _self = this; this.getEntryChildren(entry).forEach(function(child) { if (child.entryName != entryName) { _self.deleteEntry(child.entryName) } }) } entryList.splice(entryList.indexOf(entry), 1); delete(entryTable[entryName]); mainHeader.totalEntries = entryList.length; }, /** * Iterates and returns all nested files and directories of the given entry * * @param entry * @return Array */ getEntryChildren : function(/*ZipEntry*/entry) { if (entry.isDirectory) { var list = [], name = entry.entryName, len = name.length; entryList.forEach(function(zipEntry) { if (zipEntry.entryName.substr(0, len) == name) { list.push(zipEntry); } }); return list; } return [] }, /** * Returns the zip file * * @return Buffer */ compressToBuffer : function() { if (entryList.length > 1) { entryList.sort(function(a, b) { var nameA = a.entryName.toLowerCase(); var nameB = b.entryName.toLowerCase(); if (nameA < nameB) {return -1} if (nameA > nameB) {return 1} return 0; }); } var totalSize = 0, dataBlock = [], entryHeaders = [], dindex = 0; mainHeader.size = 0; mainHeader.offset = 0; entryList.forEach(function(entry) { entry.header.offset = dindex; // compress data and set local and entry header accordingly. Reason why is called first var compressedData = entry.getCompressedData(); // data header var dataHeader = entry.header.dataHeaderToBinary(); var postHeader = new Buffer(entry.entryName + entry.extra.toString()); var dataLength = dataHeader.length + postHeader.length + compressedData.length; dindex += dataLength; dataBlock.push(dataHeader); dataBlock.push(postHeader); dataBlock.push(compressedData); var entryHeader = entry.packHeader(); entryHeaders.push(entryHeader); mainHeader.size += entryHeader.length; totalSize += (dataLength + entryHeader.length); }); totalSize += mainHeader.mainHeaderSize; // also includes zip file comment length // point to end of data and begining of central directory first record mainHeader.offset = dindex; dindex = 0; var outBuffer = new Buffer(totalSize); dataBlock.forEach(function(content) { content.copy(outBuffer, dindex); // write data blocks dindex += content.length; }); entryHeaders.forEach(function(content) { content.copy(outBuffer, dindex); // write central directory entries dindex += content.length; }); var mh = mainHeader.toBinary(); if (_comment) { _comment.copy(mh, Utils.Constants.ENDHDR); // add zip file comment } mh.copy(outBuffer, dindex); // write main header return outBuffer }, toAsyncBuffer : function(/*Function*/onSuccess,/*Function*/onFail,/*Function*/onItemStart,/*Function*/onItemEnd) { if (entryList.length > 1) { entryList.sort(function(a, b) { var nameA = a.entryName.toLowerCase(); var nameB = b.entryName.toLowerCase(); if (nameA > nameB) {return -1} if (nameA < nameB) {return 1} return 0; }); } var totalSize = 0, dataBlock = [], entryHeaders = [], dindex = 0; mainHeader.size = 0; mainHeader.offset = 0; var compress=function(entryList){ var self=arguments.callee; var entry; if(entryList.length){ var entry=entryList.pop(); var name=entry.entryName + entry.extra.toString(); if(onItemStart)onItemStart(name); entry.getCompressedDataAsync(function(compressedData){ if(onItemEnd)onItemEnd(name); entry.header.offset = dindex; // data header var dataHeader = entry.header.dataHeaderToBinary(); var postHeader = new Buffer(name); var dataLength = dataHeader.length + postHeader.length + compressedData.length; dindex += dataLength; dataBlock.push(dataHeader); dataBlock.push(postHeader); dataBlock.push(compressedData); var entryHeader = entry.packHeader(); entryHeaders.push(entryHeader); mainHeader.size += entryHeader.length; totalSize += (dataLength + entryHeader.length); if(entryList.length){ self(entryList); }else{ totalSize += mainHeader.mainHeaderSize; // also includes zip file comment length // point to end of data and begining of central directory first record mainHeader.offset = dindex; dindex = 0; var outBuffer = new Buffer(totalSize); dataBlock.forEach(function(content) { content.copy(outBuffer, dindex); // write data blocks dindex += content.length; }); entryHeaders.forEach(function(content) { content.copy(outBuffer, dindex); // write central directory entries dindex += content.length; }); var mh = mainHeader.toBinary(); if (_comment) { _comment.copy(mh, Utils.Constants.ENDHDR); // add zip file comment } mh.copy(outBuffer, dindex); // write main header onSuccess(outBuffer); } }); } }; compress(entryList); } } };