From d9b99d68a767f1a1e6586e4932086e8cc8051018 Mon Sep 17 00:00:00 2001 From: SheetJS Date: Tue, 4 Sep 2018 03:14:20 -0400 Subject: [PATCH] version bump 1.1.0: zip support --- .gitignore | 12 +- CHANGELOG.md | 4 + README.md | 55 ++- bin/cfb.njs | 59 +++- bits/00_header.js | 2 +- bits/05_buf.js | 14 +- bits/08_blob.js | 1 - bits/21_crc32.js | 101 ++++++ bits/31_version.js | 2 +- bits/37_dosdates.js | 38 +++ bits/38_extrafield.js | 27 ++ bits/40_parse.js | 3 + bits/41_mver.js | 1 + bits/60_writehead.js | 1 + bits/78_zlib.js | 22 ++ bits/79_flate.js | 70 ++++ bits/80_deflate.js | 23 ++ bits/81_inflate.js | 226 ++++++++++++ bits/82_zparse.js | 103 ++++++ bits/83_zwrite.js | 102 ++++++ bits/85_api.js | 2 + bits/88_cfbexports.js | 3 + cfb.flow.js | 740 +++++++++++++++++++++++++++++++++++++++- cfb.js | 736 ++++++++++++++++++++++++++++++++++++++- dist/cfb.js | 736 ++++++++++++++++++++++++++++++++++++++- dist/cfb.min.js | 2 +- dist/cfb.min.map | 2 +- dist/xlscfb.js | 728 ++++++++++++++++++++++++++++++++++++++- fails.lst | 11 +- index.html | 18 +- misc/flow.js | 1 + misc/suppress_export.js | 3 + package.json | 13 +- test.js | 166 ++++++--- types/bin_cfb.ts | 1 + types/index.d.ts | 23 +- types/tslint.json | 4 +- xlscfb.flow.js | 728 ++++++++++++++++++++++++++++++++++++++- xlscfb.js | 725 ++++++++++++++++++++++++++++++++++++++- 39 files changed, 5384 insertions(+), 124 deletions(-) create mode 100644 bits/21_crc32.js create mode 100644 bits/37_dosdates.js create mode 100644 bits/38_extrafield.js create mode 100644 bits/78_zlib.js create mode 100644 bits/79_flate.js create mode 100644 bits/80_deflate.js create mode 100644 bits/81_inflate.js create mode 100644 bits/82_zparse.js create mode 100644 bits/83_zwrite.js diff --git a/.gitignore b/.gitignore index 5f8f8ce..200a0dc 100644 --- a/.gitignore +++ b/.gitignore @@ -10,18 +10,26 @@ test_files_pres *.[cC][sS][vV] *.[dD][iIbB][fF] *.[pP][rR][nN] +*.[pP][mM][dD]* +*.[pP][dD][fF] *.[sS][lL][kK] *.socialcalc -*.[xX][lL][sSwWcC] -*.[xX][lL][sS][xXmMbB] +*.[xX][lL][sSwWcCaAtTmM] +*.[xX][lL][sSaAtT][xXmMbB] *.[oO][dD][sS] *.[fF][oO][dD][sS] *.[xX][mM][lL] *.[uU][oO][sS] *.[wW][kKqQbB][S1234567890] *.[qQ][pP][wW] +*.[bB][iI][fF][fF][23458] +*.[rR][tT][fF] +*.[eE][tT][hH] +*.[zZ][iI][pP] +*.[mM][sS][iI] *.123 *.htm *.html *.sheetjs *.exe +*.img diff --git a/CHANGELOG.md b/CHANGELOG.md index 157d868..c65a3a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ This log is intended to keep track of backwards-incompatible changes, including but not limited to API changes and file location changes. Minor behavioral changes may not be included if they are not expected to break existing code. +## 1.1.0 (2018-09-04) + +* Support for ZIP file format + ## 1.0.6 (2018-04-09) * `lastIndexOf` in FAT builder enables larger file parsing at minor cost diff --git a/README.md b/README.md index 899d702..948f176 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,11 @@ -# Compound File Binary Format +# Container File Blobs -Pure JS implementation of MS-CFB: Compound File Binary File Format, a container -format used in many Microsoft file types (XLS, DOC, VBA blobs in XLSX and XLSB) +Pure JS implementation of various container file formats, including ZIP and CFB. [![Build Status](https://travis-ci.org/SheetJS/js-cfb.svg?branch=master)](https://travis-ci.org/SheetJS/js-cfb) [![Coverage Status](http://img.shields.io/coveralls/SheetJS/js-cfb/master.svg)](https://coveralls.io/r/SheetJS/js-cfb?branch=master) [![Dependencies Status](https://david-dm.org/sheetjs/js-cfb/status.svg)](https://david-dm.org/sheetjs/js-cfb) [![NPM Downloads](https://img.shields.io/npm/dt/cfb.svg)](https://npmjs.org/package/cfb) -[![ghit.me](https://ghit.me/badge.svg?repo=sheetjs/js-xlsx)](https://ghit.me/repo/sheetjs/js-xlsx) [![Analytics](https://ga-beacon.appspot.com/UA-36810333-1/SheetJS/js-cfb?pixel)](https://github.com/SheetJS/js-cfb) ## Installation @@ -79,29 +77,48 @@ The CFB object exposes the following methods and properties: `CFB.parse(blob)` takes a nodejs Buffer or an array of bytes and returns an parsed representation of the data. -`CFB.read(blob, opts)` wraps `parse`. `opts.type` controls the behavior: +`CFB.read(blob, opts)` wraps `parse`. + +`CFB.find(cfb, path)` performs a case-insensitive match for the path (or file +name, if there are no slashes) and returns an entry object or null if not found. + +`CFB.write(cfb, opts)` generates a file based on the container. + +`CFB.writeFile(cfb, filename, opts)` creates a file with the specified name. + +### Parse Options + +`CFB.read` takes an options argument. `opts.type` controls the behavior: | `type` | expected input | -|------------|-----------------------------------------------------------------| +|------------|:----------------------------------------------------------------| | `"base64"` | string: Base64 encoding of the file | | `"binary"` | string: binary string (byte `n` is `data.charCodeAt(n)`) | | `"file"` | string: path of file that will be read (nodejs only) | | (default) | buffer or array of 8-bit unsigned int (byte `n` is `data[n]`) | -`CFB.find(cfb, path)` performs a case-insensitive match for the path (or file -name, if there are no slashes) and returns an entry object or null if not found. -`CFB.write(cfb, opts)` generates a file based on the container. `opts.type` -controls the behavior: +### Write Options + +`CFB.write` and `CFB.writeFile` take options argument. + +`opts.type` controls the behavior: | `type` | output | -|------------|-----------------------------------------------------------------| +|------------|:----------------------------------------------------------------| | `"base64"` | string: Base64 encoding of the file | | `"binary"` | string: binary string (byte `n` is `data.charCodeAt(n)`) | | `"file"` | string: path of file that will be created (nodejs only) | | (default) | buffer if available, array of 8-bit unsigned int otherwise | -`CFB.writeFile(cfb, filename, opts)` creates a file with the specified name. +`opts.fileType` controls the output file type: + +| `fileType` | output | +|:-------------------|:--------------| +| `'cfb'` (default) | CFB container | +| `'zip'` | ZIP file | + +`opts.compression` enables DEFLATE compression for ZIP file type. ## Utility Functions @@ -114,6 +131,12 @@ accept a `name` argument strictly deal with absolute file names: Set the option `{unsafe:true}` to skip existence checks (for bulk additions) - `.cfb_del(cfb, name)` deletes the specified file - `.cfb_mov(cfb, old_name, new_name)` moves the old file to new path and name +- `.use_zlib(require("zlib"))` loads a nodejs zlib instance. + +By default, the library uses a pure JS inflate/deflate implementation. NodeJS +`zlib.InflateRaw` exposes the number of bytes read in versions after `8.11.0`. +If a supplied `zlib` does not support the required features, a warning will be +displayed in the console and the pure JS fallback will be used. ## Container Object Description @@ -146,11 +169,7 @@ granted by the Apache 2.0 License are reserved by the Original Author. ## References - -
- OSP-covered Specifications (click to show) - - [MS-CFB]: Compound File Binary File Format - -
+ - ZIP `APPNOTE.TXT`: https://pkware.cachefly.net/webdocs/APPNOTE/APPNOTE-6.3.4.TXT + - RFC1951: https://www.ietf.org/rfc/rfc1951.txt diff --git a/bin/cfb.njs b/bin/cfb.njs index 2d69a59..862c818 100755 --- a/bin/cfb.njs +++ b/bin/cfb.njs @@ -19,11 +19,16 @@ program .option('-O, --to-stdout', 'extract raw contents to stdout') .option('-z, --dump', 'dump internal representation but do not extract') .option('-q, --quiet', 'process but do not report') + .option('--here', 'skip the CFB root name when extracting') + .option('--osx', 'use OSX-style unzip listing') + .option('--zlib', 'use native zlib') + .option('--local', 'print times in local timezone') .option('--dev', 'development mode') .option('--read', 'read but do not print out contents'); - program.parse(process.argv); +if(program.zlib) X.utils.use_zlib(require('zlib')); + var exit = process.exit; var die = function(errno/*:number*/, msg/*:string*/) { console.error(n + ": " + msg); exit(errno); }; var logit = function(cmd/*:string*/, f/*:string*/) { console.error(sprintf("%-6s %s", cmd, f)); }; @@ -53,16 +58,32 @@ if(program.dump) { } if(program.repair) { X.writeFile(cfb, program.args[0]); exit(0); } +var rlen = cfb.FullPaths[0].length; + function fix_string(x/*:string*/)/*:string*/ { return x.replace(/[\u0000-\u001f]/g, function($$) { return sprintf("\\u%04X", $$.charCodeAt(0)); }); } -var format_date = function(date/*:Date*/)/*:string*/ { - return sprintf("%02u-%02u-%02u %02u:%02u", date.getUTCMonth()+1, date.getUTCDate(), date.getUTCFullYear()%100, date.getUTCHours(), date.getUTCMinutes()); +var format_date = function(date/*:Date*/, osx/*:?any*/)/*:string*/ { + var datefmt = osx ? "%02u-%02u-%04u %02u:%02u": "%02u-%02u-%02u %02u:%02u"; + var MM = program.local ? date.getMonth() + 1 : date.getUTCMonth() + 1; + var DD = program.local ? date.getDate() : date.getUTCDate(); + var YY = (program.local ? date.getFullYear() : date.getUTCFullYear())%(osx ? 10000 : 100); + var hh = program.local ? date.getHours() : date.getUTCHours(); + var mm = program.local ? date.getMinutes() : date.getUTCMinutes(); + return sprintf(datefmt, MM, DD, YY, hh, mm); }; if(program.listFiles) { - var basetime = new Date(1980,0,1); + var basetime = new Date(Date.UTC(1980,0,1)); var cnt = 0, rootsize = 0, filesize = 0; - console.log(" Length Date Time Name"); - console.log(" -------- ---- ---- ----"); + var fmtstr = "%9lu %s %s"; + if(program.osx) { + console.log("Archive: " + program.args[0]); + console.log(" Length Date Time Name"); + console.log("--------- ---------- ----- ----"); + fmtstr = "%9lu %s %s"; + } else { + console.log(" Length Date Time Name"); + console.log(" -------- ---- ---- ----"); + } cfb.FileIndex.forEach(function(file/*:CFBEntry*/, i/*:number*/) { switch(file.type) { case 5: @@ -70,13 +91,22 @@ if(program.listFiles) { rootsize = file.size; break; case 2: - console.log(sprintf("%9lu %s %s", file.size, format_date(basetime), fix_string(cfb.FullPaths[i]))); + var fixname = fix_string(cfb.FullPaths[i]); + if(program.osx && fixname.match(/\\u0001Sh33tJ5/)) return; + if(program.here) fixname = fixname.slice(rlen); + console.log(sprintf(fmtstr, file.size, format_date(file.mt || basetime, program.osx), fixname)); filesize += file.size; ++cnt; } }); - console.log(" -------- -------"); - console.log(sprintf("%9lu %lu file%s", rootsize || filesize, cnt, (cnt !== 1 ? "s" : ""))); + var outfmt = "%9lu %lu file%s"; + if(program.osx) { + console.log("--------- -------"); + outfmt = "%9lu %lu file%s"; + } else { + console.log(" -------- -------"); + } + console.log(sprintf(outfmt, rootsize || filesize, cnt, (cnt !== 1 ? "s" : ""))); exit(0); } @@ -124,8 +154,13 @@ if(program.args.length > 1) { } if(program.toStdout) exit(0); -for(var i=0; i!==cfb.FullPaths.length; ++i) { +for(var i=program.here ? 1 : 0; i!==cfb.FullPaths.length; ++i) { if(!cfb.FileIndex[i].name) continue; - if(cfb.FullPaths[i].slice(-1) === "/") mkdirp(cfb.FullPaths[i]); - else write(cfb.FullPaths[i], cfb.FileIndex[i]); + var fp = cfb.FullPaths[i]; + if(program.here) fp = fp.slice(rlen); + if(fp.slice(-1) === "/") mkdirp(fp); + else { + if(fp.indexOf("/") > -1) mkdirp(fp.slice(0, fp.lastIndexOf("/"))); + write(fp, cfb.FileIndex[i]); + } } diff --git a/bits/00_header.js b/bits/00_header.js index 20d4aac..dd908f0 100644 --- a/bits/00_header.js +++ b/bits/00_header.js @@ -2,5 +2,5 @@ /* vim: set ts=2: */ /*jshint eqnull:true */ /*exported CFB */ -/*global module, require:false, process:false, Buffer:false, Uint8Array:false */ +/*global module, require:false, process:false, Buffer:false, Uint8Array:false, Uint16Array:false */ diff --git a/bits/05_buf.js b/bits/05_buf.js index 21c30f1..d0a0fd3 100644 --- a/bits/05_buf.js +++ b/bits/05_buf.js @@ -5,9 +5,11 @@ var Buffer_from = /*::(*/function(){}/*:: :any)*/; if(typeof Buffer !== 'undefined') { var nbfs = !Buffer.from; if(!nbfs) try { Buffer.from("foo", "utf8"); } catch(e) { nbfs = true; } - Buffer_from = nbfs ? function(buf, enc) { return (enc) ? new Buffer(buf, enc) : new Buffer(buf); } : Buffer.from.bind(Buffer); + Buffer_from = /*::((*/nbfs ? function(buf, enc) { return (enc) ? new Buffer(buf, enc) : new Buffer(buf); } : Buffer.from.bind(Buffer)/*::) :any)*/; // $FlowIgnore if(!Buffer.alloc) Buffer.alloc = function(n) { return new Buffer(n); }; + // $FlowIgnore + if(!Buffer.allocUnsafe) Buffer.allocUnsafe = function(n) { return new Buffer(n); }; } function new_raw_buf(len/*:number*/) { @@ -16,8 +18,14 @@ function new_raw_buf(len/*:number*/) { /* jshint +W056 */ } -var s2a = function s2a(s/*:string*/) { - if(has_buf) return Buffer.from(s, "binary"); +function new_unsafe_buf(len/*:number*/) { + /* jshint -W056 */ + return has_buf ? Buffer.allocUnsafe(len) : new Array(len); + /* jshint +W056 */ +} + +var s2a = function s2a(s/*:string*/)/*:any*/ { + if(has_buf) return Buffer_from(s, "binary"); return s.split("").map(function(x/*:string*/)/*:number*/{ return x.charCodeAt(0) & 0xff; }); }; diff --git a/bits/08_blob.js b/bits/08_blob.js index c059f31..7d56d1e 100644 --- a/bits/08_blob.js +++ b/bits/08_blob.js @@ -22,7 +22,6 @@ if(has_buf/*:: && typeof Buffer !== 'undefined'*/) { }; __hexlify = function(b/*:RawBytes|CFBlob*/,s/*:number*/,l/*:number*/)/*:string*/ { return Buffer.isBuffer(b)/*:: && b instanceof Buffer*/ ? b.toString('hex',s,s+l) : ___hexlify(b,s,l); }; __toBuffer = function(bufs/*:Array>*/)/*:RawBytes*/ { return (bufs[0].length > 0 && Buffer.isBuffer(bufs[0][0])) ? Buffer.concat((bufs[0]/*:any*/)) : ___toBuffer(bufs);}; - // $FlowIgnore s2a = function(s/*:string*/)/*:RawBytes*/ { return Buffer_from(s, "binary"); }; bconcat = function(bufs/*:Array*/)/*:RawBytes*/ { return Buffer.isBuffer(bufs[0]) ? Buffer.concat(/*::(*/bufs/*:: :any)*/) : __bconcat(bufs); }; } diff --git a/bits/21_crc32.js b/bits/21_crc32.js new file mode 100644 index 0000000..cc86b0f --- /dev/null +++ b/bits/21_crc32.js @@ -0,0 +1,101 @@ +/* crc32.js (C) 2014-present SheetJS -- http://sheetjs.com */ +/* vim: set ts=2: */ +/*exported CRC32 */ +var CRC32; +(function (factory) { + /*jshint ignore:start */ + /*eslint-disable */ + factory(CRC32 = {}); + /*eslint-enable */ + /*jshint ignore:end */ +}(function(CRC32) { +CRC32.version = '1.2.0'; +/* see perf/crc32table.js */ +/*global Int32Array */ +function signed_crc_table()/*:any*/ { + var c = 0, table/*:Array*/ = new Array(256); + + for(var n =0; n != 256; ++n){ + c = n; + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + table[n] = c; + } + + return typeof Int32Array !== 'undefined' ? new Int32Array(table) : table; +} + +var T = signed_crc_table(); +function crc32_bstr(bstr/*:string*/, seed/*:number*/)/*:number*/ { + var C = seed ^ -1, L = bstr.length - 1; + for(var i = 0; i < L;) { + C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF]; + C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF]; + } + if(i === L) C = (C>>>8) ^ T[(C ^ bstr.charCodeAt(i))&0xFF]; + return C ^ -1; +} + +function crc32_buf(buf/*:Uint8Array|Array*/, seed/*:number*/)/*:number*/ { + if(buf.length > 10000) return crc32_buf_8(buf, seed); + var C = seed ^ -1, L = buf.length - 3; + for(var i = 0; i < L;) { + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + } + while(i < L+3) C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + return C ^ -1; +} + +function crc32_buf_8(buf/*:Uint8Array|Array*/, seed/*:number*/)/*:number*/ { + var C = seed ^ -1, L = buf.length - 7; + for(var i = 0; i < L;) { + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + } + while(i < L+7) C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + return C ^ -1; +} + +function crc32_str(str/*:string*/, seed/*:number*/)/*:number*/ { + var C = seed ^ -1; + for(var i = 0, L=str.length, c, d; i < L;) { + c = str.charCodeAt(i++); + if(c < 0x80) { + C = (C>>>8) ^ T[(C ^ c)&0xFF]; + } else if(c < 0x800) { + C = (C>>>8) ^ T[(C ^ (192|((c>>6)&31)))&0xFF]; + C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF]; + } else if(c >= 0xD800 && c < 0xE000) { + c = (c&1023)+64; d = str.charCodeAt(i++)&1023; + C = (C>>>8) ^ T[(C ^ (240|((c>>8)&7)))&0xFF]; + C = (C>>>8) ^ T[(C ^ (128|((c>>2)&63)))&0xFF]; + C = (C>>>8) ^ T[(C ^ (128|((d>>6)&15)|((c&3)<<4)))&0xFF]; + C = (C>>>8) ^ T[(C ^ (128|(d&63)))&0xFF]; + } else { + C = (C>>>8) ^ T[(C ^ (224|((c>>12)&15)))&0xFF]; + C = (C>>>8) ^ T[(C ^ (128|((c>>6)&63)))&0xFF]; + C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF]; + } + } + return C ^ -1; +} +CRC32.table = T; +CRC32.bstr = crc32_bstr; +CRC32.buf = crc32_buf; +CRC32.str = crc32_str; +})); diff --git a/bits/31_version.js b/bits/31_version.js index 52fb8d0..f1182b7 100644 --- a/bits/31_version.js +++ b/bits/31_version.js @@ -1 +1 @@ -exports.version = '1.0.8'; +exports.version = '1.1.0'; diff --git a/bits/37_dosdates.js b/bits/37_dosdates.js new file mode 100644 index 0000000..c8e9bec --- /dev/null +++ b/bits/37_dosdates.js @@ -0,0 +1,38 @@ +/* -------------------------------------------------------------------------- */ +/* DOS Date format: + high|YYYYYYYm.mmmddddd.HHHHHMMM.MMMSSSSS|low + add 1980 to stored year + stored second should be doubled +*/ + +/* write JS date to buf as a DOS date */ +function write_dos_date(buf/*:CFBlob*/, date/*:Date|string*/) { + if(typeof date === "string") date = new Date(date); + var hms/*:number*/ = date.getHours(); + hms = hms << 6 | date.getMinutes(); + hms = hms << 5 | (date.getSeconds()>>>1); + buf.write_shift(2, hms); + var ymd/*:number*/ = (date.getFullYear() - 1980); + ymd = ymd << 4 | (date.getMonth()+1); + ymd = ymd << 5 | date.getDate(); + buf.write_shift(2, ymd); +} + +/* read four bytes from buf and interpret as a DOS date */ +function parse_dos_date(buf/*:CFBlob*/)/*:Date*/ { + var hms = buf.read_shift(2) & 0xFFFF; + var ymd = buf.read_shift(2) & 0xFFFF; + var val = new Date(); + var d = ymd & 0x1F; ymd >>>= 5; + var m = ymd & 0x0F; ymd >>>= 4; + val.setMilliseconds(0); + val.setFullYear(ymd + 1980); + val.setMonth(m-1); + val.setDate(d); + var S = hms & 0x1F; hms >>>= 5; + var M = hms & 0x3F; hms >>>= 6; + val.setHours(hms); + val.setMinutes(M); + val.setSeconds(S<<1); + return val; +} diff --git a/bits/38_extrafield.js b/bits/38_extrafield.js new file mode 100644 index 0000000..a7b1a34 --- /dev/null +++ b/bits/38_extrafield.js @@ -0,0 +1,27 @@ +function parse_extra_field(blob/*:CFBlob*/)/*:any*/ { + prep_blob(blob, 0); + var o = /*::(*/{}/*:: :any)*/; + var flags = 0; + while(blob.l <= blob.length - 4) { + var type = blob.read_shift(2); + var sz = blob.read_shift(2), tgt = blob.l + sz; + var p = {}; + switch(type) { + /* UNIX-style Timestamps */ + case 0x5455: { + flags = blob.read_shift(1); + if(flags & 1) p.mtime = blob.read_shift(4); + /* for some reason, CD flag corresponds to LFH */ + if(sz > 5) { + if(flags & 2) p.atime = blob.read_shift(4); + if(flags & 4) p.ctime = blob.read_shift(4); + } + if(p.mtime) p.mt = new Date(p.mtime*1000); + } + break; + } + blob.l = tgt; + o[type] = p; + } + return o; +} diff --git a/bits/40_parse.js b/bits/40_parse.js index 1dc1d78..633e2b3 100644 --- a/bits/40_parse.js +++ b/bits/40_parse.js @@ -1,4 +1,5 @@ function parse(file/*:RawBytes*/, options/*:CFBReadOpts*/)/*:CFBContainer*/ { +if(file[0] == 0x50 && file[1] == 0x4b) return parse_zip(file, options); if(file.length < 512) throw new Error("CFB file size " + file.length + " < 512"); var mver = 3; var ssz = 512; @@ -19,6 +20,8 @@ var mv = check_get_mver(blob); mver = mv[0]; switch(mver) { case 3: ssz = 512; break; case 4: ssz = 4096; break; + case 0: if(mv[1] == 0) return parse_zip(file, options); + /* falls through */ default: throw new Error("Major Version: Expected 3 or 4 saw " + mver); } diff --git a/bits/41_mver.js b/bits/41_mver.js index 8f5f257..a970977 100644 --- a/bits/41_mver.js +++ b/bits/41_mver.js @@ -1,5 +1,6 @@ /* [MS-CFB] 2.2 Compound File Header -- read up to major version */ function check_get_mver(blob/*:CFBlob*/)/*:[number, number]*/ { + if(blob[blob.l] == 0x50 && blob[blob.l + 1] == 0x4b) return [0, 0]; // header signature 8 blob.chk(HEADER_SIGNATURE, 'Header Signature: '); diff --git a/bits/60_writehead.js b/bits/60_writehead.js index 035d5df..47e5bc8 100644 --- a/bits/60_writehead.js +++ b/bits/60_writehead.js @@ -1,3 +1,4 @@ function _write(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes*/ { var _opts = options || {}; rebuild_cfb(cfb); + if(_opts.fileType == 'zip') return write_zip(cfb, _opts); diff --git a/bits/78_zlib.js b/bits/78_zlib.js new file mode 100644 index 0000000..f76e5d2 --- /dev/null +++ b/bits/78_zlib.js @@ -0,0 +1,22 @@ +/* node < 8.1 zlib does not expose bytesRead, so default to pure JS */ +var _zlib; +function use_zlib(zlib) { try { + var InflateRaw = zlib.InflateRaw; + var InflRaw = new InflateRaw(); + InflRaw._processChunk(new Uint8Array([3, 0]), InflRaw._finishFlushFlag); + if(InflRaw.bytesRead) _zlib = zlib; + else throw new Error("zlib does not expose bytesRead"); +} catch(e) {console.error("cannot use native zlib: " + (e.message || e)); } } + +function _inflateRawSync(payload, usz) { + if(!_zlib) return _inflate(payload, usz); + var InflateRaw = _zlib.InflateRaw; + var InflRaw = new InflateRaw(); + var out = InflRaw._processChunk(payload.slice(payload.l), InflRaw._finishFlushFlag); + payload.l += InflRaw.bytesRead; + return out; +} + +function _deflateRawSync(payload) { + return _zlib ? _zlib.deflateRawSync(payload) : _deflate(payload); +} diff --git a/bits/79_flate.js b/bits/79_flate.js new file mode 100644 index 0000000..d516ccc --- /dev/null +++ b/bits/79_flate.js @@ -0,0 +1,70 @@ +var CLEN_ORDER = [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ]; + +/* LEN_ID = [ 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285 ]; */ +var LEN_LN = [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13 , 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 ]; + +/* DST_ID = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 ]; */ +var DST_LN = [ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 ]; + +function bit_swap_8(n) { var t = (((((n<<1)|(n<<11)) & 0x22110) | (((n<<5)|(n<<15)) & 0x88440))); return ((t>>16) | (t>>8) |t)&0xFF; } + +var use_typed_arrays = typeof Uint8Array !== 'undefined'; + +var bitswap8 = use_typed_arrays ? new Uint8Array(1<<8) : []; +for(var q = 0; q < (1<<8); ++q) bitswap8[q] = bit_swap_8(q); + +function bit_swap_n(n, b) { + var rev = bitswap8[n & 0xFF]; + if(b <= 8) return rev >>> (8-b); + rev = (rev << 8) | bitswap8[(n>>8)&0xFF]; + if(b <= 16) return rev >>> (16-b); + rev = (rev << 8) | bitswap8[(n>>16)&0xFF]; + return rev >>> (24-b); +} + +/* helpers for unaligned bit reads */ +function read_bits_2(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 6 ? 0 : buf[h+1]<<8))>>>w)& 0x03; } +function read_bits_3(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 5 ? 0 : buf[h+1]<<8))>>>w)& 0x07; } +function read_bits_4(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 4 ? 0 : buf[h+1]<<8))>>>w)& 0x0F; } +function read_bits_5(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 3 ? 0 : buf[h+1]<<8))>>>w)& 0x1F; } +function read_bits_7(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 1 ? 0 : buf[h+1]<<8))>>>w)& 0x7F; } + +/* works up to n = 3 * 8 + 1 = 25 */ +function read_bits_n(buf, bl, n) { + var w = (bl&7), h = (bl>>>3), f = ((1<>> w; + if(n < 8 - w) return v & f; + v |= buf[h+1]<<(8-w); + if(n < 16 - w) return v & f; + v |= buf[h+2]<<(16-w); + if(n < 24 - w) return v & f; + v |= buf[h+3]<<(24-w); + return v & f; +} + +/* until ArrayBuffer#realloc is a thing, fake a realloc */ +function realloc(b, sz/*:number*/) { + var L = b.length, M = 2*L > sz ? 2*L : sz + 5, i = 0; + if(L >= sz) return b; + if(has_buf) { + var o = new_unsafe_buf(M); + // $FlowIgnore + if(b.copy) b.copy(o); + else for(; i < b.length; ++i) o[i] = b[i]; + return o; + } else if(use_typed_arrays) { + var a = new Uint8Array(M); + if(a.set) a.set(b); + else for(; i < b.length; ++i) a[i] = b[i]; + return a; + } + b.length = M; + return b; +} + +/* zero-filled arrays for older browsers */ +function zero_fill_array(n) { + var o = new Array(n); + for(var i = 0; i < n; ++i) o[i] = 0; + return o; +} \ No newline at end of file diff --git a/bits/80_deflate.js b/bits/80_deflate.js new file mode 100644 index 0000000..dacac21 --- /dev/null +++ b/bits/80_deflate.js @@ -0,0 +1,23 @@ +var _deflate = (function() { +var _deflateRaw = (function() { + return function deflateRaw(data, out) { + var boff = 0; + while(boff < data.length) { + var L = Math.min(0xFFFF, data.length - boff); + var h = boff + L == data.length; + /* TODO: this is only type 0 stored */ + out.write_shift(1, +h); + out.write_shift(2, L); + out.write_shift(2, (~L) & 0xFFFF); + while(L-- > 0) out[out.l++] = data[boff++]; + } + return out.l; + }; +})(); + +return function(data) { + var buf = new_buf(50+Math.floor(data.length*1.1)); + var off = _deflateRaw(data, buf); + return buf.slice(0, off); +}; +})(); diff --git a/bits/81_inflate.js b/bits/81_inflate.js new file mode 100644 index 0000000..0424fe0 --- /dev/null +++ b/bits/81_inflate.js @@ -0,0 +1,226 @@ +/* modified inflate function also moves original read head */ + +/* build tree (used for literals and lengths) */ +function build_tree(clens, cmap, MAX/*:number*/)/*:number*/ { + var maxlen = 1, w = 0, i = 0, j = 0, ccode = 0, L = clens.length; + + var bl_count = use_typed_arrays ? new Uint16Array(32) : zero_fill_array(32); + for(i = 0; i < 32; ++i) bl_count[i] = 0; + + for(i = L; i < MAX; ++i) clens[i] = 0; + L = clens.length; + + var ctree = use_typed_arrays ? new Uint16Array(L) : zero_fill_array(L); // [] + + /* build code tree */ + for(i = 0; i < L; ++i) { + bl_count[(w = clens[i])]++; + if(maxlen < w) maxlen = w; + ctree[i] = 0; + } + bl_count[0] = 0; + for(i = 1; i <= maxlen; ++i) bl_count[i+16] = (ccode = (ccode + bl_count[i-1])<<1); + for(i = 0; i < L; ++i) { + ccode = clens[i]; + if(ccode != 0) ctree[i] = bl_count[ccode+16]++; + } + + /* cmap[maxlen + 4 bits] = (off&15) + (lit<<4) reverse mapping */ + var cleni = 0; + for(i = 0; i < L; ++i) { + cleni = clens[i]; + if(cleni != 0) { + ccode = bit_swap_n(ctree[i], maxlen)>>(maxlen-cleni); + for(j = (1<<(maxlen + 4 - cleni)) - 1; j>=0; --j) + cmap[ccode|(j<*/ = []; + var i = 0; + for(;i<32; i++) dlens.push(5); + build_tree(dlens, fix_dmap, 32); + + var clens/*:Array*/ = []; + i = 0; + for(; i<=143; i++) clens.push(8); + for(; i<=255; i++) clens.push(9); + for(; i<=279; i++) clens.push(7); + for(; i<=287; i++) clens.push(8); + build_tree(clens, fix_lmap, 288); +})(); + +var dyn_lmap = use_typed_arrays ? new Uint16Array(32768) : zero_fill_array(32768); +var dyn_dmap = use_typed_arrays ? new Uint16Array(32768) : zero_fill_array(32768); +var dyn_cmap = use_typed_arrays ? new Uint16Array(128) : zero_fill_array(128); +var dyn_len_1 = 1, dyn_len_2 = 1; + +/* 5.5.3 Expanding Huffman Codes */ +function dyn(data, boff/*:number*/) { + /* nomenclature from RFC1951 refers to bit values; these are offset by the implicit constant */ + var _HLIT = read_bits_5(data, boff) + 257; boff += 5; + var _HDIST = read_bits_5(data, boff) + 1; boff += 5; + var _HCLEN = read_bits_4(data, boff) + 4; boff += 4; + var w = 0; + + /* grab and store code lengths */ + var clens = use_typed_arrays ? new Uint8Array(19) : zero_fill_array(19); + var ctree = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]; + var maxlen = 1; + var bl_count = use_typed_arrays ? new Uint8Array(8) : zero_fill_array(8); + var next_code = use_typed_arrays ? new Uint8Array(8) : zero_fill_array(8); + var L = clens.length; /* 19 */ + for(var i = 0; i < _HCLEN; ++i) { + clens[CLEN_ORDER[i]] = w = read_bits_3(data, boff); + if(maxlen < w) maxlen = w; + bl_count[w]++; + boff += 3; + } + + /* build code tree */ + var ccode = 0; + bl_count[0] = 0; + for(i = 1; i <= maxlen; ++i) next_code[i] = ccode = (ccode + bl_count[i-1])<<1; + for(i = 0; i < L; ++i) if((ccode = clens[i]) != 0) ctree[i] = next_code[ccode]++; + /* cmap[7 bits from stream] = (off&7) + (lit<<3) */ + var cleni = 0; + for(i = 0; i < L; ++i) { + cleni = clens[i]; + if(cleni != 0) { + ccode = bitswap8[ctree[i]]>>(8-cleni); + for(var j = (1<<(7-cleni))-1; j>=0; --j) dyn_cmap[ccode|(j<*/ = []; + maxlen = 1; + for(; hcodes.length < _HLIT + _HDIST;) { + ccode = dyn_cmap[read_bits_7(data, boff)]; + boff += ccode & 7; + switch((ccode >>>= 3)) { + case 16: + w = 3 + read_bits_2(data, boff); boff += 2; + ccode = hcodes[hcodes.length - 1]; + while(w-- > 0) hcodes.push(ccode); + break; + case 17: + w = 3 + read_bits_3(data, boff); boff += 3; + while(w-- > 0) hcodes.push(0); + break; + case 18: + w = 11 + read_bits_7(data, boff); boff += 7; + while(w -- > 0) hcodes.push(0); + break; + default: + hcodes.push(ccode); + if(maxlen < ccode) maxlen = ccode; + break; + } + } + + /* build literal / length trees */ + var h1 = hcodes.slice(0, _HLIT), h2 = hcodes.slice(_HLIT); + for(i = _HLIT; i < 286; ++i) h1[i] = 0; + for(i = _HDIST; i < 30; ++i) h2[i] = 0; + dyn_len_1 = build_tree(h1, dyn_lmap, 286); + dyn_len_2 = build_tree(h2, dyn_dmap, 30); + return boff; +} + +/* return [ data, bytesRead ] */ +function inflate(data, usz/*:number*/) { + /* shortcircuit for empty buffer [0x03, 0x00] */ + if(data[0] == 3 && !(data[1] & 0x3)) { return [new_raw_buf(usz), 2]; } + + /* bit offset */ + var boff = 0; + + /* header includes final bit and type bits */ + var header = 0; + + var outbuf = new_unsafe_buf(usz ? usz : (1<<18)); + var woff = 0; + var OL = outbuf.length>>>0; + var max_len_1 = 0, max_len_2 = 0; + + while((header&1) == 0) { + header = read_bits_3(data, boff); boff += 3; + if((header >>> 1) == 0) { + /* Stored block */ + if(boff & 7) boff += 8 - (boff&7); + /* 2 bytes sz, 2 bytes bit inverse */ + var sz = data[boff>>>3] | data[(boff>>>3)+1]<<8; + boff += 32; + /* push sz bytes */ + if(!usz && OL < woff + sz) { outbuf = realloc(outbuf, woff + sz); OL = outbuf.length; } + if(typeof data.copy === 'function') { + // $FlowIgnore + data.copy(outbuf, woff, boff>>>3, (boff>>>3)+sz); + woff += sz; boff += 8*sz; + } else while(sz-- > 0) { outbuf[woff++] = data[boff>>>3]; boff += 8; } + continue; + } else if((header >>> 1) == 1) { + /* Fixed Huffman */ + max_len_1 = 9; max_len_2 = 5; + } else { + /* Dynamic Huffman */ + boff = dyn(data, boff); + max_len_1 = dyn_len_1; max_len_2 = dyn_len_2; + } + if(!usz && (OL < woff + 32767)) { outbuf = realloc(outbuf, woff + 32767); OL = outbuf.length; } + for(;;) { // while(true) is apparently out of vogue in modern JS circles + /* ingest code and move read head */ + var bits = read_bits_n(data, boff, max_len_1); + var code = (header>>>1) == 1 ? fix_lmap[bits] : dyn_lmap[bits]; + boff += code & 15; code >>>= 4; + /* 0-255 are literals, 256 is end of block token, 257+ are copy tokens */ + if(((code>>>8)&0xFF) === 0) outbuf[woff++] = code; + else if(code == 256) break; + else { + code -= 257; + var len_eb = (code < 8) ? 0 : ((code-4)>>2); if(len_eb > 5) len_eb = 0; + var tgt = woff + LEN_LN[code]; + /* length extra bits */ + if(len_eb > 0) { + tgt += read_bits_n(data, boff, len_eb); + boff += len_eb; + } + + /* dist code */ + bits = read_bits_n(data, boff, max_len_2); + code = (header>>>1) == 1 ? fix_dmap[bits] : dyn_dmap[bits]; + boff += code & 15; code >>>= 4; + var dst_eb = (code < 4 ? 0 : (code-2)>>1); + var dst = DST_LN[code]; + /* dist extra bits */ + if(dst_eb > 0) { + dst += read_bits_n(data, boff, dst_eb); + boff += dst_eb; + } + + /* in the common case, manual byte copy is faster than TA set / Buffer copy */ + if(!usz && OL < tgt) { outbuf = realloc(outbuf, tgt); OL = outbuf.length; } + while(woff < tgt) { outbuf[woff] = outbuf[woff - dst]; ++woff; } + } + } + } + return [usz ? outbuf : outbuf.slice(0, woff), (boff+7)>>>3]; +} + +function _inflate(payload, usz) { + var data = payload.slice(payload.l||0); + var out = inflate(data, usz); + payload.l += out[1]; + return out[0]; +} + diff --git a/bits/82_zparse.js b/bits/82_zparse.js new file mode 100644 index 0000000..29f1cf7 --- /dev/null +++ b/bits/82_zparse.js @@ -0,0 +1,103 @@ +function warn_or_throw(wrn, msg) { + if(wrn) { if(typeof console !== 'undefined') console.error(msg); } + else throw new Error(msg); +} + +function parse_zip(file/*:RawBytes*/, options/*:CFBReadOpts*/)/*:CFBContainer*/ { + var blob/*:CFBlob*/ = /*::(*/file/*:: :any)*/; + prep_blob(blob, 0); + + var FileIndex/*:CFBFileIndex*/ = [], FullPaths/*:Array*/ = []; + var o = { + FileIndex: FileIndex, + FullPaths: FullPaths + }; + init_cfb(o, { root: options.root }); + + /* find end of central directory, start just after signature */ + var i = blob.length - 4; + while((blob[i] != 0x50 || blob[i+1] != 0x4b || blob[i+2] != 0x05 || blob[i+3] != 0x06) && i >= 0) --i; + blob.l = i + 4; + + /* parse end of central directory */ + blob.l += 4; + var fcnt = blob.read_shift(2); + blob.l += 6; + var start_cd = blob.read_shift(4); + + /* parse central directory */ + blob.l = start_cd; + + for(i = 0; i < fcnt; ++i) { + /* trust local file header instead of CD entry */ + blob.l += 20; + var csz = blob.read_shift(4); + var usz = blob.read_shift(4); + var namelen = blob.read_shift(2); + var efsz = blob.read_shift(2); + var fcsz = blob.read_shift(2); + blob.l += 8; + var offset = blob.read_shift(4); + var EF = parse_extra_field(/*::(*/blob.slice(blob.l+namelen, blob.l+namelen+efsz)/*:: :any)*/); + blob.l += namelen + efsz + fcsz; + + var L = blob.l; + blob.l = offset + 4; + parse_local_file(blob, csz, usz, o, EF); + blob.l = L; + } + + return o; +} + + +/* head starts just after local file header signature */ +function parse_local_file(blob/*:CFBlob*/, csz/*:number*/, usz/*:number*/, o/*:CFBContainer*/, EF) { + /* [local file header] */ + blob.l += 2; + var flags = blob.read_shift(2); + var meth = blob.read_shift(2); + var date = parse_dos_date(blob); + + if(flags & 0x2041) throw new Error("Unsupported ZIP encryption"); + var crc32 = blob.read_shift(4); + var _csz = blob.read_shift(4); + var _usz = blob.read_shift(4); + + var namelen = blob.read_shift(2); + var efsz = blob.read_shift(2); + + // TODO: flags & (1<<11) // UTF8 + var name = ""; for(var i = 0; i < namelen; ++i) name += String.fromCharCode(blob[blob.l++]); + if(efsz) { + var ef = parse_extra_field(/*::(*/blob.slice(blob.l, blob.l + efsz)/*:: :any)*/); + if((ef[0x5455]||{}).mt) date = ef[0x5455].mt; + if(((EF||{})[0x5455]||{}).mt) date = EF[0x5455].mt; + } + blob.l += efsz; + + /* [encryption header] */ + + /* [file data] */ + var data = blob.slice(blob.l, blob.l + _csz); + switch(meth) { + case 8: data = _inflateRawSync(blob, _usz); break; + case 0: break; + default: throw new Error("Unsupported ZIP Compression method " + meth); + } + + /* [data descriptor] */ + var wrn = false; + if(flags & 8) { + crc32 = blob.read_shift(4); + if(crc32 == 0x08074b50) { crc32 = blob.read_shift(4); wrn = true; } + _csz = blob.read_shift(4); + _usz = blob.read_shift(4); + } + + if(_csz != csz) warn_or_throw(wrn, "Bad compressed size: " + csz + " != " + _csz); + if(_usz != usz) warn_or_throw(wrn, "Bad uncompressed size: " + usz + " != " + _usz); + var _crc32 = CRC32.buf(data, 0); + if(crc32 != _crc32) warn_or_throw(wrn, "Bad CRC32 checksum: " + crc32 + " != " + _crc32); + cfb_add(o, name, data, {unsafe: true, mt: date}); +} diff --git a/bits/83_zwrite.js b/bits/83_zwrite.js new file mode 100644 index 0000000..6797bee --- /dev/null +++ b/bits/83_zwrite.js @@ -0,0 +1,102 @@ +function write_zip(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes*/ { + var _opts = options || {}; + var out = [], cdirs = []; + var o/*:CFBlob*/ = new_buf(1); + var method = (_opts.compression ? 8 : 0), flags = 0; + var desc = false; + if(desc) flags |= 8; + var i = 0, j = 0; + + var start_cd = 0, fcnt = 0; + var root = cfb.FullPaths[0], fp = root, fi = cfb.FileIndex[0]; + var crcs = []; + var sz_cd = 0; + + for(i = 1; i < cfb.FullPaths.length; ++i) { + fp = cfb.FullPaths[i].slice(root.length); fi = cfb.FileIndex[i]; + if(!fi.size || !fi.content || fp == "\u0001Sh33tJ5") continue; + var start = start_cd; + + /* TODO: CP437 filename */ + var namebuf = new_buf(fp.length); + for(j = 0; j < fp.length; ++j) namebuf.write_shift(1, fp.charCodeAt(j) & 0x7F); + namebuf = namebuf.slice(0, namebuf.l); + crcs[fcnt] = CRC32.buf(/*::((*/fi.content/*::||[]):any)*/, 0); + + var outbuf = fi.content/*::||[]*/; + if(method == 8) outbuf = _deflateRawSync(outbuf); + + /* local file header */ + o = new_buf(30); + o.write_shift(4, 0x04034b50); + o.write_shift(2, 20); + o.write_shift(2, flags); + o.write_shift(2, method); + /* TODO: last mod file time/date */ + if(fi.mt) write_dos_date(o, fi.mt); + else o.write_shift(4, 0); + o.write_shift(-4, (flags & 8) ? 0 : crcs[fcnt]); + o.write_shift(4, (flags & 8) ? 0 : outbuf.length); + o.write_shift(4, (flags & 8) ? 0 : /*::(*/fi.content/*::||[])*/.length); + o.write_shift(2, namebuf.length); + o.write_shift(2, 0); + + start_cd += o.length; + out.push(o); + start_cd += namebuf.length; + out.push(namebuf); + + /* TODO: encryption header ? */ + start_cd += outbuf.length; + out.push(outbuf); + + /* data descriptor */ + if(flags & 8) { + o = new_buf(12); + o.write_shift(-4, crcs[fcnt]); + o.write_shift(4, outbuf.length); + o.write_shift(4, /*::(*/fi.content/*::||[])*/.length); + start_cd += o.l; + out.push(o); + } + + /* central directory */ + o = new_buf(46); + o.write_shift(4, 0x02014b50); + o.write_shift(2, 0); + o.write_shift(2, 20); + o.write_shift(2, flags); + o.write_shift(2, method); + o.write_shift(4, 0); /* TODO: last mod file time/date */ + o.write_shift(-4, crcs[fcnt]); + + o.write_shift(4, outbuf.length); + o.write_shift(4, /*::(*/fi.content/*::||[])*/.length); + o.write_shift(2, namebuf.length); + o.write_shift(2, 0); + o.write_shift(2, 0); + o.write_shift(2, 0); + o.write_shift(2, 0); + o.write_shift(4, 0); + o.write_shift(4, start); + + sz_cd += o.l; + cdirs.push(o); + sz_cd += namebuf.length; + cdirs.push(namebuf); + ++fcnt; + } + + /* end of central directory */ + o = new_buf(22); + o.write_shift(4, 0x06054b50); + o.write_shift(2, 0); + o.write_shift(2, 0); + o.write_shift(2, fcnt); + o.write_shift(2, fcnt); + o.write_shift(4, sz_cd); + o.write_shift(4, start_cd); + o.write_shift(2, 0); + + return bconcat(([bconcat((out/*:any*/)), bconcat(cdirs), o]/*:any*/)); +} diff --git a/bits/85_api.js b/bits/85_api.js index ff172c3..e34bf03 100644 --- a/bits/85_api.js +++ b/bits/85_api.js @@ -25,6 +25,8 @@ function cfb_add(cfb/*:CFBContainer*/, name/*:string*/, content/*:?RawBytes*/, o file.size = content ? content.length : 0; if(opts) { if(opts.CLSID) file.clsid = opts.CLSID; + if(opts.mt) file.mt = opts.mt; + if(opts.ct) file.ct = opts.ct; } return file; } diff --git a/bits/88_cfbexports.js b/bits/88_cfbexports.js index 5ce2829..5e5a273 100644 --- a/bits/88_cfbexports.js +++ b/bits/88_cfbexports.js @@ -13,6 +13,9 @@ exports.utils = { CheckField: CheckField, prep_blob: prep_blob, bconcat: bconcat, + use_zlib: use_zlib, + _deflateRaw: _deflate, + _inflateRaw: _inflate, consts: consts }; diff --git a/cfb.flow.js b/cfb.flow.js index a27be02..06792cc 100644 --- a/cfb.flow.js +++ b/cfb.flow.js @@ -2,7 +2,7 @@ /* vim: set ts=2: */ /*jshint eqnull:true */ /*exported CFB */ -/*global module, require:false, process:false, Buffer:false, Uint8Array:false */ +/*global module, require:false, process:false, Buffer:false, Uint8Array:false, Uint16Array:false */ var Base64 = (function make_b64(){ var map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; @@ -55,9 +55,11 @@ var Buffer_from = /*::(*/function(){}/*:: :any)*/; if(typeof Buffer !== 'undefined') { var nbfs = !Buffer.from; if(!nbfs) try { Buffer.from("foo", "utf8"); } catch(e) { nbfs = true; } - Buffer_from = nbfs ? function(buf, enc) { return (enc) ? new Buffer(buf, enc) : new Buffer(buf); } : Buffer.from.bind(Buffer); + Buffer_from = /*::((*/nbfs ? function(buf, enc) { return (enc) ? new Buffer(buf, enc) : new Buffer(buf); } : Buffer.from.bind(Buffer)/*::) :any)*/; // $FlowIgnore if(!Buffer.alloc) Buffer.alloc = function(n) { return new Buffer(n); }; + // $FlowIgnore + if(!Buffer.allocUnsafe) Buffer.allocUnsafe = function(n) { return new Buffer(n); }; } function new_raw_buf(len/*:number*/) { @@ -66,8 +68,14 @@ function new_raw_buf(len/*:number*/) { /* jshint +W056 */ } -var s2a = function s2a(s/*:string*/) { - if(has_buf) return Buffer.from(s, "binary"); +function new_unsafe_buf(len/*:number*/) { + /* jshint -W056 */ + return has_buf ? Buffer.allocUnsafe(len) : new Array(len); + /* jshint +W056 */ +} + +var s2a = function s2a(s/*:string*/)/*:any*/ { + if(has_buf) return Buffer_from(s, "binary"); return s.split("").map(function(x/*:string*/)/*:number*/{ return x.charCodeAt(0) & 0xff; }); }; @@ -96,7 +104,6 @@ if(has_buf/*:: && typeof Buffer !== 'undefined'*/) { }; __hexlify = function(b/*:RawBytes|CFBlob*/,s/*:number*/,l/*:number*/)/*:string*/ { return Buffer.isBuffer(b)/*:: && b instanceof Buffer*/ ? b.toString('hex',s,s+l) : ___hexlify(b,s,l); }; __toBuffer = function(bufs/*:Array>*/)/*:RawBytes*/ { return (bufs[0].length > 0 && Buffer.isBuffer(bufs[0][0])) ? Buffer.concat((bufs[0]/*:any*/)) : ___toBuffer(bufs);}; - // $FlowIgnore s2a = function(s/*:string*/)/*:RawBytes*/ { return Buffer_from(s, "binary"); }; bconcat = function(bufs/*:Array*/)/*:RawBytes*/ { return Buffer.isBuffer(bufs[0]) ? Buffer.concat(/*::(*/bufs/*:: :any)*/) : __bconcat(bufs); }; } @@ -184,10 +191,111 @@ type SectorList = { } type CFBFiles = {[n:string]:CFBEntry}; */ +/* crc32.js (C) 2014-present SheetJS -- http://sheetjs.com */ +/* vim: set ts=2: */ +/*exported CRC32 */ +var CRC32; +(function (factory) { + /*jshint ignore:start */ + /*eslint-disable */ + factory(CRC32 = {}); + /*eslint-enable */ + /*jshint ignore:end */ +}(function(CRC32) { +CRC32.version = '1.2.0'; +/* see perf/crc32table.js */ +/*global Int32Array */ +function signed_crc_table()/*:any*/ { + var c = 0, table/*:Array*/ = new Array(256); + + for(var n =0; n != 256; ++n){ + c = n; + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + table[n] = c; + } + + return typeof Int32Array !== 'undefined' ? new Int32Array(table) : table; +} + +var T = signed_crc_table(); +function crc32_bstr(bstr/*:string*/, seed/*:number*/)/*:number*/ { + var C = seed ^ -1, L = bstr.length - 1; + for(var i = 0; i < L;) { + C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF]; + C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF]; + } + if(i === L) C = (C>>>8) ^ T[(C ^ bstr.charCodeAt(i))&0xFF]; + return C ^ -1; +} + +function crc32_buf(buf/*:Uint8Array|Array*/, seed/*:number*/)/*:number*/ { + if(buf.length > 10000) return crc32_buf_8(buf, seed); + var C = seed ^ -1, L = buf.length - 3; + for(var i = 0; i < L;) { + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + } + while(i < L+3) C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + return C ^ -1; +} + +function crc32_buf_8(buf/*:Uint8Array|Array*/, seed/*:number*/)/*:number*/ { + var C = seed ^ -1, L = buf.length - 7; + for(var i = 0; i < L;) { + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + } + while(i < L+7) C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + return C ^ -1; +} + +function crc32_str(str/*:string*/, seed/*:number*/)/*:number*/ { + var C = seed ^ -1; + for(var i = 0, L=str.length, c, d; i < L;) { + c = str.charCodeAt(i++); + if(c < 0x80) { + C = (C>>>8) ^ T[(C ^ c)&0xFF]; + } else if(c < 0x800) { + C = (C>>>8) ^ T[(C ^ (192|((c>>6)&31)))&0xFF]; + C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF]; + } else if(c >= 0xD800 && c < 0xE000) { + c = (c&1023)+64; d = str.charCodeAt(i++)&1023; + C = (C>>>8) ^ T[(C ^ (240|((c>>8)&7)))&0xFF]; + C = (C>>>8) ^ T[(C ^ (128|((c>>2)&63)))&0xFF]; + C = (C>>>8) ^ T[(C ^ (128|((d>>6)&15)|((c&3)<<4)))&0xFF]; + C = (C>>>8) ^ T[(C ^ (128|(d&63)))&0xFF]; + } else { + C = (C>>>8) ^ T[(C ^ (224|((c>>12)&15)))&0xFF]; + C = (C>>>8) ^ T[(C ^ (128|((c>>6)&63)))&0xFF]; + C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF]; + } + } + return C ^ -1; +} +CRC32.table = T; +CRC32.bstr = crc32_bstr; +CRC32.buf = crc32_buf; +CRC32.str = crc32_str; +})); /* [MS-CFB] v20171201 */ var CFB = (function _CFB(){ var exports/*:CFBModule*/ = /*::(*/{}/*:: :any)*/; -exports.version = '1.0.8'; +exports.version = '1.1.0'; /* [MS-CFB] 2.6.4 */ function namecmp(l/*:string*/, r/*:string*/)/*:number*/ { var L = l.split("/"), R = r.split("/"); @@ -208,9 +316,75 @@ function filename(p/*:string*/)/*:string*/ { var c = p.lastIndexOf("/"); return (c === -1) ? p : p.slice(c+1); } +/* -------------------------------------------------------------------------- */ +/* DOS Date format: + high|YYYYYYYm.mmmddddd.HHHHHMMM.MMMSSSSS|low + add 1980 to stored year + stored second should be doubled +*/ + +/* write JS date to buf as a DOS date */ +function write_dos_date(buf/*:CFBlob*/, date/*:Date|string*/) { + if(typeof date === "string") date = new Date(date); + var hms/*:number*/ = date.getHours(); + hms = hms << 6 | date.getMinutes(); + hms = hms << 5 | (date.getSeconds()>>>1); + buf.write_shift(2, hms); + var ymd/*:number*/ = (date.getFullYear() - 1980); + ymd = ymd << 4 | (date.getMonth()+1); + ymd = ymd << 5 | date.getDate(); + buf.write_shift(2, ymd); +} + +/* read four bytes from buf and interpret as a DOS date */ +function parse_dos_date(buf/*:CFBlob*/)/*:Date*/ { + var hms = buf.read_shift(2) & 0xFFFF; + var ymd = buf.read_shift(2) & 0xFFFF; + var val = new Date(); + var d = ymd & 0x1F; ymd >>>= 5; + var m = ymd & 0x0F; ymd >>>= 4; + val.setMilliseconds(0); + val.setFullYear(ymd + 1980); + val.setMonth(m-1); + val.setDate(d); + var S = hms & 0x1F; hms >>>= 5; + var M = hms & 0x3F; hms >>>= 6; + val.setHours(hms); + val.setMinutes(M); + val.setSeconds(S<<1); + return val; +} +function parse_extra_field(blob/*:CFBlob*/)/*:any*/ { + prep_blob(blob, 0); + var o = /*::(*/{}/*:: :any)*/; + var flags = 0; + while(blob.l <= blob.length - 4) { + var type = blob.read_shift(2); + var sz = blob.read_shift(2), tgt = blob.l + sz; + var p = {}; + switch(type) { + /* UNIX-style Timestamps */ + case 0x5455: { + flags = blob.read_shift(1); + if(flags & 1) p.mtime = blob.read_shift(4); + /* for some reason, CD flag corresponds to LFH */ + if(sz > 5) { + if(flags & 2) p.atime = blob.read_shift(4); + if(flags & 4) p.ctime = blob.read_shift(4); + } + if(p.mtime) p.mt = new Date(p.mtime*1000); + } + break; + } + blob.l = tgt; + o[type] = p; + } + return o; +} var fs/*:: = require('fs'); */; function get_fs() { return fs || (fs = require('fs')); } function parse(file/*:RawBytes*/, options/*:CFBReadOpts*/)/*:CFBContainer*/ { +if(file[0] == 0x50 && file[1] == 0x4b) return parse_zip(file, options); if(file.length < 512) throw new Error("CFB file size " + file.length + " < 512"); var mver = 3; var ssz = 512; @@ -231,6 +405,8 @@ var mv = check_get_mver(blob); mver = mv[0]; switch(mver) { case 3: ssz = 512; break; case 4: ssz = 4096; break; + case 0: if(mv[1] == 0) return parse_zip(file, options); + /* falls through */ default: throw new Error("Major Version: Expected 3 or 4 saw " + mver); } @@ -309,6 +485,7 @@ return o; /* [MS-CFB] 2.2 Compound File Header -- read up to major version */ function check_get_mver(blob/*:CFBlob*/)/*:[number, number]*/ { + if(blob[blob.l] == 0x50 && blob[blob.l + 1] == 0x4b) return [0, 0]; // header signature 8 blob.chk(HEADER_SIGNATURE, 'Header Signature: '); @@ -626,6 +803,7 @@ function rebuild_cfb(cfb/*:CFBContainer*/, f/*:?boolean*/)/*:void*/ { function _write(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes*/ { var _opts = options || {}; rebuild_cfb(cfb); + if(_opts.fileType == 'zip') return write_zip(cfb, _opts); var L = (function(cfb/*:CFBContainer*/)/*:Array*/{ var mini_size = 0, fat_size = 0; for(var i = 0; i < cfb.FileIndex.length; ++i) { @@ -826,6 +1004,551 @@ function write(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes|string } return o; } +/* node < 8.1 zlib does not expose bytesRead, so default to pure JS */ +var _zlib; +function use_zlib(zlib) { try { + var InflateRaw = zlib.InflateRaw; + var InflRaw = new InflateRaw(); + InflRaw._processChunk(new Uint8Array([3, 0]), InflRaw._finishFlushFlag); + if(InflRaw.bytesRead) _zlib = zlib; + else throw new Error("zlib does not expose bytesRead"); +} catch(e) {console.error("cannot use native zlib: " + (e.message || e)); } } + +function _inflateRawSync(payload, usz) { + if(!_zlib) return _inflate(payload, usz); + var InflateRaw = _zlib.InflateRaw; + var InflRaw = new InflateRaw(); + var out = InflRaw._processChunk(payload.slice(payload.l), InflRaw._finishFlushFlag); + payload.l += InflRaw.bytesRead; + return out; +} + +function _deflateRawSync(payload) { + return _zlib ? _zlib.deflateRawSync(payload) : _deflate(payload); +} +var CLEN_ORDER = [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ]; + +/* LEN_ID = [ 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285 ]; */ +var LEN_LN = [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13 , 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 ]; + +/* DST_ID = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 ]; */ +var DST_LN = [ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 ]; + +function bit_swap_8(n) { var t = (((((n<<1)|(n<<11)) & 0x22110) | (((n<<5)|(n<<15)) & 0x88440))); return ((t>>16) | (t>>8) |t)&0xFF; } + +var use_typed_arrays = typeof Uint8Array !== 'undefined'; + +var bitswap8 = use_typed_arrays ? new Uint8Array(1<<8) : []; +for(var q = 0; q < (1<<8); ++q) bitswap8[q] = bit_swap_8(q); + +function bit_swap_n(n, b) { + var rev = bitswap8[n & 0xFF]; + if(b <= 8) return rev >>> (8-b); + rev = (rev << 8) | bitswap8[(n>>8)&0xFF]; + if(b <= 16) return rev >>> (16-b); + rev = (rev << 8) | bitswap8[(n>>16)&0xFF]; + return rev >>> (24-b); +} + +/* helpers for unaligned bit reads */ +function read_bits_2(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 6 ? 0 : buf[h+1]<<8))>>>w)& 0x03; } +function read_bits_3(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 5 ? 0 : buf[h+1]<<8))>>>w)& 0x07; } +function read_bits_4(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 4 ? 0 : buf[h+1]<<8))>>>w)& 0x0F; } +function read_bits_5(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 3 ? 0 : buf[h+1]<<8))>>>w)& 0x1F; } +function read_bits_7(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 1 ? 0 : buf[h+1]<<8))>>>w)& 0x7F; } + +/* works up to n = 3 * 8 + 1 = 25 */ +function read_bits_n(buf, bl, n) { + var w = (bl&7), h = (bl>>>3), f = ((1<>> w; + if(n < 8 - w) return v & f; + v |= buf[h+1]<<(8-w); + if(n < 16 - w) return v & f; + v |= buf[h+2]<<(16-w); + if(n < 24 - w) return v & f; + v |= buf[h+3]<<(24-w); + return v & f; +} + +/* until ArrayBuffer#realloc is a thing, fake a realloc */ +function realloc(b, sz/*:number*/) { + var L = b.length, M = 2*L > sz ? 2*L : sz + 5, i = 0; + if(L >= sz) return b; + if(has_buf) { + var o = new_unsafe_buf(M); + // $FlowIgnore + if(b.copy) b.copy(o); + else for(; i < b.length; ++i) o[i] = b[i]; + return o; + } else if(use_typed_arrays) { + var a = new Uint8Array(M); + if(a.set) a.set(b); + else for(; i < b.length; ++i) a[i] = b[i]; + return a; + } + b.length = M; + return b; +} + +/* zero-filled arrays for older browsers */ +function zero_fill_array(n) { + var o = new Array(n); + for(var i = 0; i < n; ++i) o[i] = 0; + return o; +}var _deflate = (function() { +var _deflateRaw = (function() { + return function deflateRaw(data, out) { + var boff = 0; + while(boff < data.length) { + var L = Math.min(0xFFFF, data.length - boff); + var h = boff + L == data.length; + /* TODO: this is only type 0 stored */ + out.write_shift(1, +h); + out.write_shift(2, L); + out.write_shift(2, (~L) & 0xFFFF); + while(L-- > 0) out[out.l++] = data[boff++]; + } + return out.l; + }; +})(); + +return function(data) { + var buf = new_buf(50+Math.floor(data.length*1.1)); + var off = _deflateRaw(data, buf); + return buf.slice(0, off); +}; +})(); +/* modified inflate function also moves original read head */ + +/* build tree (used for literals and lengths) */ +function build_tree(clens, cmap, MAX/*:number*/)/*:number*/ { + var maxlen = 1, w = 0, i = 0, j = 0, ccode = 0, L = clens.length; + + var bl_count = use_typed_arrays ? new Uint16Array(32) : zero_fill_array(32); + for(i = 0; i < 32; ++i) bl_count[i] = 0; + + for(i = L; i < MAX; ++i) clens[i] = 0; + L = clens.length; + + var ctree = use_typed_arrays ? new Uint16Array(L) : zero_fill_array(L); // [] + + /* build code tree */ + for(i = 0; i < L; ++i) { + bl_count[(w = clens[i])]++; + if(maxlen < w) maxlen = w; + ctree[i] = 0; + } + bl_count[0] = 0; + for(i = 1; i <= maxlen; ++i) bl_count[i+16] = (ccode = (ccode + bl_count[i-1])<<1); + for(i = 0; i < L; ++i) { + ccode = clens[i]; + if(ccode != 0) ctree[i] = bl_count[ccode+16]++; + } + + /* cmap[maxlen + 4 bits] = (off&15) + (lit<<4) reverse mapping */ + var cleni = 0; + for(i = 0; i < L; ++i) { + cleni = clens[i]; + if(cleni != 0) { + ccode = bit_swap_n(ctree[i], maxlen)>>(maxlen-cleni); + for(j = (1<<(maxlen + 4 - cleni)) - 1; j>=0; --j) + cmap[ccode|(j<*/ = []; + var i = 0; + for(;i<32; i++) dlens.push(5); + build_tree(dlens, fix_dmap, 32); + + var clens/*:Array*/ = []; + i = 0; + for(; i<=143; i++) clens.push(8); + for(; i<=255; i++) clens.push(9); + for(; i<=279; i++) clens.push(7); + for(; i<=287; i++) clens.push(8); + build_tree(clens, fix_lmap, 288); +})(); + +var dyn_lmap = use_typed_arrays ? new Uint16Array(32768) : zero_fill_array(32768); +var dyn_dmap = use_typed_arrays ? new Uint16Array(32768) : zero_fill_array(32768); +var dyn_cmap = use_typed_arrays ? new Uint16Array(128) : zero_fill_array(128); +var dyn_len_1 = 1, dyn_len_2 = 1; + +/* 5.5.3 Expanding Huffman Codes */ +function dyn(data, boff/*:number*/) { + /* nomenclature from RFC1951 refers to bit values; these are offset by the implicit constant */ + var _HLIT = read_bits_5(data, boff) + 257; boff += 5; + var _HDIST = read_bits_5(data, boff) + 1; boff += 5; + var _HCLEN = read_bits_4(data, boff) + 4; boff += 4; + var w = 0; + + /* grab and store code lengths */ + var clens = use_typed_arrays ? new Uint8Array(19) : zero_fill_array(19); + var ctree = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]; + var maxlen = 1; + var bl_count = use_typed_arrays ? new Uint8Array(8) : zero_fill_array(8); + var next_code = use_typed_arrays ? new Uint8Array(8) : zero_fill_array(8); + var L = clens.length; /* 19 */ + for(var i = 0; i < _HCLEN; ++i) { + clens[CLEN_ORDER[i]] = w = read_bits_3(data, boff); + if(maxlen < w) maxlen = w; + bl_count[w]++; + boff += 3; + } + + /* build code tree */ + var ccode = 0; + bl_count[0] = 0; + for(i = 1; i <= maxlen; ++i) next_code[i] = ccode = (ccode + bl_count[i-1])<<1; + for(i = 0; i < L; ++i) if((ccode = clens[i]) != 0) ctree[i] = next_code[ccode]++; + /* cmap[7 bits from stream] = (off&7) + (lit<<3) */ + var cleni = 0; + for(i = 0; i < L; ++i) { + cleni = clens[i]; + if(cleni != 0) { + ccode = bitswap8[ctree[i]]>>(8-cleni); + for(var j = (1<<(7-cleni))-1; j>=0; --j) dyn_cmap[ccode|(j<*/ = []; + maxlen = 1; + for(; hcodes.length < _HLIT + _HDIST;) { + ccode = dyn_cmap[read_bits_7(data, boff)]; + boff += ccode & 7; + switch((ccode >>>= 3)) { + case 16: + w = 3 + read_bits_2(data, boff); boff += 2; + ccode = hcodes[hcodes.length - 1]; + while(w-- > 0) hcodes.push(ccode); + break; + case 17: + w = 3 + read_bits_3(data, boff); boff += 3; + while(w-- > 0) hcodes.push(0); + break; + case 18: + w = 11 + read_bits_7(data, boff); boff += 7; + while(w -- > 0) hcodes.push(0); + break; + default: + hcodes.push(ccode); + if(maxlen < ccode) maxlen = ccode; + break; + } + } + + /* build literal / length trees */ + var h1 = hcodes.slice(0, _HLIT), h2 = hcodes.slice(_HLIT); + for(i = _HLIT; i < 286; ++i) h1[i] = 0; + for(i = _HDIST; i < 30; ++i) h2[i] = 0; + dyn_len_1 = build_tree(h1, dyn_lmap, 286); + dyn_len_2 = build_tree(h2, dyn_dmap, 30); + return boff; +} + +/* return [ data, bytesRead ] */ +function inflate(data, usz/*:number*/) { + /* shortcircuit for empty buffer [0x03, 0x00] */ + if(data[0] == 3 && !(data[1] & 0x3)) { return [new_raw_buf(usz), 2]; } + + /* bit offset */ + var boff = 0; + + /* header includes final bit and type bits */ + var header = 0; + + var outbuf = new_unsafe_buf(usz ? usz : (1<<18)); + var woff = 0; + var OL = outbuf.length>>>0; + var max_len_1 = 0, max_len_2 = 0; + + while((header&1) == 0) { + header = read_bits_3(data, boff); boff += 3; + if((header >>> 1) == 0) { + /* Stored block */ + if(boff & 7) boff += 8 - (boff&7); + /* 2 bytes sz, 2 bytes bit inverse */ + var sz = data[boff>>>3] | data[(boff>>>3)+1]<<8; + boff += 32; + /* push sz bytes */ + if(!usz && OL < woff + sz) { outbuf = realloc(outbuf, woff + sz); OL = outbuf.length; } + if(typeof data.copy === 'function') { + // $FlowIgnore + data.copy(outbuf, woff, boff>>>3, (boff>>>3)+sz); + woff += sz; boff += 8*sz; + } else while(sz-- > 0) { outbuf[woff++] = data[boff>>>3]; boff += 8; } + continue; + } else if((header >>> 1) == 1) { + /* Fixed Huffman */ + max_len_1 = 9; max_len_2 = 5; + } else { + /* Dynamic Huffman */ + boff = dyn(data, boff); + max_len_1 = dyn_len_1; max_len_2 = dyn_len_2; + } + if(!usz && (OL < woff + 32767)) { outbuf = realloc(outbuf, woff + 32767); OL = outbuf.length; } + for(;;) { // while(true) is apparently out of vogue in modern JS circles + /* ingest code and move read head */ + var bits = read_bits_n(data, boff, max_len_1); + var code = (header>>>1) == 1 ? fix_lmap[bits] : dyn_lmap[bits]; + boff += code & 15; code >>>= 4; + /* 0-255 are literals, 256 is end of block token, 257+ are copy tokens */ + if(((code>>>8)&0xFF) === 0) outbuf[woff++] = code; + else if(code == 256) break; + else { + code -= 257; + var len_eb = (code < 8) ? 0 : ((code-4)>>2); if(len_eb > 5) len_eb = 0; + var tgt = woff + LEN_LN[code]; + /* length extra bits */ + if(len_eb > 0) { + tgt += read_bits_n(data, boff, len_eb); + boff += len_eb; + } + + /* dist code */ + bits = read_bits_n(data, boff, max_len_2); + code = (header>>>1) == 1 ? fix_dmap[bits] : dyn_dmap[bits]; + boff += code & 15; code >>>= 4; + var dst_eb = (code < 4 ? 0 : (code-2)>>1); + var dst = DST_LN[code]; + /* dist extra bits */ + if(dst_eb > 0) { + dst += read_bits_n(data, boff, dst_eb); + boff += dst_eb; + } + + /* in the common case, manual byte copy is faster than TA set / Buffer copy */ + if(!usz && OL < tgt) { outbuf = realloc(outbuf, tgt); OL = outbuf.length; } + while(woff < tgt) { outbuf[woff] = outbuf[woff - dst]; ++woff; } + } + } + } + return [usz ? outbuf : outbuf.slice(0, woff), (boff+7)>>>3]; +} + +function _inflate(payload, usz) { + var data = payload.slice(payload.l||0); + var out = inflate(data, usz); + payload.l += out[1]; + return out[0]; +} + +function warn_or_throw(wrn, msg) { + if(wrn) { if(typeof console !== 'undefined') console.error(msg); } + else throw new Error(msg); +} + +function parse_zip(file/*:RawBytes*/, options/*:CFBReadOpts*/)/*:CFBContainer*/ { + var blob/*:CFBlob*/ = /*::(*/file/*:: :any)*/; + prep_blob(blob, 0); + + var FileIndex/*:CFBFileIndex*/ = [], FullPaths/*:Array*/ = []; + var o = { + FileIndex: FileIndex, + FullPaths: FullPaths + }; + init_cfb(o, { root: options.root }); + + /* find end of central directory, start just after signature */ + var i = blob.length - 4; + while((blob[i] != 0x50 || blob[i+1] != 0x4b || blob[i+2] != 0x05 || blob[i+3] != 0x06) && i >= 0) --i; + blob.l = i + 4; + + /* parse end of central directory */ + blob.l += 4; + var fcnt = blob.read_shift(2); + blob.l += 6; + var start_cd = blob.read_shift(4); + + /* parse central directory */ + blob.l = start_cd; + + for(i = 0; i < fcnt; ++i) { + /* trust local file header instead of CD entry */ + blob.l += 20; + var csz = blob.read_shift(4); + var usz = blob.read_shift(4); + var namelen = blob.read_shift(2); + var efsz = blob.read_shift(2); + var fcsz = blob.read_shift(2); + blob.l += 8; + var offset = blob.read_shift(4); + var EF = parse_extra_field(/*::(*/blob.slice(blob.l+namelen, blob.l+namelen+efsz)/*:: :any)*/); + blob.l += namelen + efsz + fcsz; + + var L = blob.l; + blob.l = offset + 4; + parse_local_file(blob, csz, usz, o, EF); + blob.l = L; + } + + return o; +} + + +/* head starts just after local file header signature */ +function parse_local_file(blob/*:CFBlob*/, csz/*:number*/, usz/*:number*/, o/*:CFBContainer*/, EF) { + /* [local file header] */ + blob.l += 2; + var flags = blob.read_shift(2); + var meth = blob.read_shift(2); + var date = parse_dos_date(blob); + + if(flags & 0x2041) throw new Error("Unsupported ZIP encryption"); + var crc32 = blob.read_shift(4); + var _csz = blob.read_shift(4); + var _usz = blob.read_shift(4); + + var namelen = blob.read_shift(2); + var efsz = blob.read_shift(2); + + // TODO: flags & (1<<11) // UTF8 + var name = ""; for(var i = 0; i < namelen; ++i) name += String.fromCharCode(blob[blob.l++]); + if(efsz) { + var ef = parse_extra_field(/*::(*/blob.slice(blob.l, blob.l + efsz)/*:: :any)*/); + if((ef[0x5455]||{}).mt) date = ef[0x5455].mt; + if(((EF||{})[0x5455]||{}).mt) date = EF[0x5455].mt; + } + blob.l += efsz; + + /* [encryption header] */ + + /* [file data] */ + var data = blob.slice(blob.l, blob.l + _csz); + switch(meth) { + case 8: data = _inflateRawSync(blob, _usz); break; + case 0: break; + default: throw new Error("Unsupported ZIP Compression method " + meth); + } + + /* [data descriptor] */ + var wrn = false; + if(flags & 8) { + crc32 = blob.read_shift(4); + if(crc32 == 0x08074b50) { crc32 = blob.read_shift(4); wrn = true; } + _csz = blob.read_shift(4); + _usz = blob.read_shift(4); + } + + if(_csz != csz) warn_or_throw(wrn, "Bad compressed size: " + csz + " != " + _csz); + if(_usz != usz) warn_or_throw(wrn, "Bad uncompressed size: " + usz + " != " + _usz); + var _crc32 = CRC32.buf(data, 0); + if(crc32 != _crc32) warn_or_throw(wrn, "Bad CRC32 checksum: " + crc32 + " != " + _crc32); + cfb_add(o, name, data, {unsafe: true, mt: date}); +} +function write_zip(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes*/ { + var _opts = options || {}; + var out = [], cdirs = []; + var o/*:CFBlob*/ = new_buf(1); + var method = (_opts.compression ? 8 : 0), flags = 0; + var desc = false; + if(desc) flags |= 8; + var i = 0, j = 0; + + var start_cd = 0, fcnt = 0; + var root = cfb.FullPaths[0], fp = root, fi = cfb.FileIndex[0]; + var crcs = []; + var sz_cd = 0; + + for(i = 1; i < cfb.FullPaths.length; ++i) { + fp = cfb.FullPaths[i].slice(root.length); fi = cfb.FileIndex[i]; + if(!fi.size || !fi.content || fp == "\u0001Sh33tJ5") continue; + var start = start_cd; + + /* TODO: CP437 filename */ + var namebuf = new_buf(fp.length); + for(j = 0; j < fp.length; ++j) namebuf.write_shift(1, fp.charCodeAt(j) & 0x7F); + namebuf = namebuf.slice(0, namebuf.l); + crcs[fcnt] = CRC32.buf(/*::((*/fi.content/*::||[]):any)*/, 0); + + var outbuf = fi.content/*::||[]*/; + if(method == 8) outbuf = _deflateRawSync(outbuf); + + /* local file header */ + o = new_buf(30); + o.write_shift(4, 0x04034b50); + o.write_shift(2, 20); + o.write_shift(2, flags); + o.write_shift(2, method); + /* TODO: last mod file time/date */ + if(fi.mt) write_dos_date(o, fi.mt); + else o.write_shift(4, 0); + o.write_shift(-4, (flags & 8) ? 0 : crcs[fcnt]); + o.write_shift(4, (flags & 8) ? 0 : outbuf.length); + o.write_shift(4, (flags & 8) ? 0 : /*::(*/fi.content/*::||[])*/.length); + o.write_shift(2, namebuf.length); + o.write_shift(2, 0); + + start_cd += o.length; + out.push(o); + start_cd += namebuf.length; + out.push(namebuf); + + /* TODO: encryption header ? */ + start_cd += outbuf.length; + out.push(outbuf); + + /* data descriptor */ + if(flags & 8) { + o = new_buf(12); + o.write_shift(-4, crcs[fcnt]); + o.write_shift(4, outbuf.length); + o.write_shift(4, /*::(*/fi.content/*::||[])*/.length); + start_cd += o.l; + out.push(o); + } + + /* central directory */ + o = new_buf(46); + o.write_shift(4, 0x02014b50); + o.write_shift(2, 0); + o.write_shift(2, 20); + o.write_shift(2, flags); + o.write_shift(2, method); + o.write_shift(4, 0); /* TODO: last mod file time/date */ + o.write_shift(-4, crcs[fcnt]); + + o.write_shift(4, outbuf.length); + o.write_shift(4, /*::(*/fi.content/*::||[])*/.length); + o.write_shift(2, namebuf.length); + o.write_shift(2, 0); + o.write_shift(2, 0); + o.write_shift(2, 0); + o.write_shift(2, 0); + o.write_shift(4, 0); + o.write_shift(4, start); + + sz_cd += o.l; + cdirs.push(o); + sz_cd += namebuf.length; + cdirs.push(namebuf); + ++fcnt; + } + + /* end of central directory */ + o = new_buf(22); + o.write_shift(4, 0x06054b50); + o.write_shift(2, 0); + o.write_shift(2, 0); + o.write_shift(2, fcnt); + o.write_shift(2, fcnt); + o.write_shift(4, sz_cd); + o.write_shift(4, start_cd); + o.write_shift(2, 0); + + return bconcat(([bconcat((out/*:any*/)), bconcat(cdirs), o]/*:any*/)); +} function cfb_new(opts/*:?any*/)/*:CFBContainer*/ { var o/*:CFBContainer*/ = ({}/*:any*/); init_cfb(o, opts); @@ -853,6 +1576,8 @@ function cfb_add(cfb/*:CFBContainer*/, name/*:string*/, content/*:?RawBytes*/, o file.size = content ? content.length : 0; if(opts) { if(opts.CLSID) file.clsid = opts.CLSID; + if(opts.mt) file.mt = opts.mt; + if(opts.ct) file.ct = opts.ct; } return file; } @@ -896,6 +1621,9 @@ exports.utils = { CheckField: CheckField, prep_blob: prep_blob, bconcat: bconcat, + use_zlib: use_zlib, + _deflateRaw: _deflate, + _inflateRaw: _inflate, consts: consts }; diff --git a/cfb.js b/cfb.js index 9f86177..dd56c02 100644 --- a/cfb.js +++ b/cfb.js @@ -2,7 +2,7 @@ /* vim: set ts=2: */ /*jshint eqnull:true */ /*exported CFB */ -/*global module, require:false, process:false, Buffer:false, Uint8Array:false */ +/*global module, require:false, process:false, Buffer:false, Uint8Array:false, Uint16Array:false */ var Base64 = (function make_b64(){ var map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; @@ -58,6 +58,8 @@ if(typeof Buffer !== 'undefined') { Buffer_from = nbfs ? function(buf, enc) { return (enc) ? new Buffer(buf, enc) : new Buffer(buf); } : Buffer.from.bind(Buffer); // $FlowIgnore if(!Buffer.alloc) Buffer.alloc = function(n) { return new Buffer(n); }; + // $FlowIgnore + if(!Buffer.allocUnsafe) Buffer.allocUnsafe = function(n) { return new Buffer(n); }; } function new_raw_buf(len) { @@ -66,8 +68,14 @@ function new_raw_buf(len) { /* jshint +W056 */ } +function new_unsafe_buf(len) { + /* jshint -W056 */ + return has_buf ? Buffer.allocUnsafe(len) : new Array(len); + /* jshint +W056 */ +} + var s2a = function s2a(s) { - if(has_buf) return Buffer.from(s, "binary"); + if(has_buf) return Buffer_from(s, "binary"); return s.split("").map(function(x){ return x.charCodeAt(0) & 0xff; }); }; @@ -96,7 +104,6 @@ if(has_buf) { }; __hexlify = function(b,s,l) { return Buffer.isBuffer(b) ? b.toString('hex',s,s+l) : ___hexlify(b,s,l); }; __toBuffer = function(bufs) { return (bufs[0].length > 0 && Buffer.isBuffer(bufs[0][0])) ? Buffer.concat((bufs[0])) : ___toBuffer(bufs);}; - // $FlowIgnore s2a = function(s) { return Buffer_from(s, "binary"); }; bconcat = function(bufs) { return Buffer.isBuffer(bufs[0]) ? Buffer.concat(bufs) : __bconcat(bufs); }; } @@ -166,10 +173,111 @@ function new_buf(sz) { return o; } +/* crc32.js (C) 2014-present SheetJS -- http://sheetjs.com */ +/* vim: set ts=2: */ +/*exported CRC32 */ +var CRC32; +(function (factory) { + /*jshint ignore:start */ + /*eslint-disable */ + factory(CRC32 = {}); + /*eslint-enable */ + /*jshint ignore:end */ +}(function(CRC32) { +CRC32.version = '1.2.0'; +/* see perf/crc32table.js */ +/*global Int32Array */ +function signed_crc_table() { + var c = 0, table = new Array(256); + + for(var n =0; n != 256; ++n){ + c = n; + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + table[n] = c; + } + + return typeof Int32Array !== 'undefined' ? new Int32Array(table) : table; +} + +var T = signed_crc_table(); +function crc32_bstr(bstr, seed) { + var C = seed ^ -1, L = bstr.length - 1; + for(var i = 0; i < L;) { + C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF]; + C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF]; + } + if(i === L) C = (C>>>8) ^ T[(C ^ bstr.charCodeAt(i))&0xFF]; + return C ^ -1; +} + +function crc32_buf(buf, seed) { + if(buf.length > 10000) return crc32_buf_8(buf, seed); + var C = seed ^ -1, L = buf.length - 3; + for(var i = 0; i < L;) { + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + } + while(i < L+3) C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + return C ^ -1; +} + +function crc32_buf_8(buf, seed) { + var C = seed ^ -1, L = buf.length - 7; + for(var i = 0; i < L;) { + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + } + while(i < L+7) C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + return C ^ -1; +} + +function crc32_str(str, seed) { + var C = seed ^ -1; + for(var i = 0, L=str.length, c, d; i < L;) { + c = str.charCodeAt(i++); + if(c < 0x80) { + C = (C>>>8) ^ T[(C ^ c)&0xFF]; + } else if(c < 0x800) { + C = (C>>>8) ^ T[(C ^ (192|((c>>6)&31)))&0xFF]; + C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF]; + } else if(c >= 0xD800 && c < 0xE000) { + c = (c&1023)+64; d = str.charCodeAt(i++)&1023; + C = (C>>>8) ^ T[(C ^ (240|((c>>8)&7)))&0xFF]; + C = (C>>>8) ^ T[(C ^ (128|((c>>2)&63)))&0xFF]; + C = (C>>>8) ^ T[(C ^ (128|((d>>6)&15)|((c&3)<<4)))&0xFF]; + C = (C>>>8) ^ T[(C ^ (128|(d&63)))&0xFF]; + } else { + C = (C>>>8) ^ T[(C ^ (224|((c>>12)&15)))&0xFF]; + C = (C>>>8) ^ T[(C ^ (128|((c>>6)&63)))&0xFF]; + C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF]; + } + } + return C ^ -1; +} +CRC32.table = T; +CRC32.bstr = crc32_bstr; +CRC32.buf = crc32_buf; +CRC32.str = crc32_str; +})); /* [MS-CFB] v20171201 */ var CFB = (function _CFB(){ var exports = {}; -exports.version = '1.0.8'; +exports.version = '1.1.0'; /* [MS-CFB] 2.6.4 */ function namecmp(l, r) { var L = l.split("/"), R = r.split("/"); @@ -190,9 +298,75 @@ function filename(p) { var c = p.lastIndexOf("/"); return (c === -1) ? p : p.slice(c+1); } +/* -------------------------------------------------------------------------- */ +/* DOS Date format: + high|YYYYYYYm.mmmddddd.HHHHHMMM.MMMSSSSS|low + add 1980 to stored year + stored second should be doubled +*/ + +/* write JS date to buf as a DOS date */ +function write_dos_date(buf, date) { + if(typeof date === "string") date = new Date(date); + var hms = date.getHours(); + hms = hms << 6 | date.getMinutes(); + hms = hms << 5 | (date.getSeconds()>>>1); + buf.write_shift(2, hms); + var ymd = (date.getFullYear() - 1980); + ymd = ymd << 4 | (date.getMonth()+1); + ymd = ymd << 5 | date.getDate(); + buf.write_shift(2, ymd); +} + +/* read four bytes from buf and interpret as a DOS date */ +function parse_dos_date(buf) { + var hms = buf.read_shift(2) & 0xFFFF; + var ymd = buf.read_shift(2) & 0xFFFF; + var val = new Date(); + var d = ymd & 0x1F; ymd >>>= 5; + var m = ymd & 0x0F; ymd >>>= 4; + val.setMilliseconds(0); + val.setFullYear(ymd + 1980); + val.setMonth(m-1); + val.setDate(d); + var S = hms & 0x1F; hms >>>= 5; + var M = hms & 0x3F; hms >>>= 6; + val.setHours(hms); + val.setMinutes(M); + val.setSeconds(S<<1); + return val; +} +function parse_extra_field(blob) { + prep_blob(blob, 0); + var o = {}; + var flags = 0; + while(blob.l <= blob.length - 4) { + var type = blob.read_shift(2); + var sz = blob.read_shift(2), tgt = blob.l + sz; + var p = {}; + switch(type) { + /* UNIX-style Timestamps */ + case 0x5455: { + flags = blob.read_shift(1); + if(flags & 1) p.mtime = blob.read_shift(4); + /* for some reason, CD flag corresponds to LFH */ + if(sz > 5) { + if(flags & 2) p.atime = blob.read_shift(4); + if(flags & 4) p.ctime = blob.read_shift(4); + } + if(p.mtime) p.mt = new Date(p.mtime*1000); + } + break; + } + blob.l = tgt; + o[type] = p; + } + return o; +} var fs; function get_fs() { return fs || (fs = require('fs')); } function parse(file, options) { +if(file[0] == 0x50 && file[1] == 0x4b) return parse_zip(file, options); if(file.length < 512) throw new Error("CFB file size " + file.length + " < 512"); var mver = 3; var ssz = 512; @@ -213,6 +387,8 @@ var mv = check_get_mver(blob); mver = mv[0]; switch(mver) { case 3: ssz = 512; break; case 4: ssz = 4096; break; + case 0: if(mv[1] == 0) return parse_zip(file, options); + /* falls through */ default: throw new Error("Major Version: Expected 3 or 4 saw " + mver); } @@ -291,6 +467,7 @@ return o; /* [MS-CFB] 2.2 Compound File Header -- read up to major version */ function check_get_mver(blob) { + if(blob[blob.l] == 0x50 && blob[blob.l + 1] == 0x4b) return [0, 0]; // header signature 8 blob.chk(HEADER_SIGNATURE, 'Header Signature: '); @@ -608,6 +785,7 @@ function rebuild_cfb(cfb, f) { function _write(cfb, options) { var _opts = options || {}; rebuild_cfb(cfb); + if(_opts.fileType == 'zip') return write_zip(cfb, _opts); var L = (function(cfb){ var mini_size = 0, fat_size = 0; for(var i = 0; i < cfb.FileIndex.length; ++i) { @@ -802,6 +980,551 @@ function write(cfb, options) { } return o; } +/* node < 8.1 zlib does not expose bytesRead, so default to pure JS */ +var _zlib; +function use_zlib(zlib) { try { + var InflateRaw = zlib.InflateRaw; + var InflRaw = new InflateRaw(); + InflRaw._processChunk(new Uint8Array([3, 0]), InflRaw._finishFlushFlag); + if(InflRaw.bytesRead) _zlib = zlib; + else throw new Error("zlib does not expose bytesRead"); +} catch(e) {console.error("cannot use native zlib: " + (e.message || e)); } } + +function _inflateRawSync(payload, usz) { + if(!_zlib) return _inflate(payload, usz); + var InflateRaw = _zlib.InflateRaw; + var InflRaw = new InflateRaw(); + var out = InflRaw._processChunk(payload.slice(payload.l), InflRaw._finishFlushFlag); + payload.l += InflRaw.bytesRead; + return out; +} + +function _deflateRawSync(payload) { + return _zlib ? _zlib.deflateRawSync(payload) : _deflate(payload); +} +var CLEN_ORDER = [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ]; + +/* LEN_ID = [ 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285 ]; */ +var LEN_LN = [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13 , 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 ]; + +/* DST_ID = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 ]; */ +var DST_LN = [ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 ]; + +function bit_swap_8(n) { var t = (((((n<<1)|(n<<11)) & 0x22110) | (((n<<5)|(n<<15)) & 0x88440))); return ((t>>16) | (t>>8) |t)&0xFF; } + +var use_typed_arrays = typeof Uint8Array !== 'undefined'; + +var bitswap8 = use_typed_arrays ? new Uint8Array(1<<8) : []; +for(var q = 0; q < (1<<8); ++q) bitswap8[q] = bit_swap_8(q); + +function bit_swap_n(n, b) { + var rev = bitswap8[n & 0xFF]; + if(b <= 8) return rev >>> (8-b); + rev = (rev << 8) | bitswap8[(n>>8)&0xFF]; + if(b <= 16) return rev >>> (16-b); + rev = (rev << 8) | bitswap8[(n>>16)&0xFF]; + return rev >>> (24-b); +} + +/* helpers for unaligned bit reads */ +function read_bits_2(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 6 ? 0 : buf[h+1]<<8))>>>w)& 0x03; } +function read_bits_3(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 5 ? 0 : buf[h+1]<<8))>>>w)& 0x07; } +function read_bits_4(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 4 ? 0 : buf[h+1]<<8))>>>w)& 0x0F; } +function read_bits_5(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 3 ? 0 : buf[h+1]<<8))>>>w)& 0x1F; } +function read_bits_7(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 1 ? 0 : buf[h+1]<<8))>>>w)& 0x7F; } + +/* works up to n = 3 * 8 + 1 = 25 */ +function read_bits_n(buf, bl, n) { + var w = (bl&7), h = (bl>>>3), f = ((1<>> w; + if(n < 8 - w) return v & f; + v |= buf[h+1]<<(8-w); + if(n < 16 - w) return v & f; + v |= buf[h+2]<<(16-w); + if(n < 24 - w) return v & f; + v |= buf[h+3]<<(24-w); + return v & f; +} + +/* until ArrayBuffer#realloc is a thing, fake a realloc */ +function realloc(b, sz) { + var L = b.length, M = 2*L > sz ? 2*L : sz + 5, i = 0; + if(L >= sz) return b; + if(has_buf) { + var o = new_unsafe_buf(M); + // $FlowIgnore + if(b.copy) b.copy(o); + else for(; i < b.length; ++i) o[i] = b[i]; + return o; + } else if(use_typed_arrays) { + var a = new Uint8Array(M); + if(a.set) a.set(b); + else for(; i < b.length; ++i) a[i] = b[i]; + return a; + } + b.length = M; + return b; +} + +/* zero-filled arrays for older browsers */ +function zero_fill_array(n) { + var o = new Array(n); + for(var i = 0; i < n; ++i) o[i] = 0; + return o; +}var _deflate = (function() { +var _deflateRaw = (function() { + return function deflateRaw(data, out) { + var boff = 0; + while(boff < data.length) { + var L = Math.min(0xFFFF, data.length - boff); + var h = boff + L == data.length; + /* TODO: this is only type 0 stored */ + out.write_shift(1, +h); + out.write_shift(2, L); + out.write_shift(2, (~L) & 0xFFFF); + while(L-- > 0) out[out.l++] = data[boff++]; + } + return out.l; + }; +})(); + +return function(data) { + var buf = new_buf(50+Math.floor(data.length*1.1)); + var off = _deflateRaw(data, buf); + return buf.slice(0, off); +}; +})(); +/* modified inflate function also moves original read head */ + +/* build tree (used for literals and lengths) */ +function build_tree(clens, cmap, MAX) { + var maxlen = 1, w = 0, i = 0, j = 0, ccode = 0, L = clens.length; + + var bl_count = use_typed_arrays ? new Uint16Array(32) : zero_fill_array(32); + for(i = 0; i < 32; ++i) bl_count[i] = 0; + + for(i = L; i < MAX; ++i) clens[i] = 0; + L = clens.length; + + var ctree = use_typed_arrays ? new Uint16Array(L) : zero_fill_array(L); // [] + + /* build code tree */ + for(i = 0; i < L; ++i) { + bl_count[(w = clens[i])]++; + if(maxlen < w) maxlen = w; + ctree[i] = 0; + } + bl_count[0] = 0; + for(i = 1; i <= maxlen; ++i) bl_count[i+16] = (ccode = (ccode + bl_count[i-1])<<1); + for(i = 0; i < L; ++i) { + ccode = clens[i]; + if(ccode != 0) ctree[i] = bl_count[ccode+16]++; + } + + /* cmap[maxlen + 4 bits] = (off&15) + (lit<<4) reverse mapping */ + var cleni = 0; + for(i = 0; i < L; ++i) { + cleni = clens[i]; + if(cleni != 0) { + ccode = bit_swap_n(ctree[i], maxlen)>>(maxlen-cleni); + for(j = (1<<(maxlen + 4 - cleni)) - 1; j>=0; --j) + cmap[ccode|(j<>(8-cleni); + for(var j = (1<<(7-cleni))-1; j>=0; --j) dyn_cmap[ccode|(j<>>= 3)) { + case 16: + w = 3 + read_bits_2(data, boff); boff += 2; + ccode = hcodes[hcodes.length - 1]; + while(w-- > 0) hcodes.push(ccode); + break; + case 17: + w = 3 + read_bits_3(data, boff); boff += 3; + while(w-- > 0) hcodes.push(0); + break; + case 18: + w = 11 + read_bits_7(data, boff); boff += 7; + while(w -- > 0) hcodes.push(0); + break; + default: + hcodes.push(ccode); + if(maxlen < ccode) maxlen = ccode; + break; + } + } + + /* build literal / length trees */ + var h1 = hcodes.slice(0, _HLIT), h2 = hcodes.slice(_HLIT); + for(i = _HLIT; i < 286; ++i) h1[i] = 0; + for(i = _HDIST; i < 30; ++i) h2[i] = 0; + dyn_len_1 = build_tree(h1, dyn_lmap, 286); + dyn_len_2 = build_tree(h2, dyn_dmap, 30); + return boff; +} + +/* return [ data, bytesRead ] */ +function inflate(data, usz) { + /* shortcircuit for empty buffer [0x03, 0x00] */ + if(data[0] == 3 && !(data[1] & 0x3)) { return [new_raw_buf(usz), 2]; } + + /* bit offset */ + var boff = 0; + + /* header includes final bit and type bits */ + var header = 0; + + var outbuf = new_unsafe_buf(usz ? usz : (1<<18)); + var woff = 0; + var OL = outbuf.length>>>0; + var max_len_1 = 0, max_len_2 = 0; + + while((header&1) == 0) { + header = read_bits_3(data, boff); boff += 3; + if((header >>> 1) == 0) { + /* Stored block */ + if(boff & 7) boff += 8 - (boff&7); + /* 2 bytes sz, 2 bytes bit inverse */ + var sz = data[boff>>>3] | data[(boff>>>3)+1]<<8; + boff += 32; + /* push sz bytes */ + if(!usz && OL < woff + sz) { outbuf = realloc(outbuf, woff + sz); OL = outbuf.length; } + if(typeof data.copy === 'function') { + // $FlowIgnore + data.copy(outbuf, woff, boff>>>3, (boff>>>3)+sz); + woff += sz; boff += 8*sz; + } else while(sz-- > 0) { outbuf[woff++] = data[boff>>>3]; boff += 8; } + continue; + } else if((header >>> 1) == 1) { + /* Fixed Huffman */ + max_len_1 = 9; max_len_2 = 5; + } else { + /* Dynamic Huffman */ + boff = dyn(data, boff); + max_len_1 = dyn_len_1; max_len_2 = dyn_len_2; + } + if(!usz && (OL < woff + 32767)) { outbuf = realloc(outbuf, woff + 32767); OL = outbuf.length; } + for(;;) { // while(true) is apparently out of vogue in modern JS circles + /* ingest code and move read head */ + var bits = read_bits_n(data, boff, max_len_1); + var code = (header>>>1) == 1 ? fix_lmap[bits] : dyn_lmap[bits]; + boff += code & 15; code >>>= 4; + /* 0-255 are literals, 256 is end of block token, 257+ are copy tokens */ + if(((code>>>8)&0xFF) === 0) outbuf[woff++] = code; + else if(code == 256) break; + else { + code -= 257; + var len_eb = (code < 8) ? 0 : ((code-4)>>2); if(len_eb > 5) len_eb = 0; + var tgt = woff + LEN_LN[code]; + /* length extra bits */ + if(len_eb > 0) { + tgt += read_bits_n(data, boff, len_eb); + boff += len_eb; + } + + /* dist code */ + bits = read_bits_n(data, boff, max_len_2); + code = (header>>>1) == 1 ? fix_dmap[bits] : dyn_dmap[bits]; + boff += code & 15; code >>>= 4; + var dst_eb = (code < 4 ? 0 : (code-2)>>1); + var dst = DST_LN[code]; + /* dist extra bits */ + if(dst_eb > 0) { + dst += read_bits_n(data, boff, dst_eb); + boff += dst_eb; + } + + /* in the common case, manual byte copy is faster than TA set / Buffer copy */ + if(!usz && OL < tgt) { outbuf = realloc(outbuf, tgt); OL = outbuf.length; } + while(woff < tgt) { outbuf[woff] = outbuf[woff - dst]; ++woff; } + } + } + } + return [usz ? outbuf : outbuf.slice(0, woff), (boff+7)>>>3]; +} + +function _inflate(payload, usz) { + var data = payload.slice(payload.l||0); + var out = inflate(data, usz); + payload.l += out[1]; + return out[0]; +} + +function warn_or_throw(wrn, msg) { + if(wrn) { if(typeof console !== 'undefined') console.error(msg); } + else throw new Error(msg); +} + +function parse_zip(file, options) { + var blob = file; + prep_blob(blob, 0); + + var FileIndex = [], FullPaths = []; + var o = { + FileIndex: FileIndex, + FullPaths: FullPaths + }; + init_cfb(o, { root: options.root }); + + /* find end of central directory, start just after signature */ + var i = blob.length - 4; + while((blob[i] != 0x50 || blob[i+1] != 0x4b || blob[i+2] != 0x05 || blob[i+3] != 0x06) && i >= 0) --i; + blob.l = i + 4; + + /* parse end of central directory */ + blob.l += 4; + var fcnt = blob.read_shift(2); + blob.l += 6; + var start_cd = blob.read_shift(4); + + /* parse central directory */ + blob.l = start_cd; + + for(i = 0; i < fcnt; ++i) { + /* trust local file header instead of CD entry */ + blob.l += 20; + var csz = blob.read_shift(4); + var usz = blob.read_shift(4); + var namelen = blob.read_shift(2); + var efsz = blob.read_shift(2); + var fcsz = blob.read_shift(2); + blob.l += 8; + var offset = blob.read_shift(4); + var EF = parse_extra_field(blob.slice(blob.l+namelen, blob.l+namelen+efsz)); + blob.l += namelen + efsz + fcsz; + + var L = blob.l; + blob.l = offset + 4; + parse_local_file(blob, csz, usz, o, EF); + blob.l = L; + } + + return o; +} + + +/* head starts just after local file header signature */ +function parse_local_file(blob, csz, usz, o, EF) { + /* [local file header] */ + blob.l += 2; + var flags = blob.read_shift(2); + var meth = blob.read_shift(2); + var date = parse_dos_date(blob); + + if(flags & 0x2041) throw new Error("Unsupported ZIP encryption"); + var crc32 = blob.read_shift(4); + var _csz = blob.read_shift(4); + var _usz = blob.read_shift(4); + + var namelen = blob.read_shift(2); + var efsz = blob.read_shift(2); + + // TODO: flags & (1<<11) // UTF8 + var name = ""; for(var i = 0; i < namelen; ++i) name += String.fromCharCode(blob[blob.l++]); + if(efsz) { + var ef = parse_extra_field(blob.slice(blob.l, blob.l + efsz)); + if((ef[0x5455]||{}).mt) date = ef[0x5455].mt; + if(((EF||{})[0x5455]||{}).mt) date = EF[0x5455].mt; + } + blob.l += efsz; + + /* [encryption header] */ + + /* [file data] */ + var data = blob.slice(blob.l, blob.l + _csz); + switch(meth) { + case 8: data = _inflateRawSync(blob, _usz); break; + case 0: break; + default: throw new Error("Unsupported ZIP Compression method " + meth); + } + + /* [data descriptor] */ + var wrn = false; + if(flags & 8) { + crc32 = blob.read_shift(4); + if(crc32 == 0x08074b50) { crc32 = blob.read_shift(4); wrn = true; } + _csz = blob.read_shift(4); + _usz = blob.read_shift(4); + } + + if(_csz != csz) warn_or_throw(wrn, "Bad compressed size: " + csz + " != " + _csz); + if(_usz != usz) warn_or_throw(wrn, "Bad uncompressed size: " + usz + " != " + _usz); + var _crc32 = CRC32.buf(data, 0); + if(crc32 != _crc32) warn_or_throw(wrn, "Bad CRC32 checksum: " + crc32 + " != " + _crc32); + cfb_add(o, name, data, {unsafe: true, mt: date}); +} +function write_zip(cfb, options) { + var _opts = options || {}; + var out = [], cdirs = []; + var o = new_buf(1); + var method = (_opts.compression ? 8 : 0), flags = 0; + var desc = false; + if(desc) flags |= 8; + var i = 0, j = 0; + + var start_cd = 0, fcnt = 0; + var root = cfb.FullPaths[0], fp = root, fi = cfb.FileIndex[0]; + var crcs = []; + var sz_cd = 0; + + for(i = 1; i < cfb.FullPaths.length; ++i) { + fp = cfb.FullPaths[i].slice(root.length); fi = cfb.FileIndex[i]; + if(!fi.size || !fi.content || fp == "\u0001Sh33tJ5") continue; + var start = start_cd; + + /* TODO: CP437 filename */ + var namebuf = new_buf(fp.length); + for(j = 0; j < fp.length; ++j) namebuf.write_shift(1, fp.charCodeAt(j) & 0x7F); + namebuf = namebuf.slice(0, namebuf.l); + crcs[fcnt] = CRC32.buf(fi.content, 0); + + var outbuf = fi.content; + if(method == 8) outbuf = _deflateRawSync(outbuf); + + /* local file header */ + o = new_buf(30); + o.write_shift(4, 0x04034b50); + o.write_shift(2, 20); + o.write_shift(2, flags); + o.write_shift(2, method); + /* TODO: last mod file time/date */ + if(fi.mt) write_dos_date(o, fi.mt); + else o.write_shift(4, 0); + o.write_shift(-4, (flags & 8) ? 0 : crcs[fcnt]); + o.write_shift(4, (flags & 8) ? 0 : outbuf.length); + o.write_shift(4, (flags & 8) ? 0 : fi.content.length); + o.write_shift(2, namebuf.length); + o.write_shift(2, 0); + + start_cd += o.length; + out.push(o); + start_cd += namebuf.length; + out.push(namebuf); + + /* TODO: encryption header ? */ + start_cd += outbuf.length; + out.push(outbuf); + + /* data descriptor */ + if(flags & 8) { + o = new_buf(12); + o.write_shift(-4, crcs[fcnt]); + o.write_shift(4, outbuf.length); + o.write_shift(4, fi.content.length); + start_cd += o.l; + out.push(o); + } + + /* central directory */ + o = new_buf(46); + o.write_shift(4, 0x02014b50); + o.write_shift(2, 0); + o.write_shift(2, 20); + o.write_shift(2, flags); + o.write_shift(2, method); + o.write_shift(4, 0); /* TODO: last mod file time/date */ + o.write_shift(-4, crcs[fcnt]); + + o.write_shift(4, outbuf.length); + o.write_shift(4, fi.content.length); + o.write_shift(2, namebuf.length); + o.write_shift(2, 0); + o.write_shift(2, 0); + o.write_shift(2, 0); + o.write_shift(2, 0); + o.write_shift(4, 0); + o.write_shift(4, start); + + sz_cd += o.l; + cdirs.push(o); + sz_cd += namebuf.length; + cdirs.push(namebuf); + ++fcnt; + } + + /* end of central directory */ + o = new_buf(22); + o.write_shift(4, 0x06054b50); + o.write_shift(2, 0); + o.write_shift(2, 0); + o.write_shift(2, fcnt); + o.write_shift(2, fcnt); + o.write_shift(4, sz_cd); + o.write_shift(4, start_cd); + o.write_shift(2, 0); + + return bconcat(([bconcat((out)), bconcat(cdirs), o])); +} function cfb_new(opts) { var o = ({}); init_cfb(o, opts); @@ -828,6 +1551,8 @@ file.content = (content); file.size = content ? content.length : 0; if(opts) { if(opts.CLSID) file.clsid = opts.CLSID; + if(opts.mt) file.mt = opts.mt; + if(opts.ct) file.ct = opts.ct; } return file; } @@ -871,6 +1596,9 @@ exports.utils = { CheckField: CheckField, prep_blob: prep_blob, bconcat: bconcat, + use_zlib: use_zlib, + _deflateRaw: _deflate, + _inflateRaw: _inflate, consts: consts }; diff --git a/dist/cfb.js b/dist/cfb.js index 9f86177..dd56c02 100644 --- a/dist/cfb.js +++ b/dist/cfb.js @@ -2,7 +2,7 @@ /* vim: set ts=2: */ /*jshint eqnull:true */ /*exported CFB */ -/*global module, require:false, process:false, Buffer:false, Uint8Array:false */ +/*global module, require:false, process:false, Buffer:false, Uint8Array:false, Uint16Array:false */ var Base64 = (function make_b64(){ var map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; @@ -58,6 +58,8 @@ if(typeof Buffer !== 'undefined') { Buffer_from = nbfs ? function(buf, enc) { return (enc) ? new Buffer(buf, enc) : new Buffer(buf); } : Buffer.from.bind(Buffer); // $FlowIgnore if(!Buffer.alloc) Buffer.alloc = function(n) { return new Buffer(n); }; + // $FlowIgnore + if(!Buffer.allocUnsafe) Buffer.allocUnsafe = function(n) { return new Buffer(n); }; } function new_raw_buf(len) { @@ -66,8 +68,14 @@ function new_raw_buf(len) { /* jshint +W056 */ } +function new_unsafe_buf(len) { + /* jshint -W056 */ + return has_buf ? Buffer.allocUnsafe(len) : new Array(len); + /* jshint +W056 */ +} + var s2a = function s2a(s) { - if(has_buf) return Buffer.from(s, "binary"); + if(has_buf) return Buffer_from(s, "binary"); return s.split("").map(function(x){ return x.charCodeAt(0) & 0xff; }); }; @@ -96,7 +104,6 @@ if(has_buf) { }; __hexlify = function(b,s,l) { return Buffer.isBuffer(b) ? b.toString('hex',s,s+l) : ___hexlify(b,s,l); }; __toBuffer = function(bufs) { return (bufs[0].length > 0 && Buffer.isBuffer(bufs[0][0])) ? Buffer.concat((bufs[0])) : ___toBuffer(bufs);}; - // $FlowIgnore s2a = function(s) { return Buffer_from(s, "binary"); }; bconcat = function(bufs) { return Buffer.isBuffer(bufs[0]) ? Buffer.concat(bufs) : __bconcat(bufs); }; } @@ -166,10 +173,111 @@ function new_buf(sz) { return o; } +/* crc32.js (C) 2014-present SheetJS -- http://sheetjs.com */ +/* vim: set ts=2: */ +/*exported CRC32 */ +var CRC32; +(function (factory) { + /*jshint ignore:start */ + /*eslint-disable */ + factory(CRC32 = {}); + /*eslint-enable */ + /*jshint ignore:end */ +}(function(CRC32) { +CRC32.version = '1.2.0'; +/* see perf/crc32table.js */ +/*global Int32Array */ +function signed_crc_table() { + var c = 0, table = new Array(256); + + for(var n =0; n != 256; ++n){ + c = n; + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + table[n] = c; + } + + return typeof Int32Array !== 'undefined' ? new Int32Array(table) : table; +} + +var T = signed_crc_table(); +function crc32_bstr(bstr, seed) { + var C = seed ^ -1, L = bstr.length - 1; + for(var i = 0; i < L;) { + C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF]; + C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF]; + } + if(i === L) C = (C>>>8) ^ T[(C ^ bstr.charCodeAt(i))&0xFF]; + return C ^ -1; +} + +function crc32_buf(buf, seed) { + if(buf.length > 10000) return crc32_buf_8(buf, seed); + var C = seed ^ -1, L = buf.length - 3; + for(var i = 0; i < L;) { + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + } + while(i < L+3) C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + return C ^ -1; +} + +function crc32_buf_8(buf, seed) { + var C = seed ^ -1, L = buf.length - 7; + for(var i = 0; i < L;) { + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + } + while(i < L+7) C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + return C ^ -1; +} + +function crc32_str(str, seed) { + var C = seed ^ -1; + for(var i = 0, L=str.length, c, d; i < L;) { + c = str.charCodeAt(i++); + if(c < 0x80) { + C = (C>>>8) ^ T[(C ^ c)&0xFF]; + } else if(c < 0x800) { + C = (C>>>8) ^ T[(C ^ (192|((c>>6)&31)))&0xFF]; + C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF]; + } else if(c >= 0xD800 && c < 0xE000) { + c = (c&1023)+64; d = str.charCodeAt(i++)&1023; + C = (C>>>8) ^ T[(C ^ (240|((c>>8)&7)))&0xFF]; + C = (C>>>8) ^ T[(C ^ (128|((c>>2)&63)))&0xFF]; + C = (C>>>8) ^ T[(C ^ (128|((d>>6)&15)|((c&3)<<4)))&0xFF]; + C = (C>>>8) ^ T[(C ^ (128|(d&63)))&0xFF]; + } else { + C = (C>>>8) ^ T[(C ^ (224|((c>>12)&15)))&0xFF]; + C = (C>>>8) ^ T[(C ^ (128|((c>>6)&63)))&0xFF]; + C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF]; + } + } + return C ^ -1; +} +CRC32.table = T; +CRC32.bstr = crc32_bstr; +CRC32.buf = crc32_buf; +CRC32.str = crc32_str; +})); /* [MS-CFB] v20171201 */ var CFB = (function _CFB(){ var exports = {}; -exports.version = '1.0.8'; +exports.version = '1.1.0'; /* [MS-CFB] 2.6.4 */ function namecmp(l, r) { var L = l.split("/"), R = r.split("/"); @@ -190,9 +298,75 @@ function filename(p) { var c = p.lastIndexOf("/"); return (c === -1) ? p : p.slice(c+1); } +/* -------------------------------------------------------------------------- */ +/* DOS Date format: + high|YYYYYYYm.mmmddddd.HHHHHMMM.MMMSSSSS|low + add 1980 to stored year + stored second should be doubled +*/ + +/* write JS date to buf as a DOS date */ +function write_dos_date(buf, date) { + if(typeof date === "string") date = new Date(date); + var hms = date.getHours(); + hms = hms << 6 | date.getMinutes(); + hms = hms << 5 | (date.getSeconds()>>>1); + buf.write_shift(2, hms); + var ymd = (date.getFullYear() - 1980); + ymd = ymd << 4 | (date.getMonth()+1); + ymd = ymd << 5 | date.getDate(); + buf.write_shift(2, ymd); +} + +/* read four bytes from buf and interpret as a DOS date */ +function parse_dos_date(buf) { + var hms = buf.read_shift(2) & 0xFFFF; + var ymd = buf.read_shift(2) & 0xFFFF; + var val = new Date(); + var d = ymd & 0x1F; ymd >>>= 5; + var m = ymd & 0x0F; ymd >>>= 4; + val.setMilliseconds(0); + val.setFullYear(ymd + 1980); + val.setMonth(m-1); + val.setDate(d); + var S = hms & 0x1F; hms >>>= 5; + var M = hms & 0x3F; hms >>>= 6; + val.setHours(hms); + val.setMinutes(M); + val.setSeconds(S<<1); + return val; +} +function parse_extra_field(blob) { + prep_blob(blob, 0); + var o = {}; + var flags = 0; + while(blob.l <= blob.length - 4) { + var type = blob.read_shift(2); + var sz = blob.read_shift(2), tgt = blob.l + sz; + var p = {}; + switch(type) { + /* UNIX-style Timestamps */ + case 0x5455: { + flags = blob.read_shift(1); + if(flags & 1) p.mtime = blob.read_shift(4); + /* for some reason, CD flag corresponds to LFH */ + if(sz > 5) { + if(flags & 2) p.atime = blob.read_shift(4); + if(flags & 4) p.ctime = blob.read_shift(4); + } + if(p.mtime) p.mt = new Date(p.mtime*1000); + } + break; + } + blob.l = tgt; + o[type] = p; + } + return o; +} var fs; function get_fs() { return fs || (fs = require('fs')); } function parse(file, options) { +if(file[0] == 0x50 && file[1] == 0x4b) return parse_zip(file, options); if(file.length < 512) throw new Error("CFB file size " + file.length + " < 512"); var mver = 3; var ssz = 512; @@ -213,6 +387,8 @@ var mv = check_get_mver(blob); mver = mv[0]; switch(mver) { case 3: ssz = 512; break; case 4: ssz = 4096; break; + case 0: if(mv[1] == 0) return parse_zip(file, options); + /* falls through */ default: throw new Error("Major Version: Expected 3 or 4 saw " + mver); } @@ -291,6 +467,7 @@ return o; /* [MS-CFB] 2.2 Compound File Header -- read up to major version */ function check_get_mver(blob) { + if(blob[blob.l] == 0x50 && blob[blob.l + 1] == 0x4b) return [0, 0]; // header signature 8 blob.chk(HEADER_SIGNATURE, 'Header Signature: '); @@ -608,6 +785,7 @@ function rebuild_cfb(cfb, f) { function _write(cfb, options) { var _opts = options || {}; rebuild_cfb(cfb); + if(_opts.fileType == 'zip') return write_zip(cfb, _opts); var L = (function(cfb){ var mini_size = 0, fat_size = 0; for(var i = 0; i < cfb.FileIndex.length; ++i) { @@ -802,6 +980,551 @@ function write(cfb, options) { } return o; } +/* node < 8.1 zlib does not expose bytesRead, so default to pure JS */ +var _zlib; +function use_zlib(zlib) { try { + var InflateRaw = zlib.InflateRaw; + var InflRaw = new InflateRaw(); + InflRaw._processChunk(new Uint8Array([3, 0]), InflRaw._finishFlushFlag); + if(InflRaw.bytesRead) _zlib = zlib; + else throw new Error("zlib does not expose bytesRead"); +} catch(e) {console.error("cannot use native zlib: " + (e.message || e)); } } + +function _inflateRawSync(payload, usz) { + if(!_zlib) return _inflate(payload, usz); + var InflateRaw = _zlib.InflateRaw; + var InflRaw = new InflateRaw(); + var out = InflRaw._processChunk(payload.slice(payload.l), InflRaw._finishFlushFlag); + payload.l += InflRaw.bytesRead; + return out; +} + +function _deflateRawSync(payload) { + return _zlib ? _zlib.deflateRawSync(payload) : _deflate(payload); +} +var CLEN_ORDER = [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ]; + +/* LEN_ID = [ 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285 ]; */ +var LEN_LN = [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13 , 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 ]; + +/* DST_ID = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 ]; */ +var DST_LN = [ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 ]; + +function bit_swap_8(n) { var t = (((((n<<1)|(n<<11)) & 0x22110) | (((n<<5)|(n<<15)) & 0x88440))); return ((t>>16) | (t>>8) |t)&0xFF; } + +var use_typed_arrays = typeof Uint8Array !== 'undefined'; + +var bitswap8 = use_typed_arrays ? new Uint8Array(1<<8) : []; +for(var q = 0; q < (1<<8); ++q) bitswap8[q] = bit_swap_8(q); + +function bit_swap_n(n, b) { + var rev = bitswap8[n & 0xFF]; + if(b <= 8) return rev >>> (8-b); + rev = (rev << 8) | bitswap8[(n>>8)&0xFF]; + if(b <= 16) return rev >>> (16-b); + rev = (rev << 8) | bitswap8[(n>>16)&0xFF]; + return rev >>> (24-b); +} + +/* helpers for unaligned bit reads */ +function read_bits_2(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 6 ? 0 : buf[h+1]<<8))>>>w)& 0x03; } +function read_bits_3(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 5 ? 0 : buf[h+1]<<8))>>>w)& 0x07; } +function read_bits_4(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 4 ? 0 : buf[h+1]<<8))>>>w)& 0x0F; } +function read_bits_5(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 3 ? 0 : buf[h+1]<<8))>>>w)& 0x1F; } +function read_bits_7(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 1 ? 0 : buf[h+1]<<8))>>>w)& 0x7F; } + +/* works up to n = 3 * 8 + 1 = 25 */ +function read_bits_n(buf, bl, n) { + var w = (bl&7), h = (bl>>>3), f = ((1<>> w; + if(n < 8 - w) return v & f; + v |= buf[h+1]<<(8-w); + if(n < 16 - w) return v & f; + v |= buf[h+2]<<(16-w); + if(n < 24 - w) return v & f; + v |= buf[h+3]<<(24-w); + return v & f; +} + +/* until ArrayBuffer#realloc is a thing, fake a realloc */ +function realloc(b, sz) { + var L = b.length, M = 2*L > sz ? 2*L : sz + 5, i = 0; + if(L >= sz) return b; + if(has_buf) { + var o = new_unsafe_buf(M); + // $FlowIgnore + if(b.copy) b.copy(o); + else for(; i < b.length; ++i) o[i] = b[i]; + return o; + } else if(use_typed_arrays) { + var a = new Uint8Array(M); + if(a.set) a.set(b); + else for(; i < b.length; ++i) a[i] = b[i]; + return a; + } + b.length = M; + return b; +} + +/* zero-filled arrays for older browsers */ +function zero_fill_array(n) { + var o = new Array(n); + for(var i = 0; i < n; ++i) o[i] = 0; + return o; +}var _deflate = (function() { +var _deflateRaw = (function() { + return function deflateRaw(data, out) { + var boff = 0; + while(boff < data.length) { + var L = Math.min(0xFFFF, data.length - boff); + var h = boff + L == data.length; + /* TODO: this is only type 0 stored */ + out.write_shift(1, +h); + out.write_shift(2, L); + out.write_shift(2, (~L) & 0xFFFF); + while(L-- > 0) out[out.l++] = data[boff++]; + } + return out.l; + }; +})(); + +return function(data) { + var buf = new_buf(50+Math.floor(data.length*1.1)); + var off = _deflateRaw(data, buf); + return buf.slice(0, off); +}; +})(); +/* modified inflate function also moves original read head */ + +/* build tree (used for literals and lengths) */ +function build_tree(clens, cmap, MAX) { + var maxlen = 1, w = 0, i = 0, j = 0, ccode = 0, L = clens.length; + + var bl_count = use_typed_arrays ? new Uint16Array(32) : zero_fill_array(32); + for(i = 0; i < 32; ++i) bl_count[i] = 0; + + for(i = L; i < MAX; ++i) clens[i] = 0; + L = clens.length; + + var ctree = use_typed_arrays ? new Uint16Array(L) : zero_fill_array(L); // [] + + /* build code tree */ + for(i = 0; i < L; ++i) { + bl_count[(w = clens[i])]++; + if(maxlen < w) maxlen = w; + ctree[i] = 0; + } + bl_count[0] = 0; + for(i = 1; i <= maxlen; ++i) bl_count[i+16] = (ccode = (ccode + bl_count[i-1])<<1); + for(i = 0; i < L; ++i) { + ccode = clens[i]; + if(ccode != 0) ctree[i] = bl_count[ccode+16]++; + } + + /* cmap[maxlen + 4 bits] = (off&15) + (lit<<4) reverse mapping */ + var cleni = 0; + for(i = 0; i < L; ++i) { + cleni = clens[i]; + if(cleni != 0) { + ccode = bit_swap_n(ctree[i], maxlen)>>(maxlen-cleni); + for(j = (1<<(maxlen + 4 - cleni)) - 1; j>=0; --j) + cmap[ccode|(j<>(8-cleni); + for(var j = (1<<(7-cleni))-1; j>=0; --j) dyn_cmap[ccode|(j<>>= 3)) { + case 16: + w = 3 + read_bits_2(data, boff); boff += 2; + ccode = hcodes[hcodes.length - 1]; + while(w-- > 0) hcodes.push(ccode); + break; + case 17: + w = 3 + read_bits_3(data, boff); boff += 3; + while(w-- > 0) hcodes.push(0); + break; + case 18: + w = 11 + read_bits_7(data, boff); boff += 7; + while(w -- > 0) hcodes.push(0); + break; + default: + hcodes.push(ccode); + if(maxlen < ccode) maxlen = ccode; + break; + } + } + + /* build literal / length trees */ + var h1 = hcodes.slice(0, _HLIT), h2 = hcodes.slice(_HLIT); + for(i = _HLIT; i < 286; ++i) h1[i] = 0; + for(i = _HDIST; i < 30; ++i) h2[i] = 0; + dyn_len_1 = build_tree(h1, dyn_lmap, 286); + dyn_len_2 = build_tree(h2, dyn_dmap, 30); + return boff; +} + +/* return [ data, bytesRead ] */ +function inflate(data, usz) { + /* shortcircuit for empty buffer [0x03, 0x00] */ + if(data[0] == 3 && !(data[1] & 0x3)) { return [new_raw_buf(usz), 2]; } + + /* bit offset */ + var boff = 0; + + /* header includes final bit and type bits */ + var header = 0; + + var outbuf = new_unsafe_buf(usz ? usz : (1<<18)); + var woff = 0; + var OL = outbuf.length>>>0; + var max_len_1 = 0, max_len_2 = 0; + + while((header&1) == 0) { + header = read_bits_3(data, boff); boff += 3; + if((header >>> 1) == 0) { + /* Stored block */ + if(boff & 7) boff += 8 - (boff&7); + /* 2 bytes sz, 2 bytes bit inverse */ + var sz = data[boff>>>3] | data[(boff>>>3)+1]<<8; + boff += 32; + /* push sz bytes */ + if(!usz && OL < woff + sz) { outbuf = realloc(outbuf, woff + sz); OL = outbuf.length; } + if(typeof data.copy === 'function') { + // $FlowIgnore + data.copy(outbuf, woff, boff>>>3, (boff>>>3)+sz); + woff += sz; boff += 8*sz; + } else while(sz-- > 0) { outbuf[woff++] = data[boff>>>3]; boff += 8; } + continue; + } else if((header >>> 1) == 1) { + /* Fixed Huffman */ + max_len_1 = 9; max_len_2 = 5; + } else { + /* Dynamic Huffman */ + boff = dyn(data, boff); + max_len_1 = dyn_len_1; max_len_2 = dyn_len_2; + } + if(!usz && (OL < woff + 32767)) { outbuf = realloc(outbuf, woff + 32767); OL = outbuf.length; } + for(;;) { // while(true) is apparently out of vogue in modern JS circles + /* ingest code and move read head */ + var bits = read_bits_n(data, boff, max_len_1); + var code = (header>>>1) == 1 ? fix_lmap[bits] : dyn_lmap[bits]; + boff += code & 15; code >>>= 4; + /* 0-255 are literals, 256 is end of block token, 257+ are copy tokens */ + if(((code>>>8)&0xFF) === 0) outbuf[woff++] = code; + else if(code == 256) break; + else { + code -= 257; + var len_eb = (code < 8) ? 0 : ((code-4)>>2); if(len_eb > 5) len_eb = 0; + var tgt = woff + LEN_LN[code]; + /* length extra bits */ + if(len_eb > 0) { + tgt += read_bits_n(data, boff, len_eb); + boff += len_eb; + } + + /* dist code */ + bits = read_bits_n(data, boff, max_len_2); + code = (header>>>1) == 1 ? fix_dmap[bits] : dyn_dmap[bits]; + boff += code & 15; code >>>= 4; + var dst_eb = (code < 4 ? 0 : (code-2)>>1); + var dst = DST_LN[code]; + /* dist extra bits */ + if(dst_eb > 0) { + dst += read_bits_n(data, boff, dst_eb); + boff += dst_eb; + } + + /* in the common case, manual byte copy is faster than TA set / Buffer copy */ + if(!usz && OL < tgt) { outbuf = realloc(outbuf, tgt); OL = outbuf.length; } + while(woff < tgt) { outbuf[woff] = outbuf[woff - dst]; ++woff; } + } + } + } + return [usz ? outbuf : outbuf.slice(0, woff), (boff+7)>>>3]; +} + +function _inflate(payload, usz) { + var data = payload.slice(payload.l||0); + var out = inflate(data, usz); + payload.l += out[1]; + return out[0]; +} + +function warn_or_throw(wrn, msg) { + if(wrn) { if(typeof console !== 'undefined') console.error(msg); } + else throw new Error(msg); +} + +function parse_zip(file, options) { + var blob = file; + prep_blob(blob, 0); + + var FileIndex = [], FullPaths = []; + var o = { + FileIndex: FileIndex, + FullPaths: FullPaths + }; + init_cfb(o, { root: options.root }); + + /* find end of central directory, start just after signature */ + var i = blob.length - 4; + while((blob[i] != 0x50 || blob[i+1] != 0x4b || blob[i+2] != 0x05 || blob[i+3] != 0x06) && i >= 0) --i; + blob.l = i + 4; + + /* parse end of central directory */ + blob.l += 4; + var fcnt = blob.read_shift(2); + blob.l += 6; + var start_cd = blob.read_shift(4); + + /* parse central directory */ + blob.l = start_cd; + + for(i = 0; i < fcnt; ++i) { + /* trust local file header instead of CD entry */ + blob.l += 20; + var csz = blob.read_shift(4); + var usz = blob.read_shift(4); + var namelen = blob.read_shift(2); + var efsz = blob.read_shift(2); + var fcsz = blob.read_shift(2); + blob.l += 8; + var offset = blob.read_shift(4); + var EF = parse_extra_field(blob.slice(blob.l+namelen, blob.l+namelen+efsz)); + blob.l += namelen + efsz + fcsz; + + var L = blob.l; + blob.l = offset + 4; + parse_local_file(blob, csz, usz, o, EF); + blob.l = L; + } + + return o; +} + + +/* head starts just after local file header signature */ +function parse_local_file(blob, csz, usz, o, EF) { + /* [local file header] */ + blob.l += 2; + var flags = blob.read_shift(2); + var meth = blob.read_shift(2); + var date = parse_dos_date(blob); + + if(flags & 0x2041) throw new Error("Unsupported ZIP encryption"); + var crc32 = blob.read_shift(4); + var _csz = blob.read_shift(4); + var _usz = blob.read_shift(4); + + var namelen = blob.read_shift(2); + var efsz = blob.read_shift(2); + + // TODO: flags & (1<<11) // UTF8 + var name = ""; for(var i = 0; i < namelen; ++i) name += String.fromCharCode(blob[blob.l++]); + if(efsz) { + var ef = parse_extra_field(blob.slice(blob.l, blob.l + efsz)); + if((ef[0x5455]||{}).mt) date = ef[0x5455].mt; + if(((EF||{})[0x5455]||{}).mt) date = EF[0x5455].mt; + } + blob.l += efsz; + + /* [encryption header] */ + + /* [file data] */ + var data = blob.slice(blob.l, blob.l + _csz); + switch(meth) { + case 8: data = _inflateRawSync(blob, _usz); break; + case 0: break; + default: throw new Error("Unsupported ZIP Compression method " + meth); + } + + /* [data descriptor] */ + var wrn = false; + if(flags & 8) { + crc32 = blob.read_shift(4); + if(crc32 == 0x08074b50) { crc32 = blob.read_shift(4); wrn = true; } + _csz = blob.read_shift(4); + _usz = blob.read_shift(4); + } + + if(_csz != csz) warn_or_throw(wrn, "Bad compressed size: " + csz + " != " + _csz); + if(_usz != usz) warn_or_throw(wrn, "Bad uncompressed size: " + usz + " != " + _usz); + var _crc32 = CRC32.buf(data, 0); + if(crc32 != _crc32) warn_or_throw(wrn, "Bad CRC32 checksum: " + crc32 + " != " + _crc32); + cfb_add(o, name, data, {unsafe: true, mt: date}); +} +function write_zip(cfb, options) { + var _opts = options || {}; + var out = [], cdirs = []; + var o = new_buf(1); + var method = (_opts.compression ? 8 : 0), flags = 0; + var desc = false; + if(desc) flags |= 8; + var i = 0, j = 0; + + var start_cd = 0, fcnt = 0; + var root = cfb.FullPaths[0], fp = root, fi = cfb.FileIndex[0]; + var crcs = []; + var sz_cd = 0; + + for(i = 1; i < cfb.FullPaths.length; ++i) { + fp = cfb.FullPaths[i].slice(root.length); fi = cfb.FileIndex[i]; + if(!fi.size || !fi.content || fp == "\u0001Sh33tJ5") continue; + var start = start_cd; + + /* TODO: CP437 filename */ + var namebuf = new_buf(fp.length); + for(j = 0; j < fp.length; ++j) namebuf.write_shift(1, fp.charCodeAt(j) & 0x7F); + namebuf = namebuf.slice(0, namebuf.l); + crcs[fcnt] = CRC32.buf(fi.content, 0); + + var outbuf = fi.content; + if(method == 8) outbuf = _deflateRawSync(outbuf); + + /* local file header */ + o = new_buf(30); + o.write_shift(4, 0x04034b50); + o.write_shift(2, 20); + o.write_shift(2, flags); + o.write_shift(2, method); + /* TODO: last mod file time/date */ + if(fi.mt) write_dos_date(o, fi.mt); + else o.write_shift(4, 0); + o.write_shift(-4, (flags & 8) ? 0 : crcs[fcnt]); + o.write_shift(4, (flags & 8) ? 0 : outbuf.length); + o.write_shift(4, (flags & 8) ? 0 : fi.content.length); + o.write_shift(2, namebuf.length); + o.write_shift(2, 0); + + start_cd += o.length; + out.push(o); + start_cd += namebuf.length; + out.push(namebuf); + + /* TODO: encryption header ? */ + start_cd += outbuf.length; + out.push(outbuf); + + /* data descriptor */ + if(flags & 8) { + o = new_buf(12); + o.write_shift(-4, crcs[fcnt]); + o.write_shift(4, outbuf.length); + o.write_shift(4, fi.content.length); + start_cd += o.l; + out.push(o); + } + + /* central directory */ + o = new_buf(46); + o.write_shift(4, 0x02014b50); + o.write_shift(2, 0); + o.write_shift(2, 20); + o.write_shift(2, flags); + o.write_shift(2, method); + o.write_shift(4, 0); /* TODO: last mod file time/date */ + o.write_shift(-4, crcs[fcnt]); + + o.write_shift(4, outbuf.length); + o.write_shift(4, fi.content.length); + o.write_shift(2, namebuf.length); + o.write_shift(2, 0); + o.write_shift(2, 0); + o.write_shift(2, 0); + o.write_shift(2, 0); + o.write_shift(4, 0); + o.write_shift(4, start); + + sz_cd += o.l; + cdirs.push(o); + sz_cd += namebuf.length; + cdirs.push(namebuf); + ++fcnt; + } + + /* end of central directory */ + o = new_buf(22); + o.write_shift(4, 0x06054b50); + o.write_shift(2, 0); + o.write_shift(2, 0); + o.write_shift(2, fcnt); + o.write_shift(2, fcnt); + o.write_shift(4, sz_cd); + o.write_shift(4, start_cd); + o.write_shift(2, 0); + + return bconcat(([bconcat((out)), bconcat(cdirs), o])); +} function cfb_new(opts) { var o = ({}); init_cfb(o, opts); @@ -828,6 +1551,8 @@ file.content = (content); file.size = content ? content.length : 0; if(opts) { if(opts.CLSID) file.clsid = opts.CLSID; + if(opts.mt) file.mt = opts.mt; + if(opts.ct) file.ct = opts.ct; } return file; } @@ -871,6 +1596,9 @@ exports.utils = { CheckField: CheckField, prep_blob: prep_blob, bconcat: bconcat, + use_zlib: use_zlib, + _deflateRaw: _deflate, + _inflateRaw: _inflate, consts: consts }; diff --git a/dist/cfb.min.js b/dist/cfb.min.js index 2130e59..70adff3 100644 --- a/dist/cfb.min.js +++ b/dist/cfb.min.js @@ -1,2 +1,2 @@ /* cfb.js (C) 2013-present SheetJS -- http://sheetjs.com */ -var Base64=function e(){var e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";return{encode:function(r){var t="";var i=0,n=0,f=0,a=0,s=0,h=0,l=0;for(var o=0;o>2;n=r.charCodeAt(o++);s=(i&3)<<4|n>>4;f=r.charCodeAt(o++);h=(n&15)<<2|f>>6;l=f&63;if(isNaN(n)){h=l=64}else if(isNaN(f)){l=64}t+=e.charAt(a)+e.charAt(s)+e.charAt(h)+e.charAt(l)}return t},decode:function r(t){var i="";var n=0,f=0,a=0,s=0,h=0,l=0,o=0;t=t.replace(/[^\w\+\/\=]/g,"");for(var u=0;u>4;i+=String.fromCharCode(n);l=e.indexOf(t.charAt(u++));f=(h&15)<<4|l>>2;if(l!==64){i+=String.fromCharCode(f)}o=e.indexOf(t.charAt(u++));a=(l&3)<<6|o;if(o!==64){i+=String.fromCharCode(a)}}return i}}}();var has_buf=typeof Buffer!=="undefined"&&typeof process!=="undefined"&&typeof process.versions!=="undefined"&&process.versions.node;var Buffer_from=function(){};if(typeof Buffer!=="undefined"){var nbfs=!Buffer.from;if(!nbfs)try{Buffer.from("foo","utf8")}catch(e){nbfs=true}Buffer_from=nbfs?function(e,r){return r?new Buffer(e,r):new Buffer(e)}:Buffer.from.bind(Buffer);if(!Buffer.alloc)Buffer.alloc=function(e){return new Buffer(e)}}function new_raw_buf(e){return has_buf?Buffer.alloc(e):new Array(e)}var s2a=function r(e){if(has_buf)return Buffer.from(e,"binary");return e.split("").map(function(e){return e.charCodeAt(0)&255})};var chr0=/\u0000/g,chr1=/[\u0001-\u0006]/g;var __toBuffer=function(e){var r=[];for(var t=0;t0&&Buffer.isBuffer(e[0][0])?Buffer.concat(e[0]):___toBuffer(e)};s2a=function(e){return Buffer_from(e,"binary")};bconcat=function(e){return Buffer.isBuffer(e[0])?Buffer.concat(e):__bconcat(e)}}var __readUInt8=function(e,r){return e[r]};var __readUInt16LE=function(e,r){return e[r+1]*(1<<8)+e[r]};var __readInt16LE=function(e,r){var t=e[r+1]*(1<<8)+e[r];return t<32768?t:(65535-t+1)*-1};var __readUInt32LE=function(e,r){return e[r+3]*(1<<24)+(e[r+2]<<16)+(e[r+1]<<8)+e[r]};var __readInt32LE=function(e,r){return(e[r+3]<<24)+(e[r+2]<<16)+(e[r+1]<<8)+e[r]};function ReadShift(e,r){var t,i,n=0;switch(e){case 1:t=__readUInt8(this,this.l);break;case 2:t=(r!=="i"?__readUInt16LE:__readInt16LE)(this,this.l);break;case 4:t=__readInt32LE(this,this.l);break;case 16:n=2;i=__hexlify(this,this.l,e);}this.l+=e;if(n===0)return t;return i}var __writeUInt32LE=function(e,r,t){e[t]=r&255;e[t+1]=r>>>8&255;e[t+2]=r>>>16&255;e[t+3]=r>>>24&255};var __writeInt32LE=function(e,r,t){e[t]=r&255;e[t+1]=r>>8&255;e[t+2]=r>>16&255;e[t+3]=r>>24&255};function WriteShift(e,r,t){var i=0,n=0;switch(t){case"hex":for(;n>8}while(this.l>>=8;this[this.l+1]=r&255;break;case 4:i=4;__writeUInt32LE(this,r,this.l);break;case-4:i=4;__writeInt32LE(this,r,this.l);break;}this.l+=i;return this}function CheckField(e,r){var t=__hexlify(this,this.l,e.length>>1);if(t!==e)throw new Error(r+"Expected "+e+" saw "+t);this.l+=e.length>>1}function prep_blob(e,r){e.l=r;e.read_shift=ReadShift;e.chk=CheckField;e.write_shift=WriteShift}function new_buf(e){var r=new_raw_buf(e);prep_blob(r,0);return r}var CFB=function t(){var e={};e.version="1.0.8";function r(e,r){var t=e.split("/"),i=r.split("/");for(var n=0,f=0,a=Math.min(t.length,i.length);n0&&u!==y)C[u].name="!MiniFAT";C[w[0]].name="!FAT";C.fat_addrs=w;C.ssz=i;var S={},B=[],m=[],A=[];v(a,C,E,B,n,S,m,u);o(m,A,B);B.shift();var L={FileIndex:m,FullPaths:A};if(r&&r.raw)L.raw={header:g,sectors:E};return L}function s(e){e.chk(S,"Header Signature: ");e.chk(m,"CLSID: ");var r=e.read_shift(2,"u");return[e.read_shift(2,"u"),r]}function h(e,r){var t=9;e.l+=2;switch(t=e.read_shift(2)){case 9:if(r!=3)throw new Error("Sector Shift: Expected 9 saw "+t);break;case 12:if(r!=4)throw new Error("Sector Shift: Expected 12 saw "+t);break;default:throw new Error("Sector Shift: Expected 9 or 12 saw "+t);}e.chk("0600","Mini Sector Shift: ");e.chk("000000000000","Reserved: ")}function l(e,r){var t=Math.ceil(e.length/r)-1;var i=[];for(var n=1;n0&&a>=0){f.push(r.slice(a*C,a*C+C));n-=C;a=__readInt32LE(t,a*4)}if(f.length===0)return new_buf(0);return bconcat(f).slice(0,e.size)}function c(e,r,t,i,n){var f=y;if(e===y){if(r!==0)throw new Error("DIFAT chain shorter than expected")}else if(e!==-1){var a=t[e],s=(i>>>2)-1;if(!a)return;for(var h=0;h=0;){n[h]=true;f[f.length]=h;a.push(e[h]);var o=t[Math.floor(h*4/i)];l=h*4&s;if(i<4+l)throw new Error("FAT boundary crossed: "+h+" 4 "+i);if(!e[o])break;h=__readInt32LE(e[o],l)}return{nodes:f,data:__toBuffer([a])}}function d(e,r,t,i){var n=e.length,f=[];var a=[],s=[],h=[];var l=i-1,o=0,u=0,c=0,_=0;for(o=0;o=n)c-=n;if(a[c])continue;h=[];for(u=c;u>=0;){a[u]=true;s[s.length]=u;h.push(e[u]);var d=t[Math.floor(u*4/i)];_=u*4&l;if(i<4+_)throw new Error("FAT boundary crossed: "+u+" 4 "+i);if(!e[d])break;u=__readInt32LE(e[d],_)}f[c]={nodes:s,data:__toBuffer([h])}}return f}function v(e,r,t,i,n,f,a,s){var h=0,l=i.length?2:0;var o=r[e].data;var c=0,d=0,v;for(;c0&&h!==y)r[h].name="!StreamData"}else if(F.size>=4096){F.storage="fat";if(r[F.start]===undefined)r[F.start]=_(t,F.start,r.fat_addrs,r.ssz);r[F.start].name=F.name;F.content=r[F.start].data.slice(0,F.size)}else{F.storage="minifat";if(F.size<0)F.size=0;else if(h!==y&&F.start!==y&&r[h]){F.content=u(F,r[h].data,(r[s]||{}).data)}}if(F.content)prep_blob(F.content,0);f[v]=F;a.push(F)}}function w(e,r){return new Date((__readUInt32LE(e,r+4)/1e7*Math.pow(2,32)+__readUInt32LE(e,r)/1e7-11644473600)*1e3)}function p(e,r){f();return a(n.readFileSync(e),r)}function F(e,r){switch(r&&r.type||"base64"){case"file":return p(e,r);case"base64":return a(s2a(Base64.decode(e)),r);case"binary":return a(s2a(e),r);}return a(e,r)}function g(e,r){var t=r||{},i=t.root||"Root Entry";if(!e.FullPaths)e.FullPaths=[];if(!e.FileIndex)e.FileIndex=[];if(e.FullPaths.length!==e.FileIndex.length)throw new Error("inconsistent CFB structure");if(e.FullPaths.length===0){e.FullPaths[0]=i+"/";e.FileIndex[0]={name:i,type:5}}if(t.CLSID)e.FileIndex[0].clsid=t.CLSID;I(e)}function I(e){var r="Sh33tJ5";if(CFB.find(e,"/"+r))return;var t=new_buf(4);t[0]=55;t[1]=t[3]=50;t[2]=54;e.FileIndex.push({name:r,type:2,content:t,size:4,L:69,R:69,C:69});e.FullPaths.push(e.FullPaths[0]+r);b(e)}function b(e,n){g(e);var f=false,a=false;for(var s=e.FullPaths.length-1;s>=0;--s){var h=e.FileIndex[s];switch(h.type){case 0:if(a)f=true;else{e.FileIndex.pop();e.FullPaths.pop()}break;case 1:;case 2:;case 5:a=true;if(isNaN(h.R*h.L*h.C))f=true;if(h.R>-1&&h.L>-1&&h.R==h.L)f=true;break;default:f=true;break;}}if(!f&&!n)return;var l=new Date(1987,1,19),o=0;var u=[];for(s=0;s1?1:-1;_.size=0;_.type=5}else if(d.slice(-1)=="/"){for(o=s+1;o=u.length?-1:o;for(o=s+1;o=u.length?-1:o;_.type=1}else{if(t(e.FullPaths[s+1]||"")==t(d))_.R=s+1;_.type=2}}}function x(e,r){var t=r||{};b(e);var i=function(e){var r=0,t=0;for(var i=0;i0){if(f<4096)r+=f+63>>6;else t+=f+511>>9}}var a=e.FullPaths.length+3>>2;var s=r+7>>3;var h=r+127>>7;var l=s+t+a+h;var o=l+127>>7;var u=o<=109?0:Math.ceil((o-109)/127);while(l+o+u+127>>7>o)u=++o<=109?0:Math.ceil((o-109)/127);var c=[1,u,o,h,a,t,r,0];e.FileIndex[0].size=r<<6;c[7]=(e.FileIndex[0].start=c[0]+c[1]+c[2]+c[3]+c[4]+c[5])+(c[6]+7>>3);return c}(e);var n=new_buf(i[7]<<9);var f=0,a=0;{for(f=0;f<8;++f)n.write_shift(1,B[f]);for(f=0;f<8;++f)n.write_shift(2,0);n.write_shift(2,62);n.write_shift(2,3);n.write_shift(2,65534);n.write_shift(2,9);n.write_shift(2,6);for(f=0;f<3;++f)n.write_shift(2,0);n.write_shift(4,0);n.write_shift(4,i[2]);n.write_shift(4,i[0]+i[1]+i[2]+i[3]-1);n.write_shift(4,0);n.write_shift(4,1<<12);n.write_shift(4,i[3]?i[0]+i[1]+i[2]-1:y);n.write_shift(4,i[3]);n.write_shift(-4,i[1]?i[0]-1:y);n.write_shift(4,i[1]);for(f=0;f<109;++f)n.write_shift(-4,f>9)}s(i[6]+7>>3);while(n.l&511)n.write_shift(-4,A.ENDOFCHAIN);a=f=0;for(h=0;h=4096)continue;o.start=a;s(l+63>>6)}while(n.l&511)n.write_shift(-4,A.ENDOFCHAIN);for(f=0;f=4096){n.l=o.start+1<<9;for(h=0;h0&&o.size<4096){for(h=0;h>2;n=e.charCodeAt(o++);s=(i&3)<<4|n>>4;f=e.charCodeAt(o++);h=(n&15)<<2|f>>6;l=f&63;if(isNaN(n)){h=l=64}else if(isNaN(f)){l=64}t+=r.charAt(a)+r.charAt(s)+r.charAt(h)+r.charAt(l)}return t},decode:function e(t){var i="";var n=0,f=0,a=0,s=0,h=0,l=0,o=0;t=t.replace(/[^\w\+\/\=]/g,"");for(var u=0;u>4;i+=String.fromCharCode(n);l=r.indexOf(t.charAt(u++));f=(h&15)<<4|l>>2;if(l!==64){i+=String.fromCharCode(f)}o=r.indexOf(t.charAt(u++));a=(l&3)<<6|o;if(o!==64){i+=String.fromCharCode(a)}}return i}}}();var has_buf=typeof Buffer!=="undefined"&&typeof process!=="undefined"&&typeof process.versions!=="undefined"&&process.versions.node;var Buffer_from=function(){};if(typeof Buffer!=="undefined"){var nbfs=!Buffer.from;if(!nbfs)try{Buffer.from("foo","utf8")}catch(e){nbfs=true}Buffer_from=nbfs?function(r,e){return e?new Buffer(r,e):new Buffer(r)}:Buffer.from.bind(Buffer);if(!Buffer.alloc)Buffer.alloc=function(r){return new Buffer(r)};if(!Buffer.allocUnsafe)Buffer.allocUnsafe=function(r){return new Buffer(r)}}function new_raw_buf(r){return has_buf?Buffer.alloc(r):new Array(r)}function new_unsafe_buf(r){return has_buf?Buffer.allocUnsafe(r):new Array(r)}var s2a=function e(r){if(has_buf)return Buffer_from(r,"binary");return r.split("").map(function(r){return r.charCodeAt(0)&255})};var chr0=/\u0000/g,chr1=/[\u0001-\u0006]/g;var __toBuffer=function(r){var e=[];for(var t=0;t0&&Buffer.isBuffer(r[0][0])?Buffer.concat(r[0]):___toBuffer(r)};s2a=function(r){return Buffer_from(r,"binary")};bconcat=function(r){return Buffer.isBuffer(r[0])?Buffer.concat(r):__bconcat(r)}}var __readUInt8=function(r,e){return r[e]};var __readUInt16LE=function(r,e){return r[e+1]*(1<<8)+r[e]};var __readInt16LE=function(r,e){var t=r[e+1]*(1<<8)+r[e];return t<32768?t:(65535-t+1)*-1};var __readUInt32LE=function(r,e){return r[e+3]*(1<<24)+(r[e+2]<<16)+(r[e+1]<<8)+r[e]};var __readInt32LE=function(r,e){return(r[e+3]<<24)+(r[e+2]<<16)+(r[e+1]<<8)+r[e]};function ReadShift(r,e){var t,i,n=0;switch(r){case 1:t=__readUInt8(this,this.l);break;case 2:t=(e!=="i"?__readUInt16LE:__readInt16LE)(this,this.l);break;case 4:t=__readInt32LE(this,this.l);break;case 16:n=2;i=__hexlify(this,this.l,r);}this.l+=r;if(n===0)return t;return i}var __writeUInt32LE=function(r,e,t){r[t]=e&255;r[t+1]=e>>>8&255;r[t+2]=e>>>16&255;r[t+3]=e>>>24&255};var __writeInt32LE=function(r,e,t){r[t]=e&255;r[t+1]=e>>8&255;r[t+2]=e>>16&255;r[t+3]=e>>24&255};function WriteShift(r,e,t){var i=0,n=0;switch(t){case"hex":for(;n>8}while(this.l>>=8;this[this.l+1]=e&255;break;case 4:i=4;__writeUInt32LE(this,e,this.l);break;case-4:i=4;__writeInt32LE(this,e,this.l);break;}this.l+=i;return this}function CheckField(r,e){var t=__hexlify(this,this.l,r.length>>1);if(t!==r)throw new Error(e+"Expected "+r+" saw "+t);this.l+=r.length>>1}function prep_blob(r,e){r.l=e;r.read_shift=ReadShift;r.chk=CheckField;r.write_shift=WriteShift}function new_buf(r){var e=new_raw_buf(r);prep_blob(e,0);return e}var CRC32;(function(r){r(CRC32={})})(function(r){r.version="1.2.0";function e(){var r=0,e=new Array(256);for(var t=0;t!=256;++t){r=t;r=r&1?-306674912^r>>>1:r>>>1;r=r&1?-306674912^r>>>1:r>>>1;r=r&1?-306674912^r>>>1:r>>>1;r=r&1?-306674912^r>>>1:r>>>1;r=r&1?-306674912^r>>>1:r>>>1;r=r&1?-306674912^r>>>1:r>>>1;r=r&1?-306674912^r>>>1:r>>>1;r=r&1?-306674912^r>>>1:r>>>1;e[t]=r}return typeof Int32Array!=="undefined"?new Int32Array(e):e}var t=e();function i(r,e){var i=e^-1,n=r.length-1;for(var f=0;f>>8^t[(i^r.charCodeAt(f++))&255];i=i>>>8^t[(i^r.charCodeAt(f++))&255]}if(f===n)i=i>>>8^t[(i^r.charCodeAt(f))&255];return i^-1}function n(r,e){if(r.length>1e4)return f(r,e);var i=e^-1,n=r.length-3;for(var a=0;a>>8^t[(i^r[a++])&255];i=i>>>8^t[(i^r[a++])&255];i=i>>>8^t[(i^r[a++])&255];i=i>>>8^t[(i^r[a++])&255]}while(a>>8^t[(i^r[a++])&255];return i^-1}function f(r,e){var i=e^-1,n=r.length-7;for(var f=0;f>>8^t[(i^r[f++])&255];i=i>>>8^t[(i^r[f++])&255];i=i>>>8^t[(i^r[f++])&255];i=i>>>8^t[(i^r[f++])&255];i=i>>>8^t[(i^r[f++])&255];i=i>>>8^t[(i^r[f++])&255];i=i>>>8^t[(i^r[f++])&255];i=i>>>8^t[(i^r[f++])&255]}while(f>>8^t[(i^r[f++])&255];return i^-1}function a(r,e){var i=e^-1;for(var n=0,f=r.length,a,s;n>>8^t[(i^a)&255]}else if(a<2048){i=i>>>8^t[(i^(192|a>>6&31))&255];i=i>>>8^t[(i^(128|a&63))&255]}else if(a>=55296&&a<57344){a=(a&1023)+64;s=r.charCodeAt(n++)&1023;i=i>>>8^t[(i^(240|a>>8&7))&255];i=i>>>8^t[(i^(128|a>>2&63))&255];i=i>>>8^t[(i^(128|s>>6&15|(a&3)<<4))&255];i=i>>>8^t[(i^(128|s&63))&255]}else{i=i>>>8^t[(i^(224|a>>12&15))&255];i=i>>>8^t[(i^(128|a>>6&63))&255];i=i>>>8^t[(i^(128|a&63))&255]}}return i^-1}r.table=t;r.bstr=i;r.buf=n;r.str=a});var CFB=function t(){var r={};r.version="1.1.0";function e(r,e){var t=r.split("/"),i=e.split("/");for(var n=0,f=0,a=Math.min(t.length,i.length);n>>1;r.write_shift(2,t);var i=e.getFullYear()-1980;i=i<<4|e.getMonth()+1;i=i<<5|e.getDate();r.write_shift(2,i)}function f(r){var e=r.read_shift(2)&65535;var t=r.read_shift(2)&65535;var i=new Date;var n=t&31;t>>>=5;var f=t&15;t>>>=4;i.setMilliseconds(0);i.setFullYear(t+1980);i.setMonth(f-1);i.setDate(n);var a=e&31;e>>>=5;var s=e&63;e>>>=6;i.setHours(e);i.setMinutes(s);i.setSeconds(a<<1);return i}function a(r){prep_blob(r,0);var e={};var t=0;while(r.l<=r.length-4){var i=r.read_shift(2);var n=r.read_shift(2),f=r.l+n;var a={};switch(i){case 21589:{t=r.read_shift(1);if(t&1)a.mtime=r.read_shift(4);if(n>5){if(t&2)a.atime=r.read_shift(4);if(t&4)a.ctime=r.read_shift(4)}if(a.mtime)a.mt=new Date(a.mtime*1e3)}break;}r.l=f;e[i]=a}return e}var s;function h(){return s||(s=require("fs"))}function l(r,e){if(r[0]==80&&r[1]==75)return wr(r,e);if(r.length<512)throw new Error("CFB file size "+r.length+" < 512");var t=3;var i=512;var n=0;var f=0;var a=0;var s=0;var h=0;var l=[];var v=r.slice(0,512);prep_blob(v,0);var w=o(v);t=w[0];switch(t){case 3:i=512;break;case 4:i=4096;break;case 0:if(w[1]==0)return wr(r,e);default:throw new Error("Major Version: Expected 3 or 4 saw "+t);}if(i!==512){v=r.slice(0,i);prep_blob(v,28)}var b=r.slice(0,i);u(v,t);var F=v.read_shift(4,"i");if(t===3&&F!==0)throw new Error("# Directory Sectors: Expected 0 saw "+F);v.l+=4;a=v.read_shift(4,"i");v.l+=4;v.chk("00100000","Mini Stream Cutoff Size: ");s=v.read_shift(4,"i");n=v.read_shift(4,"i");h=v.read_shift(4,"i");f=v.read_shift(4,"i");for(var y=-1,I=0;I<109;++I){y=v.read_shift(4,"i");if(y<0)break;l[I]=y}var C=c(r,i);d(h,f,C,i,l);var x=p(C,a,l,i);x[a].name="!Directory";if(n>0&&s!==B)x[s].name="!MiniFAT";x[l[0]].name="!FAT";x.fat_addrs=l;x.ssz=i;var m={},E=[],A=[],S=[];g(a,x,C,E,n,m,A,s);_(A,S,E);E.shift();var k={FileIndex:A,FullPaths:S};if(e&&e.raw)k.raw={header:b,sectors:C};return k}function o(r){if(r[r.l]==80&&r[r.l+1]==75)return[0,0];r.chk(S,"Header Signature: ");r.chk(R,"CLSID: ");var e=r.read_shift(2,"u");return[r.read_shift(2,"u"),e]}function u(r,e){var t=9;r.l+=2;switch(t=r.read_shift(2)){case 9:if(e!=3)throw new Error("Sector Shift: Expected 9 saw "+t);break;case 12:if(e!=4)throw new Error("Sector Shift: Expected 12 saw "+t);break;default:throw new Error("Sector Shift: Expected 9 or 12 saw "+t);}r.chk("0600","Mini Sector Shift: ");r.chk("000000000000","Reserved: ")}function c(r,e){var t=Math.ceil(r.length/e)-1;var i=[];for(var n=1;n0&&a>=0){f.push(e.slice(a*A,a*A+A));n-=A;a=__readInt32LE(t,a*4)}if(f.length===0)return new_buf(0);return bconcat(f).slice(0,r.size)}function d(r,e,t,i,n){var f=B;if(r===B){if(e!==0)throw new Error("DIFAT chain shorter than expected")}else if(r!==-1){var a=t[r],s=(i>>>2)-1;if(!a)return;for(var h=0;h=0;){n[h]=true;f[f.length]=h;a.push(r[h]);var o=t[Math.floor(h*4/i)];l=h*4&s;if(i<4+l)throw new Error("FAT boundary crossed: "+h+" 4 "+i);if(!r[o])break;h=__readInt32LE(r[o],l)}return{nodes:f,data:__toBuffer([a])}}function p(r,e,t,i){var n=r.length,f=[];var a=[],s=[],h=[];var l=i-1,o=0,u=0,c=0,_=0;for(o=0;o=n)c-=n;if(a[c])continue;h=[];for(u=c;u>=0;){a[u]=true;s[s.length]=u;h.push(r[u]);var v=t[Math.floor(u*4/i)];_=u*4&l;if(i<4+_)throw new Error("FAT boundary crossed: "+u+" 4 "+i);if(!r[v])break;u=__readInt32LE(r[v],_)}f[c]={nodes:s,data:__toBuffer([h])}}return f}function g(r,e,t,i,n,f,a,s){var h=0,l=i.length?2:0;var o=e[r].data;var u=0,c=0,_;for(;u0&&h!==B)e[h].name="!StreamData"}else if(p.size>=4096){p.storage="fat";if(e[p.start]===undefined)e[p.start]=w(t,p.start,e.fat_addrs,e.ssz);e[p.start].name=p.name;p.content=e[p.start].data.slice(0,p.size)}else{p.storage="minifat";if(p.size<0)p.size=0;else if(h!==B&&p.start!==B&&e[h]){p.content=v(p,e[h].data,(e[s]||{}).data)}}if(p.content)prep_blob(p.content,0);f[_]=p;a.push(p)}}function b(r,e){return new Date((__readUInt32LE(r,e+4)/1e7*Math.pow(2,32)+__readUInt32LE(r,e)/1e7-11644473600)*1e3)}function F(r,e){h();return l(s.readFileSync(r),e)}function y(r,e){switch(e&&e.type||"base64"){case"file":return F(r,e);case"base64":return l(s2a(Base64.decode(r)),e);case"binary":return l(s2a(r),e);}return l(r,e)}function I(r,e){var t=e||{},i=t.root||"Root Entry";if(!r.FullPaths)r.FullPaths=[];if(!r.FileIndex)r.FileIndex=[];if(r.FullPaths.length!==r.FileIndex.length)throw new Error("inconsistent CFB structure");if(r.FullPaths.length===0){r.FullPaths[0]=i+"/";r.FileIndex[0]={name:i,type:5}}if(t.CLSID)r.FileIndex[0].clsid=t.CLSID;C(r)}function C(r){var e="Sh33tJ5";if(CFB.find(r,"/"+e))return;var t=new_buf(4);t[0]=55;t[1]=t[3]=50;t[2]=54;r.FileIndex.push({name:e,type:2,content:t,size:4,L:69,R:69,C:69});r.FullPaths.push(r.FullPaths[0]+e);x(r)}function x(r,n){I(r);var f=false,a=false;for(var s=r.FullPaths.length-1;s>=0;--s){var h=r.FileIndex[s];switch(h.type){case 0:if(a)f=true;else{r.FileIndex.pop();r.FullPaths.pop()}break;case 1:;case 2:;case 5:a=true;if(isNaN(h.R*h.L*h.C))f=true;if(h.R>-1&&h.L>-1&&h.R==h.L)f=true;break;default:f=true;break;}}if(!f&&!n)return;var l=new Date(1987,1,19),o=0;var u=[];for(s=0;s1?1:-1;_.size=0;_.type=5}else if(v.slice(-1)=="/"){for(o=s+1;o=u.length?-1:o;for(o=s+1;o=u.length?-1:o;_.type=1}else{if(t(r.FullPaths[s+1]||"")==t(v))_.R=s+1;_.type=2}}}function m(r,e){var t=e||{};x(r);if(t.fileType=="zip")return gr(r,t);var i=function(r){var e=0,t=0;for(var i=0;i0){if(f<4096)e+=f+63>>6;else t+=f+511>>9}}var a=r.FullPaths.length+3>>2;var s=e+7>>3;var h=e+127>>7;var l=s+t+a+h;var o=l+127>>7;var u=o<=109?0:Math.ceil((o-109)/127);while(l+o+u+127>>7>o)u=++o<=109?0:Math.ceil((o-109)/127);var c=[1,u,o,h,a,t,e,0];r.FileIndex[0].size=e<<6;c[7]=(r.FileIndex[0].start=c[0]+c[1]+c[2]+c[3]+c[4]+c[5])+(c[6]+7>>3);return c}(r);var n=new_buf(i[7]<<9);var f=0,a=0;{for(f=0;f<8;++f)n.write_shift(1,k[f]);for(f=0;f<8;++f)n.write_shift(2,0);n.write_shift(2,62);n.write_shift(2,3);n.write_shift(2,65534);n.write_shift(2,9);n.write_shift(2,6);for(f=0;f<3;++f)n.write_shift(2,0);n.write_shift(4,0);n.write_shift(4,i[2]);n.write_shift(4,i[0]+i[1]+i[2]+i[3]-1);n.write_shift(4,0);n.write_shift(4,1<<12);n.write_shift(4,i[3]?i[0]+i[1]+i[2]-1:B);n.write_shift(4,i[3]);n.write_shift(-4,i[1]?i[0]-1:B);n.write_shift(4,i[1]);for(f=0;f<109;++f)n.write_shift(-4,f>9)}s(i[6]+7>>3);while(n.l&511)n.write_shift(-4,z.ENDOFCHAIN);a=f=0;for(h=0;h=4096)continue;o.start=a;s(l+63>>6)}while(n.l&511)n.write_shift(-4,z.ENDOFCHAIN);for(f=0;f=4096){n.l=o.start+1<<9;for(h=0;h0&&o.size<4096){for(h=0;h>16|e>>8|e)&255}var X=typeof Uint8Array!=="undefined";var q=X?new Uint8Array(1<<8):[];for(var J=0;J<1<<8;++J)q[J]=G(J);function V(r,e){var t=q[r&255];if(e<=8)return t>>>8-e;t=t<<8|q[r>>8&255];if(e<=16)return t>>>16-e;t=t<<8|q[r>>16&255];return t>>>24-e}function W(r,e){var t=e&7,i=e>>>3;return(r[i]|(t<=6?0:r[i+1]<<8))>>>t&3}function Y(r,e){var t=e&7,i=e>>>3;return(r[i]|(t<=5?0:r[i+1]<<8))>>>t&7}function Z(r,e){var t=e&7,i=e>>>3;return(r[i]|(t<=4?0:r[i+1]<<8))>>>t&15}function K(r,e){var t=e&7,i=e>>>3;return(r[i]|(t<=3?0:r[i+1]<<8))>>>t&31}function Q(r,e){var t=e&7,i=e>>>3;return(r[i]|(t<=1?0:r[i+1]<<8))>>>t&127}function $(r,e,t){var i=e&7,n=e>>>3,f=(1<>>i;if(t<8-i)return a&f;a|=r[n+1]<<8-i;if(t<16-i)return a&f;a|=r[n+2]<<16-i;if(t<24-i)return a&f;a|=r[n+3]<<24-i;return a&f}function rr(r,e){var t=r.length,i=2*t>e?2*t:e+5,n=0;if(t>=e)return r;if(has_buf){var f=new_unsafe_buf(i);if(r.copy)r.copy(f);else for(;n0)t[t.l++]=e[i++]}return t.l}}();return function(e){var t=new_buf(50+Math.floor(e.length*1.1));var i=r(e,t);return t.slice(0,i)}}();function ir(r,e,t){var i=1,n=0,f=0,a=0,s=0,h=r.length;var l=X?new Uint16Array(32):er(32);for(f=0;f<32;++f)l[f]=0;for(f=h;f>i-u;for(a=(1<=0;--a)e[s|a<>8-v;for(var d=(1<<7-v)-1;d>=0;--d)lr[_|d<>>=3){case 16:f=3+W(r,e);e+=2;_=w[w.length-1];while(f-- >0)w.push(_);break;case 17:f=3+Y(r,e);e+=3;while(f-- >0)w.push(0);break;case 18:f=11+Q(r,e);e+=7;while(f-- >0)w.push(0);break;default:w.push(_);if(h<_)h=_;break;}}var p=w.slice(0,t),g=w.slice(t);for(c=t;c<286;++c)p[c]=0;for(c=i;c<30;++c)g[c]=0;or=ir(p,sr,286);ur=ir(g,hr,30);return e}function _r(r,e){if(r[0]==3&&!(r[1]&3)){return[new_raw_buf(e),2]}var t=0;var i=0;var n=new_unsafe_buf(e?e:1<<18);var f=0;var a=n.length>>>0;var s=0,h=0;while((i&1)==0){i=Y(r,t);t+=3;if(i>>>1==0){if(t&7)t+=8-(t&7);var l=r[t>>>3]|r[(t>>>3)+1]<<8;t+=32;if(!e&&a>>3,(t>>>3)+l);f+=l;t+=8*l}else while(l-- >0){n[f++]=r[t>>>3];t+=8}continue}else if(i>>>1==1){s=9;h=5}else{t=cr(r,t);s=or;h=ur}if(!e&&a>>1==1?nr[o]:sr[o];t+=u&15;u>>>=4;if((u>>>8&255)===0)n[f++]=u;else if(u==256)break;else{u-=257;var c=u<8?0:u-4>>2;if(c>5)c=0;var _=f+H[u];if(c>0){_+=$(r,t,c);t+=c}o=$(r,t,h);u=i>>>1==1?fr[o]:hr[o];t+=u&15;u>>>=4;var v=u<4?0:u-2>>1;var d=j[u];if(v>0){d+=$(r,t,v);t+=v}if(!e&&a<_){n=rr(n,_);a=n.length}while(f<_){n[f]=n[f-d];++f}}}}return[e?n:n.slice(0,f),t+7>>>3]}function vr(r,e){var t=r.slice(r.l||0);var i=_r(t,e);r.l+=i[1];return i[0]}function dr(r,e){if(r){if(typeof console!=="undefined")console.error(e)}else throw new Error(e)}function wr(r,e){var t=r;prep_blob(t,0);var i=[],n=[];var f={FileIndex:i,FullPaths:n};I(f,{root:e.root});var s=t.length-4;while((t[s]!=80||t[s+1]!=75||t[s+2]!=5||t[s+3]!=6)&&s>=0)--s;t.l=s+4;t.l+=4;var h=t.read_shift(2);t.l+=6;var l=t.read_shift(4);t.l=l;for(s=0;s*/ = new Array(256); + + for(var n =0; n != 256; ++n){ + c = n; + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + c = ((c&1) ? (-306674912 ^ (c >>> 1)) : (c >>> 1)); + table[n] = c; + } + + return typeof Int32Array !== 'undefined' ? new Int32Array(table) : table; +} + +var T = signed_crc_table(); +function crc32_bstr(bstr/*:string*/, seed/*:number*/)/*:number*/ { + var C = seed ^ -1, L = bstr.length - 1; + for(var i = 0; i < L;) { + C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF]; + C = (C>>>8) ^ T[(C^bstr.charCodeAt(i++))&0xFF]; + } + if(i === L) C = (C>>>8) ^ T[(C ^ bstr.charCodeAt(i))&0xFF]; + return C ^ -1; +} + +function crc32_buf(buf/*:Uint8Array|Array*/, seed/*:number*/)/*:number*/ { + if(buf.length > 10000) return crc32_buf_8(buf, seed); + var C = seed ^ -1, L = buf.length - 3; + for(var i = 0; i < L;) { + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + } + while(i < L+3) C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + return C ^ -1; +} + +function crc32_buf_8(buf/*:Uint8Array|Array*/, seed/*:number*/)/*:number*/ { + var C = seed ^ -1, L = buf.length - 7; + for(var i = 0; i < L;) { + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + } + while(i < L+7) C = (C>>>8) ^ T[(C^buf[i++])&0xFF]; + return C ^ -1; +} + +function crc32_str(str/*:string*/, seed/*:number*/)/*:number*/ { + var C = seed ^ -1; + for(var i = 0, L=str.length, c, d; i < L;) { + c = str.charCodeAt(i++); + if(c < 0x80) { + C = (C>>>8) ^ T[(C ^ c)&0xFF]; + } else if(c < 0x800) { + C = (C>>>8) ^ T[(C ^ (192|((c>>6)&31)))&0xFF]; + C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF]; + } else if(c >= 0xD800 && c < 0xE000) { + c = (c&1023)+64; d = str.charCodeAt(i++)&1023; + C = (C>>>8) ^ T[(C ^ (240|((c>>8)&7)))&0xFF]; + C = (C>>>8) ^ T[(C ^ (128|((c>>2)&63)))&0xFF]; + C = (C>>>8) ^ T[(C ^ (128|((d>>6)&15)|((c&3)<<4)))&0xFF]; + C = (C>>>8) ^ T[(C ^ (128|(d&63)))&0xFF]; + } else { + C = (C>>>8) ^ T[(C ^ (224|((c>>12)&15)))&0xFF]; + C = (C>>>8) ^ T[(C ^ (128|((c>>6)&63)))&0xFF]; + C = (C>>>8) ^ T[(C ^ (128|(c&63)))&0xFF]; + } + } + return C ^ -1; +} +CRC32.table = T; +CRC32.bstr = crc32_bstr; +CRC32.buf = crc32_buf; +CRC32.str = crc32_str; +})); /* [MS-CFB] v20171201 */ var CFB = (function _CFB(){ var exports/*:CFBModule*/ = /*::(*/{}/*:: :any)*/; -exports.version = '1.0.8'; +exports.version = '1.1.0'; /* [MS-CFB] 2.6.4 */ function namecmp(l/*:string*/, r/*:string*/)/*:number*/ { var L = l.split("/"), R = r.split("/"); @@ -59,9 +163,75 @@ function filename(p/*:string*/)/*:string*/ { var c = p.lastIndexOf("/"); return (c === -1) ? p : p.slice(c+1); } +/* -------------------------------------------------------------------------- */ +/* DOS Date format: + high|YYYYYYYm.mmmddddd.HHHHHMMM.MMMSSSSS|low + add 1980 to stored year + stored second should be doubled +*/ + +/* write JS date to buf as a DOS date */ +function write_dos_date(buf/*:CFBlob*/, date/*:Date|string*/) { + if(typeof date === "string") date = new Date(date); + var hms/*:number*/ = date.getHours(); + hms = hms << 6 | date.getMinutes(); + hms = hms << 5 | (date.getSeconds()>>>1); + buf.write_shift(2, hms); + var ymd/*:number*/ = (date.getFullYear() - 1980); + ymd = ymd << 4 | (date.getMonth()+1); + ymd = ymd << 5 | date.getDate(); + buf.write_shift(2, ymd); +} + +/* read four bytes from buf and interpret as a DOS date */ +function parse_dos_date(buf/*:CFBlob*/)/*:Date*/ { + var hms = buf.read_shift(2) & 0xFFFF; + var ymd = buf.read_shift(2) & 0xFFFF; + var val = new Date(); + var d = ymd & 0x1F; ymd >>>= 5; + var m = ymd & 0x0F; ymd >>>= 4; + val.setMilliseconds(0); + val.setFullYear(ymd + 1980); + val.setMonth(m-1); + val.setDate(d); + var S = hms & 0x1F; hms >>>= 5; + var M = hms & 0x3F; hms >>>= 6; + val.setHours(hms); + val.setMinutes(M); + val.setSeconds(S<<1); + return val; +} +function parse_extra_field(blob/*:CFBlob*/)/*:any*/ { + prep_blob(blob, 0); + var o = /*::(*/{}/*:: :any)*/; + var flags = 0; + while(blob.l <= blob.length - 4) { + var type = blob.read_shift(2); + var sz = blob.read_shift(2), tgt = blob.l + sz; + var p = {}; + switch(type) { + /* UNIX-style Timestamps */ + case 0x5455: { + flags = blob.read_shift(1); + if(flags & 1) p.mtime = blob.read_shift(4); + /* for some reason, CD flag corresponds to LFH */ + if(sz > 5) { + if(flags & 2) p.atime = blob.read_shift(4); + if(flags & 4) p.ctime = blob.read_shift(4); + } + if(p.mtime) p.mt = new Date(p.mtime*1000); + } + break; + } + blob.l = tgt; + o[type] = p; + } + return o; +} var fs/*:: = require('fs'); */; function get_fs() { return fs || (fs = require('fs')); } function parse(file/*:RawBytes*/, options/*:CFBReadOpts*/)/*:CFBContainer*/ { +if(file[0] == 0x50 && file[1] == 0x4b) return parse_zip(file, options); if(file.length < 512) throw new Error("CFB file size " + file.length + " < 512"); var mver = 3; var ssz = 512; @@ -82,6 +252,8 @@ var mv = check_get_mver(blob); mver = mv[0]; switch(mver) { case 3: ssz = 512; break; case 4: ssz = 4096; break; + case 0: if(mv[1] == 0) return parse_zip(file, options); + /* falls through */ default: throw new Error("Major Version: Expected 3 or 4 saw " + mver); } @@ -160,6 +332,7 @@ return o; /* [MS-CFB] 2.2 Compound File Header -- read up to major version */ function check_get_mver(blob/*:CFBlob*/)/*:[number, number]*/ { + if(blob[blob.l] == 0x50 && blob[blob.l + 1] == 0x4b) return [0, 0]; // header signature 8 blob.chk(HEADER_SIGNATURE, 'Header Signature: '); @@ -477,6 +650,7 @@ function rebuild_cfb(cfb/*:CFBContainer*/, f/*:?boolean*/)/*:void*/ { function _write(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes*/ { var _opts = options || {}; rebuild_cfb(cfb); + if(_opts.fileType == 'zip') return write_zip(cfb, _opts); var L = (function(cfb/*:CFBContainer*/)/*:Array*/{ var mini_size = 0, fat_size = 0; for(var i = 0; i < cfb.FileIndex.length; ++i) { @@ -677,6 +851,551 @@ function write(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes|string } return o; } +/* node < 8.1 zlib does not expose bytesRead, so default to pure JS */ +var _zlib; +function use_zlib(zlib) { try { + var InflateRaw = zlib.InflateRaw; + var InflRaw = new InflateRaw(); + InflRaw._processChunk(new Uint8Array([3, 0]), InflRaw._finishFlushFlag); + if(InflRaw.bytesRead) _zlib = zlib; + else throw new Error("zlib does not expose bytesRead"); +} catch(e) {console.error("cannot use native zlib: " + (e.message || e)); } } + +function _inflateRawSync(payload, usz) { + if(!_zlib) return _inflate(payload, usz); + var InflateRaw = _zlib.InflateRaw; + var InflRaw = new InflateRaw(); + var out = InflRaw._processChunk(payload.slice(payload.l), InflRaw._finishFlushFlag); + payload.l += InflRaw.bytesRead; + return out; +} + +function _deflateRawSync(payload) { + return _zlib ? _zlib.deflateRawSync(payload) : _deflate(payload); +} +var CLEN_ORDER = [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ]; + +/* LEN_ID = [ 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285 ]; */ +var LEN_LN = [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13 , 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 ]; + +/* DST_ID = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 ]; */ +var DST_LN = [ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 ]; + +function bit_swap_8(n) { var t = (((((n<<1)|(n<<11)) & 0x22110) | (((n<<5)|(n<<15)) & 0x88440))); return ((t>>16) | (t>>8) |t)&0xFF; } + +var use_typed_arrays = typeof Uint8Array !== 'undefined'; + +var bitswap8 = use_typed_arrays ? new Uint8Array(1<<8) : []; +for(var q = 0; q < (1<<8); ++q) bitswap8[q] = bit_swap_8(q); + +function bit_swap_n(n, b) { + var rev = bitswap8[n & 0xFF]; + if(b <= 8) return rev >>> (8-b); + rev = (rev << 8) | bitswap8[(n>>8)&0xFF]; + if(b <= 16) return rev >>> (16-b); + rev = (rev << 8) | bitswap8[(n>>16)&0xFF]; + return rev >>> (24-b); +} + +/* helpers for unaligned bit reads */ +function read_bits_2(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 6 ? 0 : buf[h+1]<<8))>>>w)& 0x03; } +function read_bits_3(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 5 ? 0 : buf[h+1]<<8))>>>w)& 0x07; } +function read_bits_4(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 4 ? 0 : buf[h+1]<<8))>>>w)& 0x0F; } +function read_bits_5(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 3 ? 0 : buf[h+1]<<8))>>>w)& 0x1F; } +function read_bits_7(buf, bl) { var w = (bl&7), h = (bl>>>3); return ((buf[h]|(w <= 1 ? 0 : buf[h+1]<<8))>>>w)& 0x7F; } + +/* works up to n = 3 * 8 + 1 = 25 */ +function read_bits_n(buf, bl, n) { + var w = (bl&7), h = (bl>>>3), f = ((1<>> w; + if(n < 8 - w) return v & f; + v |= buf[h+1]<<(8-w); + if(n < 16 - w) return v & f; + v |= buf[h+2]<<(16-w); + if(n < 24 - w) return v & f; + v |= buf[h+3]<<(24-w); + return v & f; +} + +/* until ArrayBuffer#realloc is a thing, fake a realloc */ +function realloc(b, sz/*:number*/) { + var L = b.length, M = 2*L > sz ? 2*L : sz + 5, i = 0; + if(L >= sz) return b; + if(has_buf) { + var o = new_unsafe_buf(M); + // $FlowIgnore + if(b.copy) b.copy(o); + else for(; i < b.length; ++i) o[i] = b[i]; + return o; + } else if(use_typed_arrays) { + var a = new Uint8Array(M); + if(a.set) a.set(b); + else for(; i < b.length; ++i) a[i] = b[i]; + return a; + } + b.length = M; + return b; +} + +/* zero-filled arrays for older browsers */ +function zero_fill_array(n) { + var o = new Array(n); + for(var i = 0; i < n; ++i) o[i] = 0; + return o; +}var _deflate = (function() { +var _deflateRaw = (function() { + return function deflateRaw(data, out) { + var boff = 0; + while(boff < data.length) { + var L = Math.min(0xFFFF, data.length - boff); + var h = boff + L == data.length; + /* TODO: this is only type 0 stored */ + out.write_shift(1, +h); + out.write_shift(2, L); + out.write_shift(2, (~L) & 0xFFFF); + while(L-- > 0) out[out.l++] = data[boff++]; + } + return out.l; + }; +})(); + +return function(data) { + var buf = new_buf(50+Math.floor(data.length*1.1)); + var off = _deflateRaw(data, buf); + return buf.slice(0, off); +}; +})(); +/* modified inflate function also moves original read head */ + +/* build tree (used for literals and lengths) */ +function build_tree(clens, cmap, MAX/*:number*/)/*:number*/ { + var maxlen = 1, w = 0, i = 0, j = 0, ccode = 0, L = clens.length; + + var bl_count = use_typed_arrays ? new Uint16Array(32) : zero_fill_array(32); + for(i = 0; i < 32; ++i) bl_count[i] = 0; + + for(i = L; i < MAX; ++i) clens[i] = 0; + L = clens.length; + + var ctree = use_typed_arrays ? new Uint16Array(L) : zero_fill_array(L); // [] + + /* build code tree */ + for(i = 0; i < L; ++i) { + bl_count[(w = clens[i])]++; + if(maxlen < w) maxlen = w; + ctree[i] = 0; + } + bl_count[0] = 0; + for(i = 1; i <= maxlen; ++i) bl_count[i+16] = (ccode = (ccode + bl_count[i-1])<<1); + for(i = 0; i < L; ++i) { + ccode = clens[i]; + if(ccode != 0) ctree[i] = bl_count[ccode+16]++; + } + + /* cmap[maxlen + 4 bits] = (off&15) + (lit<<4) reverse mapping */ + var cleni = 0; + for(i = 0; i < L; ++i) { + cleni = clens[i]; + if(cleni != 0) { + ccode = bit_swap_n(ctree[i], maxlen)>>(maxlen-cleni); + for(j = (1<<(maxlen + 4 - cleni)) - 1; j>=0; --j) + cmap[ccode|(j<*/ = []; + var i = 0; + for(;i<32; i++) dlens.push(5); + build_tree(dlens, fix_dmap, 32); + + var clens/*:Array*/ = []; + i = 0; + for(; i<=143; i++) clens.push(8); + for(; i<=255; i++) clens.push(9); + for(; i<=279; i++) clens.push(7); + for(; i<=287; i++) clens.push(8); + build_tree(clens, fix_lmap, 288); +})(); + +var dyn_lmap = use_typed_arrays ? new Uint16Array(32768) : zero_fill_array(32768); +var dyn_dmap = use_typed_arrays ? new Uint16Array(32768) : zero_fill_array(32768); +var dyn_cmap = use_typed_arrays ? new Uint16Array(128) : zero_fill_array(128); +var dyn_len_1 = 1, dyn_len_2 = 1; + +/* 5.5.3 Expanding Huffman Codes */ +function dyn(data, boff/*:number*/) { + /* nomenclature from RFC1951 refers to bit values; these are offset by the implicit constant */ + var _HLIT = read_bits_5(data, boff) + 257; boff += 5; + var _HDIST = read_bits_5(data, boff) + 1; boff += 5; + var _HCLEN = read_bits_4(data, boff) + 4; boff += 4; + var w = 0; + + /* grab and store code lengths */ + var clens = use_typed_arrays ? new Uint8Array(19) : zero_fill_array(19); + var ctree = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]; + var maxlen = 1; + var bl_count = use_typed_arrays ? new Uint8Array(8) : zero_fill_array(8); + var next_code = use_typed_arrays ? new Uint8Array(8) : zero_fill_array(8); + var L = clens.length; /* 19 */ + for(var i = 0; i < _HCLEN; ++i) { + clens[CLEN_ORDER[i]] = w = read_bits_3(data, boff); + if(maxlen < w) maxlen = w; + bl_count[w]++; + boff += 3; + } + + /* build code tree */ + var ccode = 0; + bl_count[0] = 0; + for(i = 1; i <= maxlen; ++i) next_code[i] = ccode = (ccode + bl_count[i-1])<<1; + for(i = 0; i < L; ++i) if((ccode = clens[i]) != 0) ctree[i] = next_code[ccode]++; + /* cmap[7 bits from stream] = (off&7) + (lit<<3) */ + var cleni = 0; + for(i = 0; i < L; ++i) { + cleni = clens[i]; + if(cleni != 0) { + ccode = bitswap8[ctree[i]]>>(8-cleni); + for(var j = (1<<(7-cleni))-1; j>=0; --j) dyn_cmap[ccode|(j<*/ = []; + maxlen = 1; + for(; hcodes.length < _HLIT + _HDIST;) { + ccode = dyn_cmap[read_bits_7(data, boff)]; + boff += ccode & 7; + switch((ccode >>>= 3)) { + case 16: + w = 3 + read_bits_2(data, boff); boff += 2; + ccode = hcodes[hcodes.length - 1]; + while(w-- > 0) hcodes.push(ccode); + break; + case 17: + w = 3 + read_bits_3(data, boff); boff += 3; + while(w-- > 0) hcodes.push(0); + break; + case 18: + w = 11 + read_bits_7(data, boff); boff += 7; + while(w -- > 0) hcodes.push(0); + break; + default: + hcodes.push(ccode); + if(maxlen < ccode) maxlen = ccode; + break; + } + } + + /* build literal / length trees */ + var h1 = hcodes.slice(0, _HLIT), h2 = hcodes.slice(_HLIT); + for(i = _HLIT; i < 286; ++i) h1[i] = 0; + for(i = _HDIST; i < 30; ++i) h2[i] = 0; + dyn_len_1 = build_tree(h1, dyn_lmap, 286); + dyn_len_2 = build_tree(h2, dyn_dmap, 30); + return boff; +} + +/* return [ data, bytesRead ] */ +function inflate(data, usz/*:number*/) { + /* shortcircuit for empty buffer [0x03, 0x00] */ + if(data[0] == 3 && !(data[1] & 0x3)) { return [new_raw_buf(usz), 2]; } + + /* bit offset */ + var boff = 0; + + /* header includes final bit and type bits */ + var header = 0; + + var outbuf = new_unsafe_buf(usz ? usz : (1<<18)); + var woff = 0; + var OL = outbuf.length>>>0; + var max_len_1 = 0, max_len_2 = 0; + + while((header&1) == 0) { + header = read_bits_3(data, boff); boff += 3; + if((header >>> 1) == 0) { + /* Stored block */ + if(boff & 7) boff += 8 - (boff&7); + /* 2 bytes sz, 2 bytes bit inverse */ + var sz = data[boff>>>3] | data[(boff>>>3)+1]<<8; + boff += 32; + /* push sz bytes */ + if(!usz && OL < woff + sz) { outbuf = realloc(outbuf, woff + sz); OL = outbuf.length; } + if(typeof data.copy === 'function') { + // $FlowIgnore + data.copy(outbuf, woff, boff>>>3, (boff>>>3)+sz); + woff += sz; boff += 8*sz; + } else while(sz-- > 0) { outbuf[woff++] = data[boff>>>3]; boff += 8; } + continue; + } else if((header >>> 1) == 1) { + /* Fixed Huffman */ + max_len_1 = 9; max_len_2 = 5; + } else { + /* Dynamic Huffman */ + boff = dyn(data, boff); + max_len_1 = dyn_len_1; max_len_2 = dyn_len_2; + } + if(!usz && (OL < woff + 32767)) { outbuf = realloc(outbuf, woff + 32767); OL = outbuf.length; } + for(;;) { // while(true) is apparently out of vogue in modern JS circles + /* ingest code and move read head */ + var bits = read_bits_n(data, boff, max_len_1); + var code = (header>>>1) == 1 ? fix_lmap[bits] : dyn_lmap[bits]; + boff += code & 15; code >>>= 4; + /* 0-255 are literals, 256 is end of block token, 257+ are copy tokens */ + if(((code>>>8)&0xFF) === 0) outbuf[woff++] = code; + else if(code == 256) break; + else { + code -= 257; + var len_eb = (code < 8) ? 0 : ((code-4)>>2); if(len_eb > 5) len_eb = 0; + var tgt = woff + LEN_LN[code]; + /* length extra bits */ + if(len_eb > 0) { + tgt += read_bits_n(data, boff, len_eb); + boff += len_eb; + } + + /* dist code */ + bits = read_bits_n(data, boff, max_len_2); + code = (header>>>1) == 1 ? fix_dmap[bits] : dyn_dmap[bits]; + boff += code & 15; code >>>= 4; + var dst_eb = (code < 4 ? 0 : (code-2)>>1); + var dst = DST_LN[code]; + /* dist extra bits */ + if(dst_eb > 0) { + dst += read_bits_n(data, boff, dst_eb); + boff += dst_eb; + } + + /* in the common case, manual byte copy is faster than TA set / Buffer copy */ + if(!usz && OL < tgt) { outbuf = realloc(outbuf, tgt); OL = outbuf.length; } + while(woff < tgt) { outbuf[woff] = outbuf[woff - dst]; ++woff; } + } + } + } + return [usz ? outbuf : outbuf.slice(0, woff), (boff+7)>>>3]; +} + +function _inflate(payload, usz) { + var data = payload.slice(payload.l||0); + var out = inflate(data, usz); + payload.l += out[1]; + return out[0]; +} + +function warn_or_throw(wrn, msg) { + if(wrn) { if(typeof console !== 'undefined') console.error(msg); } + else throw new Error(msg); +} + +function parse_zip(file/*:RawBytes*/, options/*:CFBReadOpts*/)/*:CFBContainer*/ { + var blob/*:CFBlob*/ = /*::(*/file/*:: :any)*/; + prep_blob(blob, 0); + + var FileIndex/*:CFBFileIndex*/ = [], FullPaths/*:Array*/ = []; + var o = { + FileIndex: FileIndex, + FullPaths: FullPaths + }; + init_cfb(o, { root: options.root }); + + /* find end of central directory, start just after signature */ + var i = blob.length - 4; + while((blob[i] != 0x50 || blob[i+1] != 0x4b || blob[i+2] != 0x05 || blob[i+3] != 0x06) && i >= 0) --i; + blob.l = i + 4; + + /* parse end of central directory */ + blob.l += 4; + var fcnt = blob.read_shift(2); + blob.l += 6; + var start_cd = blob.read_shift(4); + + /* parse central directory */ + blob.l = start_cd; + + for(i = 0; i < fcnt; ++i) { + /* trust local file header instead of CD entry */ + blob.l += 20; + var csz = blob.read_shift(4); + var usz = blob.read_shift(4); + var namelen = blob.read_shift(2); + var efsz = blob.read_shift(2); + var fcsz = blob.read_shift(2); + blob.l += 8; + var offset = blob.read_shift(4); + var EF = parse_extra_field(/*::(*/blob.slice(blob.l+namelen, blob.l+namelen+efsz)/*:: :any)*/); + blob.l += namelen + efsz + fcsz; + + var L = blob.l; + blob.l = offset + 4; + parse_local_file(blob, csz, usz, o, EF); + blob.l = L; + } + + return o; +} + + +/* head starts just after local file header signature */ +function parse_local_file(blob/*:CFBlob*/, csz/*:number*/, usz/*:number*/, o/*:CFBContainer*/, EF) { + /* [local file header] */ + blob.l += 2; + var flags = blob.read_shift(2); + var meth = blob.read_shift(2); + var date = parse_dos_date(blob); + + if(flags & 0x2041) throw new Error("Unsupported ZIP encryption"); + var crc32 = blob.read_shift(4); + var _csz = blob.read_shift(4); + var _usz = blob.read_shift(4); + + var namelen = blob.read_shift(2); + var efsz = blob.read_shift(2); + + // TODO: flags & (1<<11) // UTF8 + var name = ""; for(var i = 0; i < namelen; ++i) name += String.fromCharCode(blob[blob.l++]); + if(efsz) { + var ef = parse_extra_field(/*::(*/blob.slice(blob.l, blob.l + efsz)/*:: :any)*/); + if((ef[0x5455]||{}).mt) date = ef[0x5455].mt; + if(((EF||{})[0x5455]||{}).mt) date = EF[0x5455].mt; + } + blob.l += efsz; + + /* [encryption header] */ + + /* [file data] */ + var data = blob.slice(blob.l, blob.l + _csz); + switch(meth) { + case 8: data = _inflateRawSync(blob, _usz); break; + case 0: break; + default: throw new Error("Unsupported ZIP Compression method " + meth); + } + + /* [data descriptor] */ + var wrn = false; + if(flags & 8) { + crc32 = blob.read_shift(4); + if(crc32 == 0x08074b50) { crc32 = blob.read_shift(4); wrn = true; } + _csz = blob.read_shift(4); + _usz = blob.read_shift(4); + } + + if(_csz != csz) warn_or_throw(wrn, "Bad compressed size: " + csz + " != " + _csz); + if(_usz != usz) warn_or_throw(wrn, "Bad uncompressed size: " + usz + " != " + _usz); + var _crc32 = CRC32.buf(data, 0); + if(crc32 != _crc32) warn_or_throw(wrn, "Bad CRC32 checksum: " + crc32 + " != " + _crc32); + cfb_add(o, name, data, {unsafe: true, mt: date}); +} +function write_zip(cfb/*:CFBContainer*/, options/*:CFBWriteOpts*/)/*:RawBytes*/ { + var _opts = options || {}; + var out = [], cdirs = []; + var o/*:CFBlob*/ = new_buf(1); + var method = (_opts.compression ? 8 : 0), flags = 0; + var desc = false; + if(desc) flags |= 8; + var i = 0, j = 0; + + var start_cd = 0, fcnt = 0; + var root = cfb.FullPaths[0], fp = root, fi = cfb.FileIndex[0]; + var crcs = []; + var sz_cd = 0; + + for(i = 1; i < cfb.FullPaths.length; ++i) { + fp = cfb.FullPaths[i].slice(root.length); fi = cfb.FileIndex[i]; + if(!fi.size || !fi.content || fp == "\u0001Sh33tJ5") continue; + var start = start_cd; + + /* TODO: CP437 filename */ + var namebuf = new_buf(fp.length); + for(j = 0; j < fp.length; ++j) namebuf.write_shift(1, fp.charCodeAt(j) & 0x7F); + namebuf = namebuf.slice(0, namebuf.l); + crcs[fcnt] = CRC32.buf(/*::((*/fi.content/*::||[]):any)*/, 0); + + var outbuf = fi.content/*::||[]*/; + if(method == 8) outbuf = _deflateRawSync(outbuf); + + /* local file header */ + o = new_buf(30); + o.write_shift(4, 0x04034b50); + o.write_shift(2, 20); + o.write_shift(2, flags); + o.write_shift(2, method); + /* TODO: last mod file time/date */ + if(fi.mt) write_dos_date(o, fi.mt); + else o.write_shift(4, 0); + o.write_shift(-4, (flags & 8) ? 0 : crcs[fcnt]); + o.write_shift(4, (flags & 8) ? 0 : outbuf.length); + o.write_shift(4, (flags & 8) ? 0 : /*::(*/fi.content/*::||[])*/.length); + o.write_shift(2, namebuf.length); + o.write_shift(2, 0); + + start_cd += o.length; + out.push(o); + start_cd += namebuf.length; + out.push(namebuf); + + /* TODO: encryption header ? */ + start_cd += outbuf.length; + out.push(outbuf); + + /* data descriptor */ + if(flags & 8) { + o = new_buf(12); + o.write_shift(-4, crcs[fcnt]); + o.write_shift(4, outbuf.length); + o.write_shift(4, /*::(*/fi.content/*::||[])*/.length); + start_cd += o.l; + out.push(o); + } + + /* central directory */ + o = new_buf(46); + o.write_shift(4, 0x02014b50); + o.write_shift(2, 0); + o.write_shift(2, 20); + o.write_shift(2, flags); + o.write_shift(2, method); + o.write_shift(4, 0); /* TODO: last mod file time/date */ + o.write_shift(-4, crcs[fcnt]); + + o.write_shift(4, outbuf.length); + o.write_shift(4, /*::(*/fi.content/*::||[])*/.length); + o.write_shift(2, namebuf.length); + o.write_shift(2, 0); + o.write_shift(2, 0); + o.write_shift(2, 0); + o.write_shift(2, 0); + o.write_shift(4, 0); + o.write_shift(4, start); + + sz_cd += o.l; + cdirs.push(o); + sz_cd += namebuf.length; + cdirs.push(namebuf); + ++fcnt; + } + + /* end of central directory */ + o = new_buf(22); + o.write_shift(4, 0x06054b50); + o.write_shift(2, 0); + o.write_shift(2, 0); + o.write_shift(2, fcnt); + o.write_shift(2, fcnt); + o.write_shift(4, sz_cd); + o.write_shift(4, start_cd); + o.write_shift(2, 0); + + return bconcat(([bconcat((out/*:any*/)), bconcat(cdirs), o]/*:any*/)); +} function cfb_new(opts/*:?any*/)/*:CFBContainer*/ { var o/*:CFBContainer*/ = ({}/*:any*/); init_cfb(o, opts); @@ -704,6 +1423,8 @@ function cfb_add(cfb/*:CFBContainer*/, name/*:string*/, content/*:?RawBytes*/, o file.size = content ? content.length : 0; if(opts) { if(opts.CLSID) file.clsid = opts.CLSID; + if(opts.mt) file.mt = opts.mt; + if(opts.ct) file.ct = opts.ct; } return file; } @@ -747,6 +1468,9 @@ exports.utils = { CheckField: CheckField, prep_blob: prep_blob, bconcat: bconcat, + use_zlib: use_zlib, + _deflateRaw: _deflate, + _inflateRaw: _inflate, consts: consts }; diff --git a/fails.lst b/fails.lst index 38f6e24..4130113 100644 --- a/fails.lst +++ b/fails.lst @@ -1,6 +1,13 @@ +# not CFB or ZIP apachepoi_testEXCEL_3.xls apachepoi_testEXCEL_4.xls xlrd_biff4_no_format_no_window2.xls -roo_type_excelx.xls -roo_type_openoffice.xls libreoffice_calc_csv-import_malformed-quotes.xls +# file exceeding 31 chars +apachepoi_59746_NoRowNums.xlsx +apachepoi_WithEmbeded.xlsx +apachepoi_picture.xlsx +roo_name_with_leading_slash.xlsx +spout-xlsx_sheet_with_prefixed_xml_files.xlsx +# not a valid file +openpyxl_r_null_archive.xlsx diff --git a/index.html b/index.html index 3bce815..372a793 100644 --- a/index.html +++ b/index.html @@ -32,7 +32,9 @@ a { text-decoration: none } Advanced Demo Options: Use readAsBinaryString: (when available) -Export data +Export Current File +- Export data as CFB +- Export data as ZIP

 
@@ -40,7 +42,7 @@ Use readAsBinaryString: (when available) - +