| Leo Repp | 58b9f11 | 2021-11-22 11:57:47 +0100 | [diff] [blame^] | 1 | 'use strict'; |
| 2 | const fileType = require('file-type'); |
| 3 | const getStream = require('get-stream'); |
| 4 | const pify = require('pify'); |
| 5 | const yauzl = require('yauzl'); |
| 6 | |
| 7 | const getType = (entry, mode) => { |
| 8 | const IFMT = 61440; |
| 9 | const IFDIR = 16384; |
| 10 | const IFLNK = 40960; |
| 11 | const madeBy = entry.versionMadeBy >> 8; |
| 12 | |
| 13 | if ((mode & IFMT) === IFLNK) { |
| 14 | return 'symlink'; |
| 15 | } |
| 16 | |
| 17 | if ((mode & IFMT) === IFDIR || (madeBy === 0 && entry.externalFileAttributes === 16)) { |
| 18 | return 'directory'; |
| 19 | } |
| 20 | |
| 21 | return 'file'; |
| 22 | }; |
| 23 | |
| 24 | const extractEntry = (entry, zip) => { |
| 25 | const file = { |
| 26 | mode: (entry.externalFileAttributes >> 16) & 0xFFFF, |
| 27 | mtime: entry.getLastModDate(), |
| 28 | path: entry.fileName |
| 29 | }; |
| 30 | |
| 31 | file.type = getType(entry, file.mode); |
| 32 | |
| 33 | if (file.mode === 0 && file.type === 'directory') { |
| 34 | file.mode = 493; |
| 35 | } |
| 36 | |
| 37 | if (file.mode === 0) { |
| 38 | file.mode = 420; |
| 39 | } |
| 40 | |
| 41 | return pify(zip.openReadStream.bind(zip))(entry) |
| 42 | .then(getStream.buffer) |
| 43 | .then(buf => { |
| 44 | file.data = buf; |
| 45 | |
| 46 | if (file.type === 'symlink') { |
| 47 | file.linkname = buf.toString(); |
| 48 | } |
| 49 | |
| 50 | return file; |
| 51 | }) |
| 52 | .catch(err => { |
| 53 | zip.close(); |
| 54 | throw err; |
| 55 | }); |
| 56 | }; |
| 57 | |
| 58 | const extractFile = zip => new Promise((resolve, reject) => { |
| 59 | const files = []; |
| 60 | |
| 61 | zip.readEntry(); |
| 62 | |
| 63 | zip.on('entry', entry => { |
| 64 | extractEntry(entry, zip) |
| 65 | .catch(reject) |
| 66 | .then(file => { |
| 67 | files.push(file); |
| 68 | zip.readEntry(); |
| 69 | }); |
| 70 | }); |
| 71 | |
| 72 | zip.on('error', reject); |
| 73 | zip.on('end', () => resolve(files)); |
| 74 | }); |
| 75 | |
| 76 | module.exports = () => buf => { |
| 77 | if (!Buffer.isBuffer(buf)) { |
| 78 | return Promise.reject(new TypeError(`Expected a Buffer, got ${typeof buf}`)); |
| 79 | } |
| 80 | |
| 81 | if (!fileType(buf) || fileType(buf).ext !== 'zip') { |
| 82 | return Promise.resolve([]); |
| 83 | } |
| 84 | |
| 85 | return pify(yauzl.fromBuffer)(buf, {lazyEntries: true}).then(extractFile); |
| 86 | }; |