diff options
author | Florian Dold <florian.dold@gmail.com> | 2016-10-10 03:43:44 +0200 |
---|---|---|
committer | Florian Dold <florian.dold@gmail.com> | 2016-10-10 03:43:44 +0200 |
commit | abd94a7f5a50f43c797a11b53549ae48fff667c3 (patch) | |
tree | ab8ed457f65cdd72e13e0571d2975729428f1551 /node_modules/yazl | |
parent | a0247c6a3fd6a09a41a7e35a3441324c4dcb58be (diff) |
add node_modules to address #4364
Diffstat (limited to 'node_modules/yazl')
-rw-r--r-- | node_modules/yazl/LICENSE | 21 | ||||
-rw-r--r-- | node_modules/yazl/README.md | 349 | ||||
-rw-r--r-- | node_modules/yazl/index.js | 638 | ||||
-rw-r--r-- | node_modules/yazl/package.json | 98 |
4 files changed, 1106 insertions, 0 deletions
diff --git a/node_modules/yazl/LICENSE b/node_modules/yazl/LICENSE new file mode 100644 index 000000000..37538d4d0 --- /dev/null +++ b/node_modules/yazl/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Josh Wolfe + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/yazl/README.md b/node_modules/yazl/README.md new file mode 100644 index 000000000..1ff27db78 --- /dev/null +++ b/node_modules/yazl/README.md @@ -0,0 +1,349 @@ +# yazl + +yet another zip library for node. For unzipping, see +[yauzl](https://github.com/thejoshwolfe/yauzl). + +Design principles: + + * Don't block the JavaScript thread. + Use and provide async APIs. + * Keep memory usage under control. + Don't attempt to buffer entire files in RAM at once. + * Prefer to open input files one at a time than all at once. + This is slightly suboptimal for time performance, + but avoids OS-imposed limits on the number of simultaneously open file handles. + +## Usage + +```js +var yazl = require("yazl"); + +var zipfile = new yazl.ZipFile(); +zipfile.addFile("file1.txt", "file1.txt"); +// (add only files, not directories) +zipfile.addFile("path/to/file.txt", "path/in/zipfile.txt"); +// pipe() can be called any time after the constructor +zipfile.outputStream.pipe(fs.createWriteStream("output.zip")).on("close", function() { + console.log("done"); +}); +// alternate apis for adding files: +zipfile.addReadStream(process.stdin, "stdin.txt", { + mtime: new Date(), + mode: 0100664, // -rw-rw-r-- +}); +zipfile.addBuffer(new Buffer("hello"), "hello.txt", { + mtime: new Date(), + mode: 0100664, // -rw-rw-r-- +}); +// call end() after all the files have been added +zipfile.end(); +``` + +## API + +### Class: ZipFile + +#### new ZipFile() + +No parameters. +Nothing can go wrong. + +#### addFile(realPath, metadataPath, [options]) + +Adds a file from the file system at `realPath` into the zipfile as `metadataPath`. +Typically `metadataPath` would be calculated as `path.relative(root, realPath)`. +Unzip programs would extract the file from the zipfile as `metadataPath`. +`realPath` is not stored in the zipfile. + +A valid `metadataPath` must not be blank. +If a `metadataPath` contains `"\\"` characters, they will be replaced by `"/"` characters. +After this substitution, a valid `metadataPath` must not start with `"/"` or `/[A-Za-z]:\//`, +and must not contain `".."` path segments. +File paths must not end with `"/"`. + +`options` may be omitted or null and has the following structure and default values: + +```js +{ + mtime: stats.mtime, + mode: stats.mode, + compress: true, + forceZip64Format: false, +} +``` + +Use `mtime` and/or `mode` to override the values +that would normally be obtained by the `fs.Stats` for the `realPath`. +The mode is the unix permission bits and file type. +The mtime and mode are stored in the zip file in the fields "last mod file time", +"last mod file date", and "external file attributes". +yazl does not store group and user ids in the zip file. + +If `compress` is `true`, the file data will be deflated (compression method 8). +If `compress` is `false`, the file data will be stored (compression method 0). + +If `forceZip64Format` is `true`, yazl will use ZIP64 format in this entry's Data Descriptor +and Central Directory Record regardless of if it's required or not (this may be useful for testing.). +Otherwise, yazl will use ZIP64 format where necessary. + +Internally, `fs.stat()` is called immediately in the `addFile` function, +and `fs.createReadStream()` is used later when the file data is actually required. +Throughout adding and encoding `n` files with `addFile()`, +the number of simultaneous open files is `O(1)`, probably just 1 at a time. + +#### addReadStream(readStream, metadataPath, [options]) + +Adds a file to the zip file whose content is read from `readStream`. +See `addFile()` for info about the `metadataPath` parameter. +`options` may be omitted or null and has the following structure and default values: + +```js +{ + mtime: new Date(), + mode: 0100664, + compress: true, + forceZip64Format: false, + size: 12345, // example value +} +``` + +See `addFile()` for the meaning of `mtime`, `mode`, `compress`, and `forceZip64Format`. +If `size` is given, it will be checked against the actual number of bytes in the `readStream`, +and an error will be emitted if there is a mismatch. + +Note that yazl will `.pipe()` data from `readStream`, so be careful using `.on('data')`. +In certain versions of node, `.on('data')` makes `.pipe()` behave incorrectly. + +#### addBuffer(buffer, metadataPath, [options]) + +Adds a file to the zip file whose content is `buffer`. +See below for info on the limitations on the size of `buffer`. +See `addFile()` for info about the `metadataPath` parameter. +`options` may be omitted or null and has the following structure and default values: + +```js +{ + mtime: new Date(), + mode: 0100664, + compress: true, + forceZip64Format: false, +} +``` + +See `addFile()` for the meaning of `mtime`, `mode`, `compress`, and `forceZip64Format`. + +This method has the unique property that General Purpose Bit `3` will not be used in the Local File Header. +This doesn't matter for unzip implementations that conform to the Zip File Spec. +However, 7-Zip 9.20 has a known bug where General Purpose Bit `3` is declared an unsupported compression method +(note that it really has nothing to do with the compression method.). +See [issue #11](https://github.com/thejoshwolfe/yazl/issues/11). +If you would like to create zip files that 7-Zip 9.20 can understand, +you must use `addBuffer()` instead of `addFile()` or `addReadStream()` for all entries in the zip file +(and `addEmptyDirectory()` is fine too). + +Note that even when yazl provides the file sizes in the Local File Header, +yazl never uses ZIP64 format for Local File Headers due to the size limit on `buffer` (see below). + +##### Size limitation on buffer + +In order to require the ZIP64 format for a local file header, +the provided `buffer` parameter would need to exceed `0xfffffffe` in length. +Alternatively, the `buffer` parameter might not exceed `0xfffffffe` in length, +but zlib compression fails to compress the buffer and actually inflates the data to more than `0xfffffffe` in length. +Both of these scenarios are not allowed by yazl, and those are enforced by a size limit on the `buffer` parameter. + +According to [this zlib documentation](http://www.zlib.net/zlib_tech.html), +the worst case compression results in "an expansion of at most 13.5%, plus eleven bytes". +Furthermore, some configurations of Node.js impose a size limit of `0x3fffffff` on every `Buffer` object. +Running this size through the worst case compression of zlib still produces a size less than `0xfffffffe` bytes, + +Therefore, yazl enforces that the provided `buffer` parameter must be at most `0x3fffffff` bytes long. + +#### addEmptyDirectory(metadataPath, [options]) + +Adds an entry to the zip file that indicates a directory should be created, +even if no other items in the zip file are contained in the directory. +This method is only required if the zip file is intended to contain an empty directory. + +See `addFile()` for info about the `metadataPath` parameter. +If `metadataPath` does not end with a `"/"`, a `"/"` will be appended. + +`options` may be omitted or null and has the following structure and default values: + +```js +{ + mtime: new Date(), + mode: 040775, +} +``` + +See `addFile()` for the meaning of `mtime` and `mode`. + +#### end([options], [finalSizeCallback]) + +Indicates that no more files will be added via `addFile()`, `addReadStream()`, or `addBuffer()`. +Some time after calling this function, `outputStream` will be ended. Note that this entails that you cannot rely on this +callback to know when you are done producing output. If for instance you are creating a zip archive on disk, you will need +to listen to the `end` event on the `outputStream` before notifying consumers of that file. + +`options` may be omitted or null and has the following structure and default values: + +```js +{ + forceZip64Format: false, +} +``` + +If `forceZip64Format` is `true`, yazl will include the ZIP64 End of Central Directory Locator +and ZIP64 End of Central Directory Record regardless of whether or not they are required +(this may be useful for testing.). +Otherwise, yazl will include these structures if necessary. + +If specified and non-null, `finalSizeCallback` is given the parameters `(finalSize)` +sometime during or after the call to `end()`. +`finalSize` is of type `Number` and can either be `-1` +or the guaranteed eventual size in bytes of the output data that can be read from `outputStream`. + +If `finalSize` is `-1`, it means means the final size is too hard to guess before processing the input file data. +This will happen if and only if the `compress` option is `true` on any call to `addFile()`, `addReadStream()`, or `addBuffer()`, +or if `addReadStream()` is called and the optional `size` option is not given. +In other words, clients should know whether they're going to get a `-1` or a real value +by looking at how they are using this library. + +The call to `finalSizeCallback` might be delayed if yazl is still waiting for `fs.Stats` for an `addFile()` entry. +If `addFile()` was never called, `finalSizeCallback` will be called during the call to `end()`. +It is not required to start piping data from `outputStream` before `finalSizeCallback` is called. +`finalSizeCallback` will be called only once, and only if this is the first call to `end()`. + +#### outputStream + +A readable stream that will produce the contents of the zip file. +It is typical to pipe this stream to a writable stream created from `fs.createWriteStream()`. + +Internally, large amounts of file data are piped to `outputStream` using `pipe()`, +which means throttling happens appropriately when this stream is piped to a slow destination. + +Data becomes available in this stream soon after calling one of `addFile()`, `addReadStream()`, or `addBuffer()`. +Clients can call `pipe()` on this stream at any time, +such as immediately after getting a new `ZipFile` instance, or long after calling `end()`. + +As a reminder, be careful using both `.on('data')` and `.pipe()` with this stream. +In certain versions of node, you cannot use both `.on('data')` and `.pipe()` successfully. + +### dateToDosDateTime(jsDate) + +`jsDate` is a `Date` instance. +Returns `{date: date, time: time}`, where `date` and `time` are unsigned 16-bit integers. + +## Regarding ZIP64 Support + +yazl automatically uses ZIP64 format to support files and archives over `2^32 - 2` bytes (~4GB) in size +and to support archives with more than `2^16 - 2` (65534) files. +(See the `forceZip64Format` option in the API above for more control over this behavior.) +ZIP64 format is necessary to exceed the limits inherent in the original zip file format. + +ZIP64 format is supported by most popular zipfile readers, but not by all of them. +Notably, the Mac Archive Utility does not understand ZIP64 format (as of writing this), +and will behave very strangely when presented with such an archive. + +## Output Structure + +The Zip File Spec leaves a lot of flexibility up to the zip file creator. +This section explains and justifies yazl's interpretation and decisions regarding this flexibility. + +This section is probably not useful to yazl clients, +but may be interesting to unzip implementors and zip file enthusiasts. + +### Disk Numbers + +All values related to disk numbers are `0`, +because yazl has no multi-disk archive support. +(The exception being the Total Number of Disks field in +the ZIP64 End of Central Directory Locator, which is always `1`.) + +### Version Made By + +Always `0x033f == (3 << 8) | 63`, which means UNIX (3) +and made from the spec version 6.3 (63). + +Note that the "UNIX" and has implications in the External File Attributes. + +### Version Needed to Extract + +Always `45`, meaning 4.5. +This enables the ZIP64 format. + +### General Purpose Bit Flag + +Bit `8` is always set. +Filenames are always encoded in utf8, even if the result is indistinguishable from ascii. + +Bit `3` is usually set in the Local File Header. +To support both a streaming input and streaming output api, +it is impossible to know the crc32 before processing the file data. +When bit `3` is set, data Descriptors are given after each file data with this information, as per the spec. +But remember a complete metadata listing is still always available in the central directory record, +so if unzip implementations are relying on that, like they should, +none of this paragraph will matter anyway. +Even so, some popular unzip implementations do not follow the spec. +The Mac Archive Utility requires Data Descriptors to include the optional signature, +so yazl includes the optional data descriptor signature. +When bit `3` is not used, the Mac Archive Utility requires there to be no data descriptor, so yazl skips it in that case. +Additionally, 7-Zip 9.20 does not seem to support bit `3` at all +(see [issue #11](https://github.com/thejoshwolfe/yazl/issues/11)). + +All other bits are unset. + +### Internal File Attributes + +Always `0`. +The "apparently an ASCII or text file" bit is always unset meaning "apparently binary". +This kind of determination is outside the scope of yazl, +and is probably not significant in any modern unzip implementation. + +### External File Attributes + +Always `stats.mode << 16`. +This is apparently the convention for "version made by" = `0x03xx` (UNIX). + +Note that for directory entries (see `addEmptyDirectory()`), +it is conventional to use the lower 8 bits for the MS-DOS directory attribute byte. +However, the spec says this is only required if the Version Made By is DOS, +so this library does not do that. + +### Directory Entries + +When adding a `metadataPath` such as `"parent/file.txt"`, yazl does not add a directory entry for `"parent/"`, +because file entries imply the need for their parent directories. +Unzip clients seem to respect this style of pathing, +and the zip file spec does not specify what is standard in this regard. + +In order to create empty directories, use `addEmptyDirectory()`. + +## Change History + + * 2.4.1 + * Fix Mac Archive Utility compatibility issue. [issue #24](https://github.com/thejoshwolfe/yazl/issues/24) + * 2.4.0 + * Add ZIP64 support. [issue #6](https://github.com/thejoshwolfe/yazl/issues/6) + * 2.3.1 + * Remove `.npmignore` from npm package. [pull #22](https://github.com/thejoshwolfe/yazl/pull/22) + * 2.3.0 + * `metadataPath` can have `\` characters now; they will be replaced with `/`. [issue #18](https://github.com/thejoshwolfe/yazl/issues/18) + * 2.2.2 + * Fix 7-Zip compatibility issue. [pull request #17](https://github.com/thejoshwolfe/yazl/pull/17) + * 2.2.1 + * Fix Mac Archive Utility compatibility issue. [issue #14](https://github.com/thejoshwolfe/yazl/issues/14) + * 2.2.0 + * Avoid using general purpose bit 3 for `addBuffer()` calls. [issue #13](https://github.com/thejoshwolfe/yazl/issues/13) + * 2.1.3 + * Fix bug when only addBuffer() and end() are called. [issue #12](https://github.com/thejoshwolfe/yazl/issues/12) + * 2.1.2 + * Fixed typo in parameter validation. [pull request #10](https://github.com/thejoshwolfe/yazl/pull/10) + * 2.1.1 + * Fixed stack overflow when using addBuffer() in certain ways. [issue #9](https://github.com/thejoshwolfe/yazl/issues/9) + * 2.1.0 + * Added `addEmptyDirectory()`. + * `options` is now optional for `addReadStream()` and `addBuffer()`. + * 2.0.0 + * Initial release. diff --git a/node_modules/yazl/index.js b/node_modules/yazl/index.js new file mode 100644 index 000000000..b9338fabd --- /dev/null +++ b/node_modules/yazl/index.js @@ -0,0 +1,638 @@ +var fs = require("fs"); +var Transform = require("stream").Transform; +var PassThrough = require("stream").PassThrough; +var zlib = require("zlib"); +var util = require("util"); +var EventEmitter = require("events").EventEmitter; +var crc32 = require("buffer-crc32"); + +exports.ZipFile = ZipFile; +exports.dateToDosDateTime = dateToDosDateTime; + +util.inherits(ZipFile, EventEmitter); +function ZipFile() { + this.outputStream = new PassThrough(); + this.entries = []; + this.outputStreamCursor = 0; + this.ended = false; // .end() sets this + this.allDone = false; // set when we've written the last bytes + this.forceZip64Eocd = false; // configurable in .end() +} + +ZipFile.prototype.addFile = function(realPath, metadataPath, options) { + var self = this; + metadataPath = validateMetadataPath(metadataPath, false); + if (options == null) options = {}; + + var entry = new Entry(metadataPath, false, options); + self.entries.push(entry); + fs.stat(realPath, function(err, stats) { + if (err) return self.emit("error", err); + if (!stats.isFile()) return self.emit("error", new Error("not a file: " + realPath)); + entry.uncompressedSize = stats.size; + if (options.mtime == null) entry.setLastModDate(stats.mtime); + if (options.mode == null) entry.setFileAttributesMode(stats.mode); + entry.setFileDataPumpFunction(function() { + var readStream = fs.createReadStream(realPath); + entry.state = Entry.FILE_DATA_IN_PROGRESS; + readStream.on("error", function(err) { + self.emit("error", err); + }); + pumpFileDataReadStream(self, entry, readStream); + }); + pumpEntries(self); + }); +}; + +ZipFile.prototype.addReadStream = function(readStream, metadataPath, options) { + var self = this; + metadataPath = validateMetadataPath(metadataPath, false); + if (options == null) options = {}; + var entry = new Entry(metadataPath, false, options); + self.entries.push(entry); + entry.setFileDataPumpFunction(function() { + entry.state = Entry.FILE_DATA_IN_PROGRESS; + pumpFileDataReadStream(self, entry, readStream); + }); + pumpEntries(self); +}; + +ZipFile.prototype.addBuffer = function(buffer, metadataPath, options) { + var self = this; + metadataPath = validateMetadataPath(metadataPath, false); + if (buffer.length > 0x3fffffff) throw new Error("buffer too large: " + buffer.length + " > " + 0x3fffffff); + if (options == null) options = {}; + if (options.size != null) throw new Error("options.size not allowed"); + var entry = new Entry(metadataPath, false, options); + entry.uncompressedSize = buffer.length; + entry.crc32 = crc32.unsigned(buffer); + entry.crcAndFileSizeKnown = true; + self.entries.push(entry); + if (!entry.compress) { + setCompressedBuffer(buffer); + } else { + zlib.deflateRaw(buffer, function(err, compressedBuffer) { + setCompressedBuffer(compressedBuffer); + }); + } + function setCompressedBuffer(compressedBuffer) { + entry.compressedSize = compressedBuffer.length; + entry.setFileDataPumpFunction(function() { + writeToOutputStream(self, compressedBuffer); + writeToOutputStream(self, entry.getDataDescriptor()); + entry.state = Entry.FILE_DATA_DONE; + + // don't call pumpEntries() recursively. + // (also, don't call process.nextTick recursively.) + setImmediate(function() { + pumpEntries(self); + }); + }); + pumpEntries(self); + } +}; + +ZipFile.prototype.addEmptyDirectory = function(metadataPath, options) { + var self = this; + metadataPath = validateMetadataPath(metadataPath, true); + if (options == null) options = {}; + if (options.size != null) throw new Error("options.size not allowed"); + if (options.compress != null) throw new Error("options.compress not allowed"); + var entry = new Entry(metadataPath, true, options); + self.entries.push(entry); + entry.setFileDataPumpFunction(function() { + writeToOutputStream(self, entry.getDataDescriptor()); + entry.state = Entry.FILE_DATA_DONE; + pumpEntries(self); + }); + pumpEntries(self); +}; + +ZipFile.prototype.end = function(options, finalSizeCallback) { + if (typeof options === "function") { + finalSizeCallback = options; + options = null; + } + if (options == null) options = {}; + if (this.ended) return; + this.ended = true; + this.finalSizeCallback = finalSizeCallback; + this.forceZip64Eocd = !!options.forceZip64Format; + pumpEntries(this); +}; + +function writeToOutputStream(self, buffer) { + self.outputStream.write(buffer); + self.outputStreamCursor += buffer.length; +} + +function pumpFileDataReadStream(self, entry, readStream) { + var crc32Watcher = new Crc32Watcher(); + var uncompressedSizeCounter = new ByteCounter(); + var compressor = entry.compress ? new zlib.DeflateRaw() : new PassThrough(); + var compressedSizeCounter = new ByteCounter(); + readStream.pipe(crc32Watcher) + .pipe(uncompressedSizeCounter) + .pipe(compressor) + .pipe(compressedSizeCounter) + .pipe(self.outputStream, {end: false}); + compressedSizeCounter.on("end", function() { + entry.crc32 = crc32Watcher.crc32; + if (entry.uncompressedSize == null) { + entry.uncompressedSize = uncompressedSizeCounter.byteCount; + } else { + if (entry.uncompressedSize !== uncompressedSizeCounter.byteCount) return self.emit("error", new Error("file data stream has unexpected number of bytes")); + } + entry.compressedSize = compressedSizeCounter.byteCount; + self.outputStreamCursor += entry.compressedSize; + writeToOutputStream(self, entry.getDataDescriptor()); + entry.state = Entry.FILE_DATA_DONE; + pumpEntries(self); + }); +} + +function pumpEntries(self) { + if (self.allDone) return; + // first check if finalSize is finally known + if (self.ended && self.finalSizeCallback != null) { + var finalSize = calculateFinalSize(self); + if (finalSize != null) { + // we have an answer + self.finalSizeCallback(finalSize); + self.finalSizeCallback = null; + } + } + + // pump entries + var entry = getFirstNotDoneEntry(); + function getFirstNotDoneEntry() { + for (var i = 0; i < self.entries.length; i++) { + var entry = self.entries[i]; + if (entry.state < Entry.FILE_DATA_DONE) return entry; + } + return null; + } + if (entry != null) { + // this entry is not done yet + if (entry.state < Entry.READY_TO_PUMP_FILE_DATA) return; // input file not open yet + if (entry.state === Entry.FILE_DATA_IN_PROGRESS) return; // we'll get there + // start with local file header + entry.relativeOffsetOfLocalHeader = self.outputStreamCursor; + var localFileHeader = entry.getLocalFileHeader(); + writeToOutputStream(self, localFileHeader); + entry.doFileDataPump(); + } else { + // all cought up on writing entries + if (self.ended) { + // head for the exit + self.offsetOfStartOfCentralDirectory = self.outputStreamCursor; + self.entries.forEach(function(entry) { + var centralDirectoryRecord = entry.getCentralDirectoryRecord(); + writeToOutputStream(self, centralDirectoryRecord); + }); + writeToOutputStream(self, getEndOfCentralDirectoryRecord(self)); + self.outputStream.end(); + self.allDone = true; + } + } +} + +function calculateFinalSize(self) { + var pretendOutputCursor = 0; + var centralDirectorySize = 0; + for (var i = 0; i < self.entries.length; i++) { + var entry = self.entries[i]; + // compression is too hard to predict + if (entry.compress) return -1; + if (entry.state >= Entry.READY_TO_PUMP_FILE_DATA) { + // if addReadStream was called without providing the size, we can't predict the final size + if (entry.uncompressedSize == null) return -1; + } else { + // if we're still waiting for fs.stat, we might learn the size someday + if (entry.uncompressedSize == null) return null; + } + // we know this for sure, and this is important to know if we need ZIP64 format. + entry.relativeOffsetOfLocalHeader = pretendOutputCursor; + var useZip64Format = entry.useZip64Format(); + + pretendOutputCursor += LOCAL_FILE_HEADER_FIXED_SIZE + entry.utf8FileName.length; + pretendOutputCursor += entry.uncompressedSize; + if (!entry.crcAndFileSizeKnown) { + // use a data descriptor + if (useZip64Format) { + pretendOutputCursor += ZIP64_DATA_DESCRIPTOR_SIZE; + } else { + pretendOutputCursor += DATA_DESCRIPTOR_SIZE; + } + } + + centralDirectorySize += CENTRAL_DIRECTORY_RECORD_FIXED_SIZE + entry.utf8FileName.length; + if (useZip64Format) { + centralDirectorySize += ZIP64_EXTENDED_INFORMATION_EXTRA_FIELD_SIZE; + } + } + + var endOfCentralDirectorySize = 0; + if (self.forceZip64Eocd || + self.entries.length >= 0xffff || + centralDirectorySize >= 0xffff || + pretendOutputCursor >= 0xffffffff) { + // use zip64 end of central directory stuff + endOfCentralDirectorySize += ZIP64_END_OF_CENTRAL_DIRECTORY_RECORD_SIZE + ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIZE; + } + endOfCentralDirectorySize += END_OF_CENTRAL_DIRECTORY_RECORD_SIZE; + return pretendOutputCursor + centralDirectorySize + endOfCentralDirectorySize; +} + +var ZIP64_END_OF_CENTRAL_DIRECTORY_RECORD_SIZE = 56; +var ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIZE = 20; +var END_OF_CENTRAL_DIRECTORY_RECORD_SIZE = 22; +function getEndOfCentralDirectoryRecord(self, actuallyJustTellMeHowLongItWouldBe) { + var needZip64Format = false; + var normalEntriesLength = self.entries.length; + if (self.forceZip64Eocd || self.entries.length >= 0xffff) { + normalEntriesLength = 0xffff; + needZip64Format = true; + } + var sizeOfCentralDirectory = self.outputStreamCursor - self.offsetOfStartOfCentralDirectory; + var normalSizeOfCentralDirectory = sizeOfCentralDirectory; + if (self.forceZip64Eocd || sizeOfCentralDirectory >= 0xffffffff) { + normalSizeOfCentralDirectory = 0xffffffff; + needZip64Format = true; + } + var normalOffsetOfStartOfCentralDirectory = self.offsetOfStartOfCentralDirectory; + if (self.forceZip64Eocd || self.offsetOfStartOfCentralDirectory >= 0xffffffff) { + normalOffsetOfStartOfCentralDirectory = 0xffffffff; + needZip64Format = true; + } + if (actuallyJustTellMeHowLongItWouldBe) { + if (needZip64Format) { + return ( + ZIP64_END_OF_CENTRAL_DIRECTORY_RECORD_SIZE + + ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIZE + + END_OF_CENTRAL_DIRECTORY_RECORD_SIZE + ); + } else { + return END_OF_CENTRAL_DIRECTORY_RECORD_SIZE; + } + } + + var eocdrBuffer = new Buffer(END_OF_CENTRAL_DIRECTORY_RECORD_SIZE); + // end of central dir signature 4 bytes (0x06054b50) + eocdrBuffer.writeUInt32LE(0x06054b50, 0); + // number of this disk 2 bytes + eocdrBuffer.writeUInt16LE(0, 4); + // number of the disk with the start of the central directory 2 bytes + eocdrBuffer.writeUInt16LE(0, 6); + // total number of entries in the central directory on this disk 2 bytes + eocdrBuffer.writeUInt16LE(normalEntriesLength, 8); + // total number of entries in the central directory 2 bytes + eocdrBuffer.writeUInt16LE(normalEntriesLength, 10); + // size of the central directory 4 bytes + eocdrBuffer.writeUInt32LE(normalSizeOfCentralDirectory, 12); + // offset of start of central directory with respect to the starting disk number 4 bytes + eocdrBuffer.writeUInt32LE(normalOffsetOfStartOfCentralDirectory, 16); + // .ZIP file comment length 2 bytes + eocdrBuffer.writeUInt16LE(0, 20); + // .ZIP file comment (variable size) + // no comment + + if (!needZip64Format) return eocdrBuffer; + + // ZIP64 format + // ZIP64 End of Central Directory Record + var zip64EocdrBuffer = new Buffer(ZIP64_END_OF_CENTRAL_DIRECTORY_RECORD_SIZE); + // zip64 end of central dir signature 4 bytes (0x06064b50) + zip64EocdrBuffer.writeUInt32LE(0x06064b50, 0); + // size of zip64 end of central directory record 8 bytes + writeUInt64LE(zip64EocdrBuffer, ZIP64_END_OF_CENTRAL_DIRECTORY_RECORD_SIZE - 12, 4); + // version made by 2 bytes + zip64EocdrBuffer.writeUInt16LE(VERSION_MADE_BY, 12); + // version needed to extract 2 bytes + zip64EocdrBuffer.writeUInt16LE(VERSION_NEEDED_TO_EXTRACT_ZIP64, 14); + // number of this disk 4 bytes + zip64EocdrBuffer.writeUInt32LE(0, 16); + // number of the disk with the start of the central directory 4 bytes + zip64EocdrBuffer.writeUInt32LE(0, 20); + // total number of entries in the central directory on this disk 8 bytes + writeUInt64LE(zip64EocdrBuffer, self.entries.length, 24); + // total number of entries in the central directory 8 bytes + writeUInt64LE(zip64EocdrBuffer, self.entries.length, 32); + // size of the central directory 8 bytes + writeUInt64LE(zip64EocdrBuffer, sizeOfCentralDirectory, 40); + // offset of start of central directory with respect to the starting disk number 8 bytes + writeUInt64LE(zip64EocdrBuffer, self.offsetOfStartOfCentralDirectory, 48); + // zip64 extensible data sector (variable size) + // nothing in the zip64 extensible data sector + + + // ZIP64 End of Central Directory Locator + var zip64EocdlBuffer = new Buffer(ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIZE); + // zip64 end of central dir locator signature 4 bytes (0x07064b50) + zip64EocdlBuffer.writeUInt32LE(0x07064b50, 0); + // number of the disk with the start of the zip64 end of central directory 4 bytes + zip64EocdlBuffer.writeUInt32LE(0, 4); + // relative offset of the zip64 end of central directory record 8 bytes + writeUInt64LE(zip64EocdlBuffer, self.outputStreamCursor, 8); + // total number of disks 4 bytes + zip64EocdlBuffer.writeUInt32LE(1, 16); + + + return Buffer.concat([ + zip64EocdrBuffer, + zip64EocdlBuffer, + eocdrBuffer, + ]); +} + +function validateMetadataPath(metadataPath, isDirectory) { + if (metadataPath === "") throw new Error("empty metadataPath"); + metadataPath = metadataPath.replace(/\\/g, "/"); + if (/^[a-zA-Z]:/.test(metadataPath) || /^\//.test(metadataPath)) throw new Error("absolute path: " + metadataPath); + if (metadataPath.split("/").indexOf("..") !== -1) throw new Error("invalid relative path: " + metadataPath); + var looksLikeDirectory = /\/$/.test(metadataPath); + if (isDirectory) { + // append a trailing '/' if necessary. + if (!looksLikeDirectory) metadataPath += "/"; + } else { + if (looksLikeDirectory) throw new Error("file path cannot end with '/': " + metadataPath); + } + return metadataPath; +} + +// this class is not part of the public API +function Entry(metadataPath, isDirectory, options) { + this.utf8FileName = new Buffer(metadataPath); + if (this.utf8FileName.length > 0xffff) throw new Error("utf8 file name too long. " + utf8FileName.length + " > " + 0xffff); + this.isDirectory = isDirectory; + this.state = Entry.WAITING_FOR_METADATA; + this.setLastModDate(options.mtime != null ? options.mtime : new Date()); + if (options.mode != null) { + this.setFileAttributesMode(options.mode); + } else { + this.setFileAttributesMode(isDirectory ? 040775 : 0100664); + } + if (isDirectory) { + this.crcAndFileSizeKnown = true; + this.crc32 = 0; + this.uncompressedSize = 0; + this.compressedSize = 0; + } else { + // unknown so far + this.crcAndFileSizeKnown = false; + this.crc32 = null; + this.uncompressedSize = null; + this.compressedSize = null; + if (options.size != null) this.uncompressedSize = options.size; + } + if (isDirectory) { + this.compress = false; + } else { + this.compress = true; // default + if (options.compress != null) this.compress = !!options.compress; + } + this.forceZip64Format = !!options.forceZip64Format; +} +Entry.WAITING_FOR_METADATA = 0; +Entry.READY_TO_PUMP_FILE_DATA = 1; +Entry.FILE_DATA_IN_PROGRESS = 2; +Entry.FILE_DATA_DONE = 3; +Entry.prototype.setLastModDate = function(date) { + var dosDateTime = dateToDosDateTime(date); + this.lastModFileTime = dosDateTime.time; + this.lastModFileDate = dosDateTime.date; +}; +Entry.prototype.setFileAttributesMode = function(mode) { + if ((mode & 0xffff) !== mode) throw new Error("invalid mode. expected: 0 <= " + mode + " <= " + 0xffff); + // http://unix.stackexchange.com/questions/14705/the-zip-formats-external-file-attribute/14727#14727 + this.externalFileAttributes = (mode << 16) >>> 0; +}; +// doFileDataPump() should not call pumpEntries() directly. see issue #9. +Entry.prototype.setFileDataPumpFunction = function(doFileDataPump) { + this.doFileDataPump = doFileDataPump; + this.state = Entry.READY_TO_PUMP_FILE_DATA; +}; +Entry.prototype.useZip64Format = function() { + return ( + (this.forceZip64Format) || + (this.uncompressedSize != null && this.uncompressedSize > 0xfffffffe) || + (this.compressedSize != null && this.compressedSize > 0xfffffffe) || + (this.relativeOffsetOfLocalHeader != null && this.relativeOffsetOfLocalHeader > 0xfffffffe) + ); +} +var LOCAL_FILE_HEADER_FIXED_SIZE = 30; +var VERSION_NEEDED_TO_EXTRACT_UTF8 = 20; +var VERSION_NEEDED_TO_EXTRACT_ZIP64 = 45; +// 3 = unix. 63 = spec version 6.3 +var VERSION_MADE_BY = (3 << 8) | 63; +var FILE_NAME_IS_UTF8 = 1 << 11; +var UNKNOWN_CRC32_AND_FILE_SIZES = 1 << 3; +Entry.prototype.getLocalFileHeader = function() { + var crc32 = 0; + var compressedSize = 0; + var uncompressedSize = 0; + if (this.crcAndFileSizeKnown) { + crc32 = this.crc32; + compressedSize = this.compressedSize; + uncompressedSize = this.uncompressedSize; + } + + var fixedSizeStuff = new Buffer(LOCAL_FILE_HEADER_FIXED_SIZE); + var generalPurposeBitFlag = FILE_NAME_IS_UTF8; + if (!this.crcAndFileSizeKnown) generalPurposeBitFlag |= UNKNOWN_CRC32_AND_FILE_SIZES; + + // local file header signature 4 bytes (0x04034b50) + fixedSizeStuff.writeUInt32LE(0x04034b50, 0); + // version needed to extract 2 bytes + fixedSizeStuff.writeUInt16LE(VERSION_NEEDED_TO_EXTRACT_UTF8, 4); + // general purpose bit flag 2 bytes + fixedSizeStuff.writeUInt16LE(generalPurposeBitFlag, 6); + // compression method 2 bytes + fixedSizeStuff.writeUInt16LE(this.getCompressionMethod(), 8); + // last mod file time 2 bytes + fixedSizeStuff.writeUInt16LE(this.lastModFileTime, 10); + // last mod file date 2 bytes + fixedSizeStuff.writeUInt16LE(this.lastModFileDate, 12); + // crc-32 4 bytes + fixedSizeStuff.writeUInt32LE(crc32, 14); + // compressed size 4 bytes + fixedSizeStuff.writeUInt32LE(compressedSize, 18); + // uncompressed size 4 bytes + fixedSizeStuff.writeUInt32LE(uncompressedSize, 22); + // file name length 2 bytes + fixedSizeStuff.writeUInt16LE(this.utf8FileName.length, 26); + // extra field length 2 bytes + fixedSizeStuff.writeUInt16LE(0, 28); + return Buffer.concat([ + fixedSizeStuff, + // file name (variable size) + this.utf8FileName, + // extra field (variable size) + // no extra fields + ]); +}; +var DATA_DESCRIPTOR_SIZE = 16; +var ZIP64_DATA_DESCRIPTOR_SIZE = 24; +Entry.prototype.getDataDescriptor = function() { + if (this.crcAndFileSizeKnown) { + // the Mac Archive Utility requires this not be present unless we set general purpose bit 3 + return new Buffer(0); + } + if (!this.useZip64Format()) { + var buffer = new Buffer(DATA_DESCRIPTOR_SIZE); + // optional signature (required according to Archive Utility) + buffer.writeUInt32LE(0x08074b50, 0); + // crc-32 4 bytes + buffer.writeUInt32LE(this.crc32, 4); + // compressed size 4 bytes + buffer.writeUInt32LE(this.compressedSize, 8); + // uncompressed size 4 bytes + buffer.writeUInt32LE(this.uncompressedSize, 12); + return buffer; + } else { + // ZIP64 format + var buffer = new Buffer(ZIP64_DATA_DESCRIPTOR_SIZE); + // optional signature (unknown if anyone cares about this) + buffer.writeUInt32LE(0x08074b50, 0); + // crc-32 4 bytes + buffer.writeUInt32LE(this.crc32, 4); + // compressed size 8 bytes + writeUInt64LE(buffer, this.compressedSize, 8); + // uncompressed size 8 bytes + writeUInt64LE(buffer, this.uncompressedSize, 16); + return buffer; + } +}; +var CENTRAL_DIRECTORY_RECORD_FIXED_SIZE = 46; +var ZIP64_EXTENDED_INFORMATION_EXTRA_FIELD_SIZE = 28; +Entry.prototype.getCentralDirectoryRecord = function() { + var fixedSizeStuff = new Buffer(CENTRAL_DIRECTORY_RECORD_FIXED_SIZE); + var generalPurposeBitFlag = FILE_NAME_IS_UTF8; + if (!this.crcAndFileSizeKnown) generalPurposeBitFlag |= UNKNOWN_CRC32_AND_FILE_SIZES; + + var normalCompressedSize = this.compressedSize; + var normalUncompressedSize = this.uncompressedSize; + var normalRelativeOffsetOfLocalHeader = this.relativeOffsetOfLocalHeader; + var versionNeededToExtract; + var zeiefBuffer; + if (this.useZip64Format()) { + normalCompressedSize = 0xffffffff; + normalUncompressedSize = 0xffffffff; + normalRelativeOffsetOfLocalHeader = 0xffffffff; + versionNeededToExtract = VERSION_NEEDED_TO_EXTRACT_ZIP64; + + // ZIP64 extended information extra field + zeiefBuffer = new Buffer(ZIP64_EXTENDED_INFORMATION_EXTRA_FIELD_SIZE); + // 0x0001 2 bytes Tag for this "extra" block type + zeiefBuffer.writeUInt16LE(0x0001, 0); + // Size 2 bytes Size of this "extra" block + zeiefBuffer.writeUInt16LE(ZIP64_EXTENDED_INFORMATION_EXTRA_FIELD_SIZE - 4, 2); + // Original Size 8 bytes Original uncompressed file size + writeUInt64LE(zeiefBuffer, this.uncompressedSize, 4); + // Compressed Size 8 bytes Size of compressed data + writeUInt64LE(zeiefBuffer, this.compressedSize, 12); + // Relative Header Offset 8 bytes Offset of local header record + writeUInt64LE(zeiefBuffer, this.relativeOffsetOfLocalHeader, 20); + // Disk Start Number 4 bytes Number of the disk on which this file starts + // (omit) + } else { + versionNeededToExtract = VERSION_NEEDED_TO_EXTRACT_UTF8; + zeiefBuffer = new Buffer(0); + } + + // central file header signature 4 bytes (0x02014b50) + fixedSizeStuff.writeUInt32LE(0x02014b50, 0); + // version made by 2 bytes + fixedSizeStuff.writeUInt16LE(VERSION_MADE_BY, 4); + // version needed to extract 2 bytes + fixedSizeStuff.writeUInt16LE(versionNeededToExtract, 6); + // general purpose bit flag 2 bytes + fixedSizeStuff.writeUInt16LE(generalPurposeBitFlag, 8); + // compression method 2 bytes + fixedSizeStuff.writeUInt16LE(this.getCompressionMethod(), 10); + // last mod file time 2 bytes + fixedSizeStuff.writeUInt16LE(this.lastModFileTime, 12); + // last mod file date 2 bytes + fixedSizeStuff.writeUInt16LE(this.lastModFileDate, 14); + // crc-32 4 bytes + fixedSizeStuff.writeUInt32LE(this.crc32, 16); + // compressed size 4 bytes + fixedSizeStuff.writeUInt32LE(normalCompressedSize, 20); + // uncompressed size 4 bytes + fixedSizeStuff.writeUInt32LE(normalUncompressedSize, 24); + // file name length 2 bytes + fixedSizeStuff.writeUInt16LE(this.utf8FileName.length, 28); + // extra field length 2 bytes + fixedSizeStuff.writeUInt16LE(zeiefBuffer.length, 30); + // file comment length 2 bytes + fixedSizeStuff.writeUInt16LE(0, 32); + // disk number start 2 bytes + fixedSizeStuff.writeUInt16LE(0, 34); + // internal file attributes 2 bytes + fixedSizeStuff.writeUInt16LE(0, 36); + // external file attributes 4 bytes + fixedSizeStuff.writeUInt32LE(this.externalFileAttributes, 38); + // relative offset of local header 4 bytes + fixedSizeStuff.writeUInt32LE(normalRelativeOffsetOfLocalHeader, 42); + + return Buffer.concat([ + fixedSizeStuff, + // file name (variable size) + this.utf8FileName, + // extra field (variable size) + zeiefBuffer, + // file comment (variable size) + // empty comment + ]); +}; +Entry.prototype.getCompressionMethod = function() { + var NO_COMPRESSION = 0; + var DEFLATE_COMPRESSION = 8; + return this.compress ? DEFLATE_COMPRESSION : NO_COMPRESSION; +}; + +function dateToDosDateTime(jsDate) { + var date = 0; + date |= jsDate.getDate() & 0x1f; // 1-31 + date |= ((jsDate.getMonth() + 1) & 0xf) << 5; // 0-11, 1-12 + date |= ((jsDate.getFullYear() - 1980) & 0x7f) << 9; // 0-128, 1980-2108 + + var time = 0; + time |= Math.floor(jsDate.getSeconds() / 2); // 0-59, 0-29 (lose odd numbers) + time |= (jsDate.getMinutes() & 0x3f) << 5; // 0-59 + time |= (jsDate.getHours() & 0x1f) << 11; // 0-23 + + return {date: date, time: time}; +} + +function writeUInt64LE(buffer, n, offset) { + // can't use bitshift here, because JavaScript only allows bitshiting on 32-bit integers. + var high = Math.floor(n / 0x100000000); + var low = n % 0x100000000; + buffer.writeUInt32LE(low, offset); + buffer.writeUInt32LE(high, offset + 4); +} + +function defaultCallback(err) { + if (err) throw err; +} + +util.inherits(ByteCounter, Transform); +function ByteCounter(options) { + Transform.call(this, options); + this.byteCount = 0; +} +ByteCounter.prototype._transform = function(chunk, encoding, cb) { + this.byteCount += chunk.length; + cb(null, chunk); +}; + +util.inherits(Crc32Watcher, Transform); +function Crc32Watcher(options) { + Transform.call(this, options); + this.crc32 = 0; +} +Crc32Watcher.prototype._transform = function(chunk, encoding, cb) { + this.crc32 = crc32.unsigned(chunk, this.crc32); + cb(null, chunk); +}; diff --git a/node_modules/yazl/package.json b/node_modules/yazl/package.json new file mode 100644 index 000000000..103948bf2 --- /dev/null +++ b/node_modules/yazl/package.json @@ -0,0 +1,98 @@ +{ + "_args": [ + [ + { + "raw": "yazl@^2.1.0", + "scope": null, + "escapedName": "yazl", + "name": "yazl", + "rawSpec": "^2.1.0", + "spec": ">=2.1.0 <3.0.0", + "type": "range" + }, + "/home/dold/repos/taler/wallet-webex/node_modules/gulp-zip" + ] + ], + "_from": "yazl@>=2.1.0 <3.0.0", + "_id": "yazl@2.4.1", + "_inCache": true, + "_location": "/yazl", + "_nodeVersion": "4.2.6", + "_npmOperationalInternal": { + "host": "packages-16-east.internal.npmjs.com", + "tmp": "tmp/yazl-2.4.1.tgz_1467311328186_0.7062251013703644" + }, + "_npmUser": { + "name": "thejoshwolfe", + "email": "thejoshwolfe@gmail.com" + }, + "_npmVersion": "3.5.2", + "_phantomChildren": {}, + "_requested": { + "raw": "yazl@^2.1.0", + "scope": null, + "escapedName": "yazl", + "name": "yazl", + "rawSpec": "^2.1.0", + "spec": ">=2.1.0 <3.0.0", + "type": "range" + }, + "_requiredBy": [ + "/gulp-zip" + ], + "_resolved": "https://registry.npmjs.org/yazl/-/yazl-2.4.1.tgz", + "_shasum": "2bc98ebdfeccf0c2b47cc36f82214bcb6d54484c", + "_shrinkwrap": null, + "_spec": "yazl@^2.1.0", + "_where": "/home/dold/repos/taler/wallet-webex/node_modules/gulp-zip", + "author": { + "name": "Josh Wolfe", + "email": "thejoshwolfe@gmail.com" + }, + "bugs": { + "url": "https://github.com/thejoshwolfe/yazl/issues" + }, + "dependencies": { + "buffer-crc32": "~0.2.3" + }, + "description": "yet another zip library for node", + "devDependencies": { + "bl": "~0.9.3", + "yauzl": "~2.3.1" + }, + "directories": {}, + "dist": { + "shasum": "2bc98ebdfeccf0c2b47cc36f82214bcb6d54484c", + "tarball": "https://registry.npmjs.org/yazl/-/yazl-2.4.1.tgz" + }, + "files": [ + "index.js" + ], + "gitHead": "8d25332df007a514b0b0d5c94a640984991d93e7", + "homepage": "https://github.com/thejoshwolfe/yazl", + "keywords": [ + "zip", + "stream", + "archive", + "file" + ], + "license": "MIT", + "main": "index.js", + "maintainers": [ + { + "name": "thejoshwolfe", + "email": "thejoshwolfe@gmail.com" + } + ], + "name": "yazl", + "optionalDependencies": {}, + "readme": "ERROR: No README data found!", + "repository": { + "type": "git", + "url": "git+https://github.com/thejoshwolfe/yazl.git" + }, + "scripts": { + "test": "node test/test.js" + }, + "version": "2.4.1" +} |