All files / node-unzipper/lib/Open unzip.js

90.08% Statements 109/121
88% Branches 22/25
100% Functions 1/1
90.08% Lines 109/121

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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