unzip.js 3.8 KB

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