TrinetraLabs/Placebo_AI
0
1"use strict";2var readerFor = require("./reader/readerFor");3var utils = require("./utils");4var CompressedObject = require("./compressedObject");5var crc32fn = require("./crc32");6var utf8 = require("./utf8");7var compressions = require("./compressions");8var support = require("./support");9 10var MADE_BY_DOS = 0x00;11var MADE_BY_UNIX = 0x03;12 13/**14 * Find a compression registered in JSZip.15 * @param {string} compressionMethod the method magic to find.16 * @return {Object|null} the JSZip compression object, null if none found.17 */18var findCompression = function(compressionMethod) {19 for (var method in compressions) {20 if (!Object.prototype.hasOwnProperty.call(compressions, method)) {21 continue;22 }23 if (compressions[method].magic === compressionMethod) {24 return compressions[method];25 }26 }27 return null;28};29 30// class ZipEntry {{{31/**32 * An entry in the zip file.33 * @constructor34 * @param {Object} options Options of the current file.35 * @param {Object} loadOptions Options for loading the stream.36 */37function ZipEntry(options, loadOptions) {38 this.options = options;39 this.loadOptions = loadOptions;40}41ZipEntry.prototype = {42 /**43 * say if the file is encrypted.44 * @return {boolean} true if the file is encrypted, false otherwise.45 */46 isEncrypted: function() {47 // bit 1 is set48 return (this.bitFlag & 0x0001) === 0x0001;49 },50 /**51 * say if the file has utf-8 filename/comment.52 * @return {boolean} true if the filename/comment is in utf-8, false otherwise.53 */54 useUTF8: function() {55 // bit 11 is set56 return (this.bitFlag & 0x0800) === 0x0800;57 },58 /**59 * Read the local part of a zip file and add the info in this object.60 * @param {DataReader} reader the reader to use.61 */62 readLocalPart: function(reader) {63 var compression, localExtraFieldsLength;64 65 // we already know everything from the central dir !66 // If the central dir data are false, we are doomed.67 // On the bright side, the local part is scary : zip64, data descriptors, both, etc.68 // The less data we get here, the more reliable this should be.69 // Let's skip the whole header and dash to the data !70 reader.skip(22);71 // in some zip created on windows, the filename stored in the central dir contains \ instead of /.72 // Strangely, the filename here is OK.73 // I would love to treat these zip files as corrupted (see http://www.info-zip.org/FAQ.html#backslashes74 // or APPNOTE#4.4.17.1, "All slashes MUST be forward slashes '/'") but there are a lot of bad zip generators...75 // Search "unzip mismatching "local" filename continuing with "central" filename version" on76 // the internet.77 //78 // I think I see the logic here : the central directory is used to display79 // content and the local directory is used to extract the files. Mixing / and \80 // may be used to display \ to windows users and use / when extracting the files.81 // Unfortunately, this lead also to some issues : http://seclists.org/fulldisclosure/2009/Sep/39482 this.fileNameLength = reader.readInt(2);83 localExtraFieldsLength = reader.readInt(2); // can't be sure this will be the same as the central dir84 // the fileName is stored as binary data, the handleUTF8 method will take care of the encoding.85 this.fileName = reader.readData(this.fileNameLength);86 reader.skip(localExtraFieldsLength);87 88 if (this.compressedSize === -1 || this.uncompressedSize === -1) {89 throw new Error("Bug or corrupted zip : didn't get enough information from the central directory " + "(compressedSize === -1 || uncompressedSize === -1)");90 }91 92 compression = findCompression(this.compressionMethod);93 if (compression === null) { // no compression found94 throw new Error("Corrupted zip : compression " + utils.pretty(this.compressionMethod) + " unknown (inner file : " + utils.transformTo("string", this.fileName) + ")");95 }96 this.decompressed = new CompressedObject(this.compressedSize, this.uncompressedSize, this.crc32, compression, reader.readData(this.compressedSize));97 },98 99 /**100 * Read the central part of a zip file and add the info in this object.101 * @param {DataReader} reader the reader to use.102 */103 readCentralPart: function(reader) {104 this.versionMadeBy = reader.readInt(2);105 reader.skip(2);106 // this.versionNeeded = reader.readInt(2);107 this.bitFlag = reader.readInt(2);108 this.compressionMethod = reader.readString(2);109 this.date = reader.readDate();110 this.crc32 = reader.readInt(4);111 this.compressedSize = reader.readInt(4);112 this.uncompressedSize = reader.readInt(4);113 var fileNameLength = reader.readInt(2);114 this.extraFieldsLength = reader.readInt(2);115 this.fileCommentLength = reader.readInt(2);116 this.diskNumberStart = reader.readInt(2);117 this.internalFileAttributes = reader.readInt(2);118 this.externalFileAttributes = reader.readInt(4);119 this.localHeaderOffset = reader.readInt(4);120 121 if (this.isEncrypted()) {122 throw new Error("Encrypted zip are not supported");123 }124 125 // will be read in the local part, see the comments there126 reader.skip(fileNameLength);127 this.readExtraFields(reader);128 this.parseZIP64ExtraField(reader);129 this.fileComment = reader.readData(this.fileCommentLength);130 },131 132 /**133 * Parse the external file attributes and get the unix/dos permissions.134 */135 processAttributes: function () {136 this.unixPermissions = null;137 this.dosPermissions = null;138 var madeBy = this.versionMadeBy >> 8;139 140 // Check if we have the DOS directory flag set.141 // We look for it in the DOS and UNIX permissions142 // but some unknown platform could set it as a compatibility flag.143 this.dir = this.externalFileAttributes & 0x0010 ? true : false;144 145 if(madeBy === MADE_BY_DOS) {146 // first 6 bits (0 to 5)147 this.dosPermissions = this.externalFileAttributes & 0x3F;148 }149 150 if(madeBy === MADE_BY_UNIX) {151 this.unixPermissions = (this.externalFileAttributes >> 16) & 0xFFFF;152 // the octal permissions are in (this.unixPermissions & 0x01FF).toString(8);153 }154 155 // fail safe : if the name ends with a / it probably means a folder156 if (!this.dir && this.fileNameStr.slice(-1) === "/") {157 this.dir = true;158 }159 },160 161 /**162 * Parse the ZIP64 extra field and merge the info in the current ZipEntry.163 * @param {DataReader} reader the reader to use.164 */165 parseZIP64ExtraField: function() {166 if (!this.extraFields[0x0001]) {167 return;168 }169 170 // should be something, preparing the extra reader171 var extraReader = readerFor(this.extraFields[0x0001].value);172 173 // I really hope that these 64bits integer can fit in 32 bits integer, because js174 // won't let us have more.175 if (this.uncompressedSize === utils.MAX_VALUE_32BITS) {176 this.uncompressedSize = extraReader.readInt(8);177 }178 if (this.compressedSize === utils.MAX_VALUE_32BITS) {179 this.compressedSize = extraReader.readInt(8);180 }181 if (this.localHeaderOffset === utils.MAX_VALUE_32BITS) {182 this.localHeaderOffset = extraReader.readInt(8);183 }184 if (this.diskNumberStart === utils.MAX_VALUE_32BITS) {185 this.diskNumberStart = extraReader.readInt(4);186 }187 },188 /**189 * Read the central part of a zip file and add the info in this object.190 * @param {DataReader} reader the reader to use.191 */192 readExtraFields: function(reader) {193 var end = reader.index + this.extraFieldsLength,194 extraFieldId,195 extraFieldLength,196 extraFieldValue;197 198 if (!this.extraFields) {199 this.extraFields = {};200 }201 202 while (reader.index + 4 < end) {203 extraFieldId = reader.readInt(2);204 extraFieldLength = reader.readInt(2);205 extraFieldValue = reader.readData(extraFieldLength);206 207 this.extraFields[extraFieldId] = {208 id: extraFieldId,209 length: extraFieldLength,210 value: extraFieldValue211 };212 }213 214 reader.setIndex(end);215 },216 /**217 * Apply an UTF8 transformation if needed.218 */219 handleUTF8: function() {220 var decodeParamType = support.uint8array ? "uint8array" : "array";221 if (this.useUTF8()) {222 this.fileNameStr = utf8.utf8decode(this.fileName);223 this.fileCommentStr = utf8.utf8decode(this.fileComment);224 } else {225 var upath = this.findExtraFieldUnicodePath();226 if (upath !== null) {227 this.fileNameStr = upath;228 } else {229 // ASCII text or unsupported code page230 var fileNameByteArray = utils.transformTo(decodeParamType, this.fileName);231 this.fileNameStr = this.loadOptions.decodeFileName(fileNameByteArray);232 }233 234 var ucomment = this.findExtraFieldUnicodeComment();235 if (ucomment !== null) {236 this.fileCommentStr = ucomment;237 } else {238 // ASCII text or unsupported code page239 var commentByteArray = utils.transformTo(decodeParamType, this.fileComment);240 this.fileCommentStr = this.loadOptions.decodeFileName(commentByteArray);241 }242 }243 },244 245 /**246 * Find the unicode path declared in the extra field, if any.247 * @return {String} the unicode path, null otherwise.248 */249 findExtraFieldUnicodePath: function() {250 var upathField = this.extraFields[0x7075];251 if (upathField) {252 var extraReader = readerFor(upathField.value);253 254 // wrong version255 if (extraReader.readInt(1) !== 1) {256 return null;257 }258 259 // the crc of the filename changed, this field is out of date.260 if (crc32fn(this.fileName) !== extraReader.readInt(4)) {261 return null;262 }263 264 return utf8.utf8decode(extraReader.readData(upathField.length - 5));265 }266 return null;267 },268 269 /**270 * Find the unicode comment declared in the extra field, if any.271 * @return {String} the unicode comment, null otherwise.272 */273 findExtraFieldUnicodeComment: function() {274 var ucommentField = this.extraFields[0x6375];275 if (ucommentField) {276 var extraReader = readerFor(ucommentField.value);277 278 // wrong version279 if (extraReader.readInt(1) !== 1) {280 return null;281 }282 283 // the crc of the comment changed, this field is out of date.284 if (crc32fn(this.fileComment) !== extraReader.readInt(4)) {285 return null;286 }287 288 return utf8.utf8decode(extraReader.readData(ucommentField.length - 5));289 }290 return null;291 }292};293module.exports = ZipEntry;294 