You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							258 lines
						
					
					
						
							5.8 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							258 lines
						
					
					
						
							5.8 KiB
						
					
					
				
								var util = require('util')
							 | 
						|
								var bl = require('bl')
							 | 
						|
								var xtend = require('xtend')
							 | 
						|
								var headers = require('./headers')
							 | 
						|
								
							 | 
						|
								var Writable = require('readable-stream').Writable
							 | 
						|
								var PassThrough = require('readable-stream').PassThrough
							 | 
						|
								
							 | 
						|
								var noop = function () {}
							 | 
						|
								
							 | 
						|
								var overflow = function (size) {
							 | 
						|
								  size &= 511
							 | 
						|
								  return size && 512 - size
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								var emptyStream = function (self, offset) {
							 | 
						|
								  var s = new Source(self, offset)
							 | 
						|
								  s.end()
							 | 
						|
								  return s
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								var mixinPax = function (header, pax) {
							 | 
						|
								  if (pax.path) header.name = pax.path
							 | 
						|
								  if (pax.linkpath) header.linkname = pax.linkpath
							 | 
						|
								  if (pax.size) header.size = parseInt(pax.size, 10)
							 | 
						|
								  header.pax = pax
							 | 
						|
								  return header
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								var Source = function (self, offset) {
							 | 
						|
								  this._parent = self
							 | 
						|
								  this.offset = offset
							 | 
						|
								  PassThrough.call(this)
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								util.inherits(Source, PassThrough)
							 | 
						|
								
							 | 
						|
								Source.prototype.destroy = function (err) {
							 | 
						|
								  this._parent.destroy(err)
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								var Extract = function (opts) {
							 | 
						|
								  if (!(this instanceof Extract)) return new Extract(opts)
							 | 
						|
								  Writable.call(this, opts)
							 | 
						|
								
							 | 
						|
								  opts = opts || {}
							 | 
						|
								
							 | 
						|
								  this._offset = 0
							 | 
						|
								  this._buffer = bl()
							 | 
						|
								  this._missing = 0
							 | 
						|
								  this._partial = false
							 | 
						|
								  this._onparse = noop
							 | 
						|
								  this._header = null
							 | 
						|
								  this._stream = null
							 | 
						|
								  this._overflow = null
							 | 
						|
								  this._cb = null
							 | 
						|
								  this._locked = false
							 | 
						|
								  this._destroyed = false
							 | 
						|
								  this._pax = null
							 | 
						|
								  this._paxGlobal = null
							 | 
						|
								  this._gnuLongPath = null
							 | 
						|
								  this._gnuLongLinkPath = null
							 | 
						|
								
							 | 
						|
								  var self = this
							 | 
						|
								  var b = self._buffer
							 | 
						|
								
							 | 
						|
								  var oncontinue = function () {
							 | 
						|
								    self._continue()
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  var onunlock = function (err) {
							 | 
						|
								    self._locked = false
							 | 
						|
								    if (err) return self.destroy(err)
							 | 
						|
								    if (!self._stream) oncontinue()
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  var onstreamend = function () {
							 | 
						|
								    self._stream = null
							 | 
						|
								    var drain = overflow(self._header.size)
							 | 
						|
								    if (drain) self._parse(drain, ondrain)
							 | 
						|
								    else self._parse(512, onheader)
							 | 
						|
								    if (!self._locked) oncontinue()
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  var ondrain = function () {
							 | 
						|
								    self._buffer.consume(overflow(self._header.size))
							 | 
						|
								    self._parse(512, onheader)
							 | 
						|
								    oncontinue()
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  var onpaxglobalheader = function () {
							 | 
						|
								    var size = self._header.size
							 | 
						|
								    self._paxGlobal = headers.decodePax(b.slice(0, size))
							 | 
						|
								    b.consume(size)
							 | 
						|
								    onstreamend()
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  var onpaxheader = function () {
							 | 
						|
								    var size = self._header.size
							 | 
						|
								    self._pax = headers.decodePax(b.slice(0, size))
							 | 
						|
								    if (self._paxGlobal) self._pax = xtend(self._paxGlobal, self._pax)
							 | 
						|
								    b.consume(size)
							 | 
						|
								    onstreamend()
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  var ongnulongpath = function () {
							 | 
						|
								    var size = self._header.size
							 | 
						|
								    this._gnuLongPath = headers.decodeLongPath(b.slice(0, size), opts.filenameEncoding)
							 | 
						|
								    b.consume(size)
							 | 
						|
								    onstreamend()
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  var ongnulonglinkpath = function () {
							 | 
						|
								    var size = self._header.size
							 | 
						|
								    this._gnuLongLinkPath = headers.decodeLongPath(b.slice(0, size), opts.filenameEncoding)
							 | 
						|
								    b.consume(size)
							 | 
						|
								    onstreamend()
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  var onheader = function () {
							 | 
						|
								    var offset = self._offset
							 | 
						|
								    var header
							 | 
						|
								    try {
							 | 
						|
								      header = self._header = headers.decode(b.slice(0, 512), opts.filenameEncoding)
							 | 
						|
								    } catch (err) {
							 | 
						|
								      self.emit('error', err)
							 | 
						|
								    }
							 | 
						|
								    b.consume(512)
							 | 
						|
								
							 | 
						|
								    if (!header) {
							 | 
						|
								      self._parse(512, onheader)
							 | 
						|
								      oncontinue()
							 | 
						|
								      return
							 | 
						|
								    }
							 | 
						|
								    if (header.type === 'gnu-long-path') {
							 | 
						|
								      self._parse(header.size, ongnulongpath)
							 | 
						|
								      oncontinue()
							 | 
						|
								      return
							 | 
						|
								    }
							 | 
						|
								    if (header.type === 'gnu-long-link-path') {
							 | 
						|
								      self._parse(header.size, ongnulonglinkpath)
							 | 
						|
								      oncontinue()
							 | 
						|
								      return
							 | 
						|
								    }
							 | 
						|
								    if (header.type === 'pax-global-header') {
							 | 
						|
								      self._parse(header.size, onpaxglobalheader)
							 | 
						|
								      oncontinue()
							 | 
						|
								      return
							 | 
						|
								    }
							 | 
						|
								    if (header.type === 'pax-header') {
							 | 
						|
								      self._parse(header.size, onpaxheader)
							 | 
						|
								      oncontinue()
							 | 
						|
								      return
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    if (self._gnuLongPath) {
							 | 
						|
								      header.name = self._gnuLongPath
							 | 
						|
								      self._gnuLongPath = null
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    if (self._gnuLongLinkPath) {
							 | 
						|
								      header.linkname = self._gnuLongLinkPath
							 | 
						|
								      self._gnuLongLinkPath = null
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    if (self._pax) {
							 | 
						|
								      self._header = header = mixinPax(header, self._pax)
							 | 
						|
								      self._pax = null
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    self._locked = true
							 | 
						|
								
							 | 
						|
								    if (!header.size || header.type === 'directory') {
							 | 
						|
								      self._parse(512, onheader)
							 | 
						|
								      self.emit('entry', header, emptyStream(self, offset), onunlock)
							 | 
						|
								      return
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    self._stream = new Source(self, offset)
							 | 
						|
								
							 | 
						|
								    self.emit('entry', header, self._stream, onunlock)
							 | 
						|
								    self._parse(header.size, onstreamend)
							 | 
						|
								    oncontinue()
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  this._onheader = onheader
							 | 
						|
								  this._parse(512, onheader)
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								util.inherits(Extract, Writable)
							 | 
						|
								
							 | 
						|
								Extract.prototype.destroy = function (err) {
							 | 
						|
								  if (this._destroyed) return
							 | 
						|
								  this._destroyed = true
							 | 
						|
								
							 | 
						|
								  if (err) this.emit('error', err)
							 | 
						|
								  this.emit('close')
							 | 
						|
								  if (this._stream) this._stream.emit('close')
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								Extract.prototype._parse = function (size, onparse) {
							 | 
						|
								  if (this._destroyed) return
							 | 
						|
								  this._offset += size
							 | 
						|
								  this._missing = size
							 | 
						|
								  if (onparse === this._onheader) this._partial = false
							 | 
						|
								  this._onparse = onparse
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								Extract.prototype._continue = function () {
							 | 
						|
								  if (this._destroyed) return
							 | 
						|
								  var cb = this._cb
							 | 
						|
								  this._cb = noop
							 | 
						|
								  if (this._overflow) this._write(this._overflow, undefined, cb)
							 | 
						|
								  else cb()
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								Extract.prototype._write = function (data, enc, cb) {
							 | 
						|
								  if (this._destroyed) return
							 | 
						|
								
							 | 
						|
								  var s = this._stream
							 | 
						|
								  var b = this._buffer
							 | 
						|
								  var missing = this._missing
							 | 
						|
								  if (data.length) this._partial = true
							 | 
						|
								
							 | 
						|
								  // we do not reach end-of-chunk now. just forward it
							 | 
						|
								
							 | 
						|
								  if (data.length < missing) {
							 | 
						|
								    this._missing -= data.length
							 | 
						|
								    this._overflow = null
							 | 
						|
								    if (s) return s.write(data, cb)
							 | 
						|
								    b.append(data)
							 | 
						|
								    return cb()
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  // end-of-chunk. the parser should call cb.
							 | 
						|
								
							 | 
						|
								  this._cb = cb
							 | 
						|
								  this._missing = 0
							 | 
						|
								
							 | 
						|
								  var overflow = null
							 | 
						|
								  if (data.length > missing) {
							 | 
						|
								    overflow = data.slice(missing)
							 | 
						|
								    data = data.slice(0, missing)
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  if (s) s.end(data)
							 | 
						|
								  else b.append(data)
							 | 
						|
								
							 | 
						|
								  this._overflow = overflow
							 | 
						|
								  this._onparse()
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								Extract.prototype._final = function (cb) {
							 | 
						|
								  if (this._partial) return this.destroy(new Error('Unexpected end of data'))
							 | 
						|
								  cb()
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								module.exports = Extract
							 | 
						|
								
							 |