rubyzip 1.2.3 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +64 -23
  3. data/lib/zip.rb +5 -3
  4. data/lib/zip/constants.rb +52 -0
  5. data/lib/zip/crypto/decrypted_io.rb +39 -0
  6. data/lib/zip/decompressor.rb +19 -1
  7. data/lib/zip/dos_time.rb +5 -0
  8. data/lib/zip/entry.rb +34 -10
  9. data/lib/zip/errors.rb +2 -0
  10. data/lib/zip/extra_field/generic.rb +1 -1
  11. data/lib/zip/extra_field/universal_time.rb +39 -12
  12. data/lib/zip/file.rb +68 -34
  13. data/lib/zip/inflater.rb +22 -36
  14. data/lib/zip/input_stream.rb +28 -24
  15. data/lib/zip/ioextras/abstract_input_stream.rb +6 -0
  16. data/lib/zip/null_decompressor.rb +1 -9
  17. data/lib/zip/pass_thru_decompressor.rb +13 -22
  18. data/lib/zip/streamable_stream.rb +1 -6
  19. data/lib/zip/version.rb +1 -1
  20. metadata +12 -154
  21. data/test/basic_zip_file_test.rb +0 -60
  22. data/test/case_sensitivity_test.rb +0 -69
  23. data/test/central_directory_entry_test.rb +0 -69
  24. data/test/central_directory_test.rb +0 -100
  25. data/test/crypto/null_encryption_test.rb +0 -57
  26. data/test/crypto/traditional_encryption_test.rb +0 -80
  27. data/test/data/WarnInvalidDate.zip +0 -0
  28. data/test/data/file1.txt +0 -46
  29. data/test/data/file1.txt.deflatedData +0 -0
  30. data/test/data/file2.txt +0 -1504
  31. data/test/data/globTest.zip +0 -0
  32. data/test/data/globTest/foo.txt +0 -0
  33. data/test/data/globTest/foo/bar/baz/foo.txt +0 -0
  34. data/test/data/globTest/food.txt +0 -0
  35. data/test/data/gpbit3stored.zip +0 -0
  36. data/test/data/mimetype +0 -1
  37. data/test/data/notzippedruby.rb +0 -7
  38. data/test/data/ntfs.zip +0 -0
  39. data/test/data/oddExtraField.zip +0 -0
  40. data/test/data/path_traversal/Makefile +0 -10
  41. data/test/data/path_traversal/jwilk/README.md +0 -5
  42. data/test/data/path_traversal/jwilk/absolute1.zip +0 -0
  43. data/test/data/path_traversal/jwilk/absolute2.zip +0 -0
  44. data/test/data/path_traversal/jwilk/dirsymlink.zip +0 -0
  45. data/test/data/path_traversal/jwilk/dirsymlink2a.zip +0 -0
  46. data/test/data/path_traversal/jwilk/dirsymlink2b.zip +0 -0
  47. data/test/data/path_traversal/jwilk/relative0.zip +0 -0
  48. data/test/data/path_traversal/jwilk/relative2.zip +0 -0
  49. data/test/data/path_traversal/jwilk/symlink.zip +0 -0
  50. data/test/data/path_traversal/relative1.zip +0 -0
  51. data/test/data/path_traversal/tilde.zip +0 -0
  52. data/test/data/path_traversal/tuzovakaoff/README.md +0 -3
  53. data/test/data/path_traversal/tuzovakaoff/absolutepath.zip +0 -0
  54. data/test/data/path_traversal/tuzovakaoff/symlink.zip +0 -0
  55. data/test/data/rubycode.zip +0 -0
  56. data/test/data/rubycode2.zip +0 -0
  57. data/test/data/test.xls +0 -0
  58. data/test/data/testDirectory.bin +0 -0
  59. data/test/data/zip64-sample.zip +0 -0
  60. data/test/data/zipWithDirs.zip +0 -0
  61. data/test/data/zipWithEncryption.zip +0 -0
  62. data/test/deflater_test.rb +0 -65
  63. data/test/encryption_test.rb +0 -42
  64. data/test/entry_set_test.rb +0 -163
  65. data/test/entry_test.rb +0 -154
  66. data/test/errors_test.rb +0 -35
  67. data/test/extra_field_test.rb +0 -76
  68. data/test/file_extract_directory_test.rb +0 -54
  69. data/test/file_extract_test.rb +0 -83
  70. data/test/file_permissions_test.rb +0 -65
  71. data/test/file_split_test.rb +0 -57
  72. data/test/file_test.rb +0 -601
  73. data/test/filesystem/dir_iterator_test.rb +0 -58
  74. data/test/filesystem/directory_test.rb +0 -139
  75. data/test/filesystem/file_mutating_test.rb +0 -87
  76. data/test/filesystem/file_nonmutating_test.rb +0 -508
  77. data/test/filesystem/file_stat_test.rb +0 -64
  78. data/test/gentestfiles.rb +0 -126
  79. data/test/inflater_test.rb +0 -14
  80. data/test/input_stream_test.rb +0 -182
  81. data/test/ioextras/abstract_input_stream_test.rb +0 -102
  82. data/test/ioextras/abstract_output_stream_test.rb +0 -106
  83. data/test/ioextras/fake_io_test.rb +0 -18
  84. data/test/local_entry_test.rb +0 -154
  85. data/test/output_stream_test.rb +0 -128
  86. data/test/pass_thru_compressor_test.rb +0 -30
  87. data/test/pass_thru_decompressor_test.rb +0 -14
  88. data/test/path_traversal_test.rb +0 -141
  89. data/test/samples/example_recursive_test.rb +0 -37
  90. data/test/settings_test.rb +0 -95
  91. data/test/test_helper.rb +0 -234
  92. data/test/unicode_file_names_and_comments_test.rb +0 -62
  93. data/test/zip64_full_test.rb +0 -51
  94. data/test/zip64_support_test.rb +0 -14
