unzip.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. const Decrypt = require('../Decrypt');
  2. const PullStream = require('../PullStream');
  3. const Stream = require('stream');
  4. const zlib = require('zlib');
  5. const parseExtraField = require('../parseExtraField');
  6. const parseDateTime = require('../parseDateTime');
  7. const parseBuffer = require('../parseBuffer');
  8. module.exports = function unzip(source, offset, _password, directoryVars, length) {
  9. const file = PullStream(),
  10. entry = Stream.PassThrough();
  11. const req = source.stream(offset, length);
  12. req.pipe(file).on('error', function(e) {
  13. entry.emit('error', e);
  14. });
  15. entry.vars = file.pull(30)
  16. .then(function(data) {
  17. let vars = parseBuffer.parse(data, [
  18. ['signature', 4],
  19. ['versionsNeededToExtract', 2],
  20. ['flags', 2],
  21. ['compressionMethod', 2],
  22. ['lastModifiedTime', 2],
  23. ['lastModifiedDate', 2],
  24. ['crc32', 4],
  25. ['compressedSize', 4],
  26. ['uncompressedSize', 4],
  27. ['fileNameLength', 2],
  28. ['extraFieldLength', 2],
  29. ]);
  30. vars.lastModifiedDateTime = parseDateTime(vars.lastModifiedDate, vars.lastModifiedTime);
  31. return file.pull(vars.fileNameLength)
  32. .then(function(fileName) {
  33. vars.fileName = fileName.toString('utf8');
  34. return file.pull(vars.extraFieldLength);
  35. })
  36. .then(function(extraField) {
  37. let checkEncryption;
  38. vars.extra = parseExtraField(extraField, vars);
  39. // Ignore logal file header vars if the directory vars are available
  40. if (directoryVars && directoryVars.compressedSize) vars = directoryVars;
  41. if (vars.flags & 0x01) checkEncryption = file.pull(12)
  42. .then(function(header) {
  43. if (!_password)
  44. throw new Error('MISSING_PASSWORD');
  45. const decrypt = Decrypt();
  46. String(_password).split('').forEach(function(d) {
  47. decrypt.update(d);
  48. });
  49. for (let i=0; i < header.length; i++)
  50. header[i] = decrypt.decryptByte(header[i]);
  51. vars.decrypt = decrypt;
  52. vars.compressedSize -= 12;
  53. const check = (vars.flags & 0x8) ? (vars.lastModifiedTime >> 8) & 0xff : (vars.crc32 >> 24) & 0xff;
  54. if (header[11] !== check)
  55. throw new Error('BAD_PASSWORD');
  56. return vars;
  57. });
  58. return Promise.resolve(checkEncryption)
  59. .then(function() {
  60. entry.emit('vars', vars);
  61. return vars;
  62. });
  63. });
  64. });
  65. entry.vars.then(function(vars) {
  66. const fileSizeKnown = !(vars.flags & 0x08) || vars.compressedSize > 0;
  67. let eof;
  68. const inflater = vars.compressionMethod ? zlib.createInflateRaw() : Stream.PassThrough();
  69. if (fileSizeKnown) {
  70. entry.size = vars.uncompressedSize;
  71. eof = vars.compressedSize;
  72. } else {
  73. eof = Buffer.alloc(4);
  74. eof.writeUInt32LE(0x08074b50, 0);
  75. }
  76. let stream = file.stream(eof);
  77. if (vars.decrypt)
  78. stream = stream.pipe(vars.decrypt.stream());
  79. stream
  80. .pipe(inflater)
  81. .on('error', function(err) { entry.emit('error', err);})
  82. .pipe(entry)
  83. .on('finish', function() {
  84. if(req.destroy)
  85. req.destroy();
  86. else if (req.abort)
  87. req.abort();
  88. else if (req.close)
  89. req.close();
  90. else if (req.push)
  91. req.push();
  92. else
  93. console.log('warning - unable to close stream');
  94. });
  95. })
  96. .catch(function(e) {
  97. entry.emit('error', e);
  98. });
  99. return entry;
  100. };