aboutsummaryrefslogtreecommitdiff
path: root/node_modules/decompress/index.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/decompress/index.js')
-rw-r--r--node_modules/decompress/index.js151
1 files changed, 151 insertions, 0 deletions
diff --git a/node_modules/decompress/index.js b/node_modules/decompress/index.js
new file mode 100644
index 0000000..6aa67ca
--- /dev/null
+++ b/node_modules/decompress/index.js
@@ -0,0 +1,151 @@
+'use strict';
+const path = require('path');
+const fs = require('graceful-fs');
+const decompressTar = require('decompress-tar');
+const decompressTarbz2 = require('decompress-tarbz2');
+const decompressTargz = require('decompress-targz');
+const decompressUnzip = require('decompress-unzip');
+const makeDir = require('make-dir');
+const pify = require('pify');
+const stripDirs = require('strip-dirs');
+
+const fsP = pify(fs);
+
+const runPlugins = (input, opts) => {
+ if (opts.plugins.length === 0) {
+ return Promise.resolve([]);
+ }
+
+ return Promise.all(opts.plugins.map(x => x(input, opts))).then(files => files.reduce((a, b) => a.concat(b)));
+};
+
+const safeMakeDir = (dir, realOutputPath) => {
+ return fsP.realpath(dir)
+ .catch(_ => {
+ const parent = path.dirname(dir);
+ return safeMakeDir(parent, realOutputPath);
+ })
+ .then(realParentPath => {
+ if (realParentPath.indexOf(realOutputPath) !== 0) {
+ throw (new Error('Refusing to create a directory outside the output path.'));
+ }
+
+ return makeDir(dir).then(fsP.realpath);
+ });
+};
+
+const preventWritingThroughSymlink = (destination, realOutputPath) => {
+ return fsP.readlink(destination)
+ .catch(_ => {
+ // Either no file exists, or it's not a symlink. In either case, this is
+ // not an escape we need to worry about in this phase.
+ return null;
+ })
+ .then(symlinkPointsTo => {
+ if (symlinkPointsTo) {
+ throw new Error('Refusing to write into a symlink');
+ }
+
+ // No symlink exists at `destination`, so we can continue
+ return realOutputPath;
+ });
+};
+
+const extractFile = (input, output, opts) => runPlugins(input, opts).then(files => {
+ if (opts.strip > 0) {
+ files = files
+ .map(x => {
+ x.path = stripDirs(x.path, opts.strip);
+ return x;
+ })
+ .filter(x => x.path !== '.');
+ }
+
+ if (typeof opts.filter === 'function') {
+ files = files.filter(opts.filter);
+ }
+
+ if (typeof opts.map === 'function') {
+ files = files.map(opts.map);
+ }
+
+ if (!output) {
+ return files;
+ }
+
+ return Promise.all(files.map(x => {
+ const dest = path.join(output, x.path);
+ const mode = x.mode & ~process.umask();
+ const now = new Date();
+
+ if (x.type === 'directory') {
+ return makeDir(output)
+ .then(outputPath => fsP.realpath(outputPath))
+ .then(realOutputPath => safeMakeDir(dest, realOutputPath))
+ .then(() => fsP.utimes(dest, now, x.mtime))
+ .then(() => x);
+ }
+
+ return makeDir(output)
+ .then(outputPath => fsP.realpath(outputPath))
+ .then(realOutputPath => {
+ // Attempt to ensure parent directory exists (failing if it's outside the output dir)
+ return safeMakeDir(path.dirname(dest), realOutputPath)
+ .then(() => realOutputPath);
+ })
+ .then(realOutputPath => {
+ if (x.type === 'file') {
+ return preventWritingThroughSymlink(dest, realOutputPath);
+ }
+
+ return realOutputPath;
+ })
+ .then(realOutputPath => {
+ return fsP.realpath(path.dirname(dest))
+ .then(realDestinationDir => {
+ if (realDestinationDir.indexOf(realOutputPath) !== 0) {
+ throw (new Error('Refusing to write outside output directory: ' + realDestinationDir));
+ }
+ });
+ })
+ .then(() => {
+ if (x.type === 'link') {
+ return fsP.link(x.linkname, dest);
+ }
+
+ if (x.type === 'symlink' && process.platform === 'win32') {
+ return fsP.link(x.linkname, dest);
+ }
+
+ if (x.type === 'symlink') {
+ return fsP.symlink(x.linkname, dest);
+ }
+
+ return fsP.writeFile(dest, x.data, {mode});
+ })
+ .then(() => x.type === 'file' && fsP.utimes(dest, now, x.mtime))
+ .then(() => x);
+ }));
+});
+
+module.exports = (input, output, opts) => {
+ if (typeof input !== 'string' && !Buffer.isBuffer(input)) {
+ return Promise.reject(new TypeError('Input file required'));
+ }
+
+ if (typeof output === 'object') {
+ opts = output;
+ output = null;
+ }
+
+ opts = Object.assign({plugins: [
+ decompressTar(),
+ decompressTarbz2(),
+ decompressTargz(),
+ decompressUnzip()
+ ]}, opts);
+
+ const read = typeof input === 'string' ? fsP.readFile(input) : Promise.resolve(input);
+
+ return read.then(buf => extractFile(buf, output, opts));
+};