@@ -4,8 +4,10 @@ module Zip
4
4
  class DestinationFileExistsError < Error; end
5
5
  class CompressionMethodError < Error; end
6
6
  class EntryNameError < Error; end
7
+ class EntrySizeError < Error; end
7
8
  class InternalError < Error; end
8
9
  class GPFBit3Error < Error; end
10
+ class DecompressionError < Error; end
9
11
 
10
12
  # Backwards compatibility with v1 (delete in v2)
11
13
  ZipError = Error
@@ -16,7 +16,7 @@ module Zip
16
16
  # If nil, start with empty.
17
17
  return false
18
18
  elsif binstr[0, 2] != self.class.const_get(:HEADER_ID)
19
- $stderr.puts 'Warning: weired extra feild header ID. skip parsing'
19
+ warn 'WARNING: weird extra field header ID. Skip parsing it.'
20
20
  return false
21
21
  end
22
22
  [binstr[2, 2].unpack('v')[0], binstr[4..-1]]
@@ -4,24 +4,51 @@ module Zip
4
4
  HEADER_ID = 'UT'
5
5
  register_map
6
6
 
7
+ ATIME_MASK = 0b010
8
+ CTIME_MASK = 0b100
9
+ MTIME_MASK = 0b001
10
+
7
11
  def initialize(binstr = nil)
8
12
  @ctime = nil
9
13
  @mtime = nil
10
14
  @atime = nil
11
- @flag = nil
12
- binstr && merge(binstr)
15
+ @flag = 0
16
+
17
+ merge(binstr) unless binstr.nil?
18
+ end
19
+
20
+ attr_reader :atime, :ctime, :mtime, :flag
21
+
22
+ def atime=(time)
23
+ @flag = time.nil? ? @flag & ~ATIME_MASK : @flag | ATIME_MASK
24
+ @atime = time
13
25
  end
14
26
 
15
- attr_accessor :atime, :ctime, :mtime, :flag
27
+ def ctime=(time)
28
+ @flag = time.nil? ? @flag & ~CTIME_MASK : @flag | CTIME_MASK
29
+ @ctime = time
30
+ end
31
+
32
+ def mtime=(time)
33
+ @flag = time.nil? ? @flag & ~MTIME_MASK : @flag | MTIME_MASK
34
+ @mtime = time
35
+ end
16
36
 
17
37
  def merge(binstr)
18
38
  return if binstr.empty?
39
+
19
40
  size, content = initial_parse(binstr)
20
- size || return
21
- @flag, mtime, atime, ctime = content.unpack('CVVV')
22
- mtime && @mtime ||= ::Zip::DOSTime.at(mtime)
23
- atime && @atime ||= ::Zip::DOSTime.at(atime)
24
- ctime && @ctime ||= ::Zip::DOSTime.at(ctime)
41
+ return if !size || size <= 0
42
+
43
+ @flag, *times = content.unpack('Cl<l<l<')
44
+
45
+ # Parse the timestamps, in order, based on which flags are set.
46
+ return if times[0].nil?
47
+ @mtime ||= ::Zip::DOSTime.at(times.shift) unless @flag & MTIME_MASK == 0
48
+ return if times[0].nil?
49
+ @atime ||= ::Zip::DOSTime.at(times.shift) unless @flag & ATIME_MASK == 0
50
+ return if times[0].nil?
51
+ @ctime ||= ::Zip::DOSTime.at(times.shift) unless @flag & CTIME_MASK == 0
25
52
  end
26
53
 
27
54
  def ==(other)
@@ -32,15 +59,15 @@ module Zip
32
59
 
33
60
  def pack_for_local
34
61
  s = [@flag].pack('C')
35
- @flag & 1 != 0 && s << [@mtime.to_i].pack('V')
36
- @flag & 2 != 0 && s << [@atime.to_i].pack('V')
37
- @flag & 4 != 0 && s << [@ctime.to_i].pack('V')
62
+ s << [@mtime.to_i].pack('l<') unless @flag & MTIME_MASK == 0
63
+ s << [@atime.to_i].pack('l<') unless @flag & ATIME_MASK == 0
64
+ s << [@ctime.to_i].pack('l<') unless @flag & CTIME_MASK == 0
38
65
  s
39
66
  end
40
67
 
41
68
  def pack_for_c_dir
42
69
  s = [@flag].pack('C')
43
- @flag & 1 == 1 && s << [@mtime.to_i].pack('V')
70
+ s << [@mtime.to_i].pack('l<') unless @flag & MTIME_MASK == 0
44
71
  s
45
72
  end
46
73
  end
@@ -49,52 +49,76 @@ module Zip
49
49
  MAX_SEGMENT_SIZE = 3_221_225_472
50
50
  MIN_SEGMENT_SIZE = 65_536
51
51
  DATA_BUFFER_SIZE = 8192
52
- IO_METHODS = [:tell, :seek, :read, :close]
52
+ IO_METHODS = [:tell, :seek, :read, :eof, :close]
53
+
54
+ DEFAULT_OPTIONS = {
55
+ restore_ownership: false,
56
+ restore_permissions: false,
57
+ restore_times: false
58
+ }.freeze
53
59
 
54
60
  attr_reader :name
55
61
 
56
- # default -> false
62
+ # default -> false.
57
63
  attr_accessor :restore_ownership
58
- # default -> false
64
+
65
+ # default -> false, but will be set to true in a future version.
59
66
  attr_accessor :restore_permissions
60
- # default -> true
67
+
68
+ # default -> false, but will be set to true in a future version.
61
69
  attr_accessor :restore_times
70
+
62
71
  # Returns the zip files comment, if it has one
63
72
  attr_accessor :comment
64
73
 
65
74
  # Opens a zip archive. Pass true as the second parameter to create
66
75
  # a new archive if it doesn't exist already.
67
- def initialize(file_name, create = false, buffer = false, options = {})
76
+ def initialize(path_or_io, create = false, buffer = false, options = {})
68
77
  super()
69
- @name = file_name
78
+ options = DEFAULT_OPTIONS.merge(options)
79
+ @name = path_or_io.respond_to?(:path) ? path_or_io.path : path_or_io
70
80
  @comment = ''
71
81
  @create = create ? true : false # allow any truthy value to mean true
72
- if !buffer && ::File.size?(file_name)
82
+
83
+ if ::File.size?(@name.to_s)
84
+ # There is a file, which exists, that is associated with this zip.
73
85
  @create = false
74
- @file_permissions = ::File.stat(file_name).mode
75
- ::File.open(name, 'rb') do |f|
76
- read_from_stream(f)
86
+ @file_permissions = ::File.stat(@name).mode
87
+
88
+ if buffer
89
+ read_from_stream(path_or_io)
90
+ else
91
+ ::File.open(@name, 'rb') do |f|
92
+ read_from_stream(f)
93
+ end
77
94
  end
95
+ elsif buffer && path_or_io.size > 0
96
+ # This zip is probably a non-empty StringIO.
97
+ read_from_stream(path_or_io)
78
98
  elsif @create
99
+ # This zip is completely new/empty and is to be created.
79
100
  @entry_set = EntrySet.new
80
- elsif ::File.zero?(file_name)
81
- raise Error, "File #{file_name} has zero size. Did you mean to pass the create flag?"
101
+ elsif ::File.zero?(@name)
102
+ # A file exists, but it is empty.
103
+ raise Error, "File #{@name} has zero size. Did you mean to pass the create flag?"
82
104
  else
83
- raise Error, "File #{file_name} not found"
105
+ # Everything is wrong.
106
+ raise Error, "File #{@name} not found"
84
107
  end
108
+
85
109
  @stored_entries = @entry_set.dup
86
110
  @stored_comment = @comment
87
- @restore_ownership = options[:restore_ownership] || false
88
- @restore_permissions = options[:restore_permissions] || true
89
- @restore_times = options[:restore_times] || true
111
+ @restore_ownership = options[:restore_ownership]
112
+ @restore_permissions = options[:restore_permissions]
113
+ @restore_times = options[:restore_times]
90
114
  end
91
115
 
92
116
  class << self
93
- # Same as #new. If a block is passed the ZipFile object is passed
94
- # to the block and is automatically closed afterwards just as with
95
- # ruby's builtin File.open method.
96
- def open(file_name, create = false)
97
- zf = ::Zip::File.new(file_name, create)
117
+ # Similar to ::new. If a block is passed the Zip::File object is passed
118
+ # to the block and is automatically closed afterwards, just as with
119
+ # ruby's builtin File::open method.
120
+ def open(file_name, create = false, options = {})
121
+ zf = ::Zip::File.new(file_name, create, false, options)
98
122
  return zf unless block_given?
99
123
  begin
100
124
  yield zf
@@ -119,17 +143,16 @@ module Zip
119
143
  unless IO_METHODS.map { |method| io.respond_to?(method) }.all? || io.is_a?(String)
120
144
  raise "Zip::File.open_buffer expects a String or IO-like argument (responds to #{IO_METHODS.join(', ')}). Found: #{io.class}"
121
145
  end
122
- if io.is_a?(::String)
123
- require 'stringio'
124
- io = ::StringIO.new(io)
125
- elsif io.respond_to?(:binmode)
126
- # https://github.com/rubyzip/rubyzip/issues/119
127
- io.binmode
128
- end
146
+
147
+ io = ::StringIO.new(io) if io.is_a?(::String)
148
+
149
+ # https://github.com/rubyzip/rubyzip/issues/119
150
+ io.binmode if io.respond_to?(:binmode)
151
+
129
152
  zf = ::Zip::File.new(io, true, true, options)
130
- zf.read_from_stream(io)
131
153
  return zf unless block_given?
132
154
  yield zf
155
+
133
156
  begin
134
157
  zf.write_buffer(io)
135
158
  rescue IOError => e
@@ -274,6 +297,13 @@ module Zip
274
297
  @entry_set << new_entry
275
298
  end
276
299
 
300
+ # Convenience method for adding the contents of a file to the archive
301
+ # in Stored format (uncompressed)
302
+ def add_stored(entry, src_path, &continue_on_exists_proc)
303
+ entry = ::Zip::Entry.new(@name, entry.to_s, nil, nil, nil, nil, ::Zip::Entry::STORED)
304
+ add(entry, src_path, &continue_on_exists_proc)
305
+ end
306
+
277
307
  # Removes the specified entry.
278
308
  def remove(entry)
279
309
  @entry_set.delete(get_entry(entry))
@@ -346,7 +376,13 @@ module Zip
346
376
  # Searches for entry with the specified name. Returns nil if
347
377
  # no entry is found. See also get_entry
348
378
  def find_entry(entry_name)
349
- @entry_set.find_entry(entry_name)
379
+ selected_entry = @entry_set.find_entry(entry_name)
380
+ return if selected_entry.nil?
381
+
382
+ selected_entry.restore_ownership = @restore_ownership
383
+ selected_entry.restore_permissions = @restore_permissions
384
+ selected_entry.restore_times = @restore_times
385
+ selected_entry
350
386
  end
351
387
 
352
388
  # Searches for entries given a glob
@@ -358,10 +394,8 @@ module Zip
358
394
  # if no entry is found.
359
395
  def get_entry(entry)
360
396
  selected_entry = find_entry(entry)
361
- raise Errno::ENOENT, entry unless selected_entry
362
- selected_entry.restore_ownership = @restore_ownership
363
- selected_entry.restore_permissions = @restore_permissions
364
- selected_entry.restore_times = @restore_times
397
+ raise Errno::ENOENT, entry if selected_entry.nil?
398
+
365
399
  selected_entry
366
400
  end
367
401
 
@@ -1,64 +1,50 @@
1
1
  module Zip
2
2
  class Inflater < Decompressor #:nodoc:all
3
- def initialize(input_stream, decrypter = NullDecrypter.new)
4
- super(input_stream)
5
- @zlib_inflater = ::Zlib::Inflate.new(-Zlib::MAX_WBITS)
6
- @output_buffer = ''.dup
7
- @has_returned_empty_string = false
8
- @decrypter = decrypter
9
- end
3
+ def initialize(*args)
4
+ super
10
5
 
11
- def sysread(number_of_bytes = nil, buf = '')
12
- readEverything = number_of_bytes.nil?
13
- while readEverything || @output_buffer.bytesize < number_of_bytes
14
- break if internal_input_finished?
15
- @output_buffer << internal_produce_input(buf)
16
- end
17
- return value_when_finished if @output_buffer.bytesize == 0 && input_finished?
18
- end_index = number_of_bytes.nil? ? @output_buffer.bytesize : number_of_bytes
19
- @output_buffer.slice!(0...end_index)
6
+ @buffer = ''.dup
7
+ @zlib_inflater = ::Zlib::Inflate.new(-Zlib::MAX_WBITS)
20
8
  end
21
9
 
22
- def produce_input
23
- if @output_buffer.empty?
24
- internal_produce_input
25
- else
26
- @output_buffer.slice!(0...(@output_buffer.length))
10
+ def read(length = nil, outbuf = '')
11
+ return ((length.nil? || length.zero?) ? "" : nil) if eof
12
+
13
+ while length.nil? || (@buffer.bytesize < length)
14
+ break if input_finished?
15
+ @buffer << produce_input
27
16
  end
17
+
18
+ outbuf.replace(@buffer.slice!(0...(length || @buffer.bytesize)))
28
19
  end
29
20
 
30
- # to be used with produce_input, not read (as read may still have more data cached)
31
- # is data cached anywhere other than @outputBuffer? the comment above may be wrong
32
- def input_finished?
33
- @output_buffer.empty? && internal_input_finished?
21
+ def eof
22
+ @buffer.empty? && input_finished?
34
23
  end
35
24
 
36
- alias :eof input_finished?
37
- alias :eof? input_finished?
25
+ alias_method :eof?, :eof
38
26
 
39
27
  private
40
28
 
41
- def internal_produce_input(buf = '')
29
+ def produce_input
42
30
  retried = 0
43
31
  begin
44
- @zlib_inflater.inflate(@decrypter.decrypt(@input_stream.read(Decompressor::CHUNK_SIZE, buf)))
32
+ @zlib_inflater.inflate(input_stream.read(Decompressor::CHUNK_SIZE))
45
33
  rescue Zlib::BufError
46
34
  raise if retried >= 5 # how many times should we retry?
47
35
  retried += 1
48
36
  retry
49
37
  end
38
+ rescue Zlib::Error
39
+ raise(::Zip::DecompressionError, 'zlib error while inflating')
50
40
  end
51
41
 
52
- def internal_input_finished?
42
+ def input_finished?
53
43
  @zlib_inflater.finished?
54
44
  end
55
-
56
- def value_when_finished # mimic behaviour of ruby File object.
57
- return if @has_returned_empty_string
58
- @has_returned_empty_string = true
59
- ''
60
- end
61
45
  end
46
+
47
+ ::Zip::Decompressor.register(::Zip::COMPRESSION_METHOD_DEFLATE, ::Zip::Inflater)
62
48
  end
63
49
 
64
50
  # Copyright (C) 2002, 2003 Thomas Sondergaard
@@ -39,6 +39,8 @@ module Zip
39
39
  # class.
40
40
 
41
41
  class InputStream
42
+ CHUNK_SIZE = 32_768
43
+
42
44
  include ::Zip::IOExtras::AbstractInputStream
43
45
 
44
46
  # Opens the indicated zip file. An exception is thrown
@@ -78,16 +80,10 @@ module Zip
78
80
  end
79
81
 
80
82
  # Modeled after IO.sysread
81
- def sysread(number_of_bytes = nil, buf = nil)
82
- @decompressor.sysread(number_of_bytes, buf)
83
- end
84
-
85
- def eof
86
- @output_buffer.empty? && @decompressor.eof
83
+ def sysread(length = nil, outbuf = '')
84
+ @decompressor.read(length, outbuf)
87
85
  end
88
86
 
89
- alias :eof? eof
90
-
91
87
  class << self
92
88
  # Same as #initialize but if a block is passed the opened
93
89
  # stream is passed to the block and closed when the block
@@ -103,7 +99,7 @@ module Zip
103
99
  end
104
100
 
105
101
  def open_buffer(filename_or_io, offset = 0)
106
- puts 'open_buffer is deprecated!!! Use open instead!'
102
+ warn 'open_buffer is deprecated!!! Use open instead!'
107
103
  open(filename_or_io, offset)
108
104
  end
109
105
  end
@@ -124,46 +120,54 @@ module Zip
124
120
 
125
121
  def open_entry
126
122
  @current_entry = ::Zip::Entry.read_local_entry(@archive_io)
127
- if @current_entry && @current_entry.gp_flags & 1 == 1 && @decrypter.is_a?(NullEncrypter)
123
+ if @current_entry && @current_entry.encrypted? && @decrypter.is_a?(NullEncrypter)
128
124
  raise Error, 'password required to decode zip file'
129
125
  end
130
- if @current_entry && @current_entry.gp_flags & 8 == 8 && @current_entry.crc == 0 \
126
+ if @current_entry && @current_entry.incomplete? && @current_entry.crc == 0 \
131
127
  && @current_entry.compressed_size == 0 \
132
128
  && @current_entry.size == 0 && !@complete_entry
133
129
  raise GPFBit3Error,
134
130
  'General purpose flag Bit 3 is set so not possible to get proper info from local header.' \
135
131
  'Please use ::Zip::File instead of ::Zip::InputStream'
136
132
  end
133
+ @decrypted_io = get_decrypted_io
137
134
  @decompressor = get_decompressor
138
135
  flush
139
136
  @current_entry
140
137
  end
141
138
 
139
+ def get_decrypted_io
140
+ header = @archive_io.read(@decrypter.header_bytesize)
141
+ @decrypter.reset!(header)
142
+
143
+ ::Zip::DecryptedIo.new(@archive_io, @decrypter)
144
+ end
145
+
142
146
  def get_decompressor
143
- if @current_entry.nil?
144
- ::Zip::NullDecompressor
145
- elsif @current_entry.compression_method == ::Zip::Entry::STORED
146
- if @current_entry.gp_flags & 8 == 8 && @current_entry.crc == 0 && @current_entry.size == 0 && @complete_entry
147
- ::Zip::PassThruDecompressor.new(@archive_io, @complete_entry.size)
147
+ return ::Zip::NullDecompressor if @current_entry.nil?
148
+
149
+ decompressed_size =
150
+ if @current_entry.incomplete? && @current_entry.crc == 0 && @current_entry.size == 0 && @complete_entry
151
+ @complete_entry.size
148
152
  else
149
- ::Zip::PassThruDecompressor.new(@archive_io, @current_entry.size)
153
+ @current_entry.size
150
154
  end
151
- elsif @current_entry.compression_method == ::Zip::Entry::DEFLATED
152
- header = @archive_io.read(@decrypter.header_bytesize)
153
- @decrypter.reset!(header)
154
- ::Zip::Inflater.new(@archive_io, @decrypter)
155
- else
155
+
156
+ decompressor_class = ::Zip::Decompressor.find_by_compression_method(@current_entry.compression_method)
157
+ if decompressor_class.nil?
156
158
  raise ::Zip::CompressionMethodError,
157
159
  "Unsupported compression method #{@current_entry.compression_method}"
158
160
  end
161
+
162
+ decompressor_class.new(@decrypted_io, decompressed_size)
159
163
  end
160
164
 
161
165
  def produce_input
162
- @decompressor.produce_input
166
+ @decompressor.read(CHUNK_SIZE)
163
167
  end
164
168
 
165
169
  def input_finished?
166
- @decompressor.input_finished?
170
+ @decompressor.eof
167
171
  end
168
172
  end
169
173
  end