rubyzip 2.3.2 → 3.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Changelog.md +485 -0
- data/LICENSE.md +24 -0
- data/README.md +192 -44
- data/Rakefile +15 -13
- data/lib/zip/central_directory.rb +179 -125
- data/lib/zip/compressor.rb +3 -1
- data/lib/zip/constants.rb +29 -21
- data/lib/zip/crypto/aes_encryption.rb +120 -0
- data/lib/zip/crypto/decrypted_io.rb +20 -14
- data/lib/zip/crypto/encryption.rb +4 -2
- data/lib/zip/crypto/null_encryption.rb +5 -13
- data/lib/zip/crypto/traditional_encryption.rb +10 -6
- data/lib/zip/decompressor.rb +4 -3
- data/lib/zip/deflater.rb +12 -8
- data/lib/zip/dirtyable.rb +32 -0
- data/lib/zip/dos_time.rb +53 -6
- data/lib/zip/entry.rb +404 -238
- data/lib/zip/entry_set.rb +11 -9
- data/lib/zip/errors.rb +136 -16
- data/lib/zip/extra_field/aes.rb +50 -0
- data/lib/zip/extra_field/generic.rb +10 -11
- data/lib/zip/extra_field/ntfs.rb +6 -4
- data/lib/zip/extra_field/old_unix.rb +3 -1
- data/lib/zip/extra_field/universal_time.rb +3 -1
- data/lib/zip/extra_field/unix.rb +5 -3
- data/lib/zip/extra_field/unknown.rb +35 -0
- data/lib/zip/extra_field/zip64.rb +19 -5
- data/lib/zip/extra_field.rb +25 -23
- data/lib/zip/file.rb +185 -226
- data/lib/zip/file_split.rb +91 -0
- data/lib/zip/filesystem/dir.rb +86 -0
- data/lib/zip/filesystem/directory_iterator.rb +48 -0
- data/lib/zip/filesystem/file.rb +263 -0
- data/lib/zip/filesystem/file_stat.rb +110 -0
- data/lib/zip/filesystem/zip_file_name_mapper.rb +81 -0
- data/lib/zip/filesystem.rb +27 -596
- data/lib/zip/inflater.rb +10 -7
- data/lib/zip/input_stream.rb +76 -44
- data/lib/zip/ioextras/abstract_input_stream.rb +18 -12
- data/lib/zip/ioextras/abstract_output_stream.rb +13 -3
- data/lib/zip/ioextras.rb +7 -7
- data/lib/zip/null_compressor.rb +3 -1
- data/lib/zip/null_decompressor.rb +6 -3
- data/lib/zip/null_input_stream.rb +3 -1
- data/lib/zip/output_stream.rb +58 -48
- data/lib/zip/pass_thru_compressor.rb +3 -1
- data/lib/zip/pass_thru_decompressor.rb +8 -5
- data/lib/zip/streamable_directory.rb +3 -1
- data/lib/zip/streamable_stream.rb +4 -1
- data/lib/zip/version.rb +4 -1
- data/lib/zip.rb +25 -3
- data/rubyzip.gemspec +39 -0
- data/samples/example.rb +8 -3
- data/samples/example_filesystem.rb +3 -2
- data/samples/example_recursive.rb +3 -1
- data/samples/gtk_ruby_zip.rb +5 -3
- data/samples/qtzip.rb +7 -6
- data/samples/write_simple.rb +2 -1
- data/samples/zipfind.rb +1 -0
- metadata +81 -49
- data/TODO +0 -15
- data/lib/zip/extra_field/zip64_placeholder.rb +0 -15
data/lib/zip/input_stream.rb
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
##
|
|
1
4
|
module Zip
|
|
2
5
|
# InputStream is the basic class for reading zip entries in a
|
|
3
6
|
# zip file. It is possible to create a InputStream object directly,
|
|
@@ -37,9 +40,8 @@ module Zip
|
|
|
37
40
|
#
|
|
38
41
|
# java.util.zip.ZipInputStream is the original inspiration for this
|
|
39
42
|
# class.
|
|
40
|
-
|
|
41
43
|
class InputStream
|
|
42
|
-
CHUNK_SIZE = 32_768
|
|
44
|
+
CHUNK_SIZE = 32_768 # :nodoc:
|
|
43
45
|
|
|
44
46
|
include ::Zip::IOExtras::AbstractInputStream
|
|
45
47
|
|
|
@@ -49,28 +51,35 @@ module Zip
|
|
|
49
51
|
#
|
|
50
52
|
# @param context [String||IO||StringIO] file path or IO/StringIO object
|
|
51
53
|
# @param offset [Integer] offset in the IO/StringIO
|
|
52
|
-
def initialize(context, offset
|
|
54
|
+
def initialize(context, offset: 0, decrypter: nil)
|
|
53
55
|
super()
|
|
54
|
-
@archive_io
|
|
55
|
-
@decompressor
|
|
56
|
-
@decrypter
|
|
56
|
+
@archive_io = get_io(context, offset)
|
|
57
|
+
@decompressor = ::Zip::NullDecompressor
|
|
58
|
+
@decrypter = decrypter
|
|
57
59
|
@current_entry = nil
|
|
60
|
+
@complete_entry = nil
|
|
58
61
|
end
|
|
59
62
|
|
|
63
|
+
# Close this InputStream. All further IO will raise an IOError.
|
|
60
64
|
def close
|
|
61
65
|
@archive_io.close
|
|
62
66
|
end
|
|
63
67
|
|
|
64
|
-
# Returns
|
|
65
|
-
# method on a newly created
|
|
66
|
-
# the first entry in the archive.
|
|
67
|
-
# no more entries.
|
|
68
|
+
# Returns an Entry object and positions the stream at the beginning of
|
|
69
|
+
# the entry data. It is necessary to call this method on a newly created
|
|
70
|
+
# InputStream before reading from the first entry in the archive.
|
|
71
|
+
# Returns nil when there are no more entries.
|
|
68
72
|
def get_next_entry
|
|
69
|
-
@
|
|
73
|
+
unless @current_entry.nil?
|
|
74
|
+
raise StreamingError, @current_entry if @current_entry.incomplete?
|
|
75
|
+
|
|
76
|
+
@archive_io.seek(@current_entry.next_header_offset, IO::SEEK_SET)
|
|
77
|
+
end
|
|
78
|
+
|
|
70
79
|
open_entry
|
|
71
80
|
end
|
|
72
81
|
|
|
73
|
-
# Rewinds the stream to the beginning of the current entry
|
|
82
|
+
# Rewinds the stream to the beginning of the current entry.
|
|
74
83
|
def rewind
|
|
75
84
|
return if @current_entry.nil?
|
|
76
85
|
|
|
@@ -81,16 +90,23 @@ module Zip
|
|
|
81
90
|
end
|
|
82
91
|
|
|
83
92
|
# Modeled after IO.sysread
|
|
84
|
-
def sysread(length = nil, outbuf = '')
|
|
93
|
+
def sysread(length = nil, outbuf = +'')
|
|
85
94
|
@decompressor.read(length, outbuf)
|
|
86
95
|
end
|
|
87
96
|
|
|
97
|
+
# Returns the size of the current entry, or `nil` if there isn't one.
|
|
98
|
+
def size
|
|
99
|
+
return if @current_entry.nil?
|
|
100
|
+
|
|
101
|
+
@current_entry.size
|
|
102
|
+
end
|
|
103
|
+
|
|
88
104
|
class << self
|
|
89
105
|
# Same as #initialize but if a block is passed the opened
|
|
90
106
|
# stream is passed to the block and closed when the block
|
|
91
107
|
# returns.
|
|
92
|
-
def open(filename_or_io, offset
|
|
93
|
-
zio = new(filename_or_io, offset, decrypter)
|
|
108
|
+
def open(filename_or_io, offset: 0, decrypter: nil)
|
|
109
|
+
zio = new(filename_or_io, offset: offset, decrypter: decrypter)
|
|
94
110
|
return zio unless block_given?
|
|
95
111
|
|
|
96
112
|
begin
|
|
@@ -99,16 +115,11 @@ module Zip
|
|
|
99
115
|
zio.close if zio
|
|
100
116
|
end
|
|
101
117
|
end
|
|
102
|
-
|
|
103
|
-
def open_buffer(filename_or_io, offset = 0)
|
|
104
|
-
warn 'open_buffer is deprecated!!! Use open instead!'
|
|
105
|
-
::Zip::InputStream.open(filename_or_io, offset)
|
|
106
|
-
end
|
|
107
118
|
end
|
|
108
119
|
|
|
109
120
|
protected
|
|
110
121
|
|
|
111
|
-
def get_io(io_or_file, offset = 0)
|
|
122
|
+
def get_io(io_or_file, offset = 0) # :nodoc:
|
|
112
123
|
if io_or_file.respond_to?(:seek)
|
|
113
124
|
io = io_or_file.dup
|
|
114
125
|
io.seek(offset, ::IO::SEEK_SET)
|
|
@@ -120,57 +131,78 @@ module Zip
|
|
|
120
131
|
end
|
|
121
132
|
end
|
|
122
133
|
|
|
123
|
-
def open_entry
|
|
134
|
+
def open_entry # :nodoc:
|
|
124
135
|
@current_entry = ::Zip::Entry.read_local_entry(@archive_io)
|
|
125
|
-
if @current_entry
|
|
126
|
-
raise Error, 'password required to decode zip file'
|
|
127
|
-
end
|
|
136
|
+
return if @current_entry.nil?
|
|
128
137
|
|
|
129
|
-
if @current_entry
|
|
130
|
-
|
|
131
|
-
&& @current_entry.size == 0 && !@complete_entry
|
|
132
|
-
raise GPFBit3Error,
|
|
133
|
-
'General purpose flag Bit 3 is set so not possible to get proper info from local header.' \
|
|
134
|
-
'Please use ::Zip::File instead of ::Zip::InputStream'
|
|
138
|
+
if @current_entry.incomplete? && @current_entry.compressed_size == 0 && !@complete_entry
|
|
139
|
+
raise StreamingError, @current_entry
|
|
135
140
|
end
|
|
136
|
-
|
|
137
|
-
@decompressor =
|
|
141
|
+
|
|
142
|
+
@decompressor = assemble_io
|
|
138
143
|
flush
|
|
139
144
|
@current_entry
|
|
140
145
|
end
|
|
141
146
|
|
|
142
|
-
def
|
|
147
|
+
def assemble_io # :nodoc:
|
|
148
|
+
io = if @current_entry.encrypted?
|
|
149
|
+
raise Error, 'A password is required to decode this zip file.' if @decrypter.nil?
|
|
150
|
+
|
|
151
|
+
get_decrypted_io
|
|
152
|
+
else
|
|
153
|
+
@archive_io
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
get_decompressor(io)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def get_decrypted_io # :nodoc:
|
|
143
160
|
header = @archive_io.read(@decrypter.header_bytesize)
|
|
144
161
|
@decrypter.reset!(header)
|
|
145
162
|
|
|
146
|
-
|
|
163
|
+
compressed_size =
|
|
164
|
+
if @current_entry.incomplete? && @current_entry.crc == 0 &&
|
|
165
|
+
@current_entry.compressed_size == 0 && @complete_entry
|
|
166
|
+
@complete_entry.compressed_size
|
|
167
|
+
else
|
|
168
|
+
@current_entry.compressed_size
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
if @decrypter.kind_of?(::Zip::AESDecrypter)
|
|
172
|
+
compressed_size -= @decrypter.header_bytesize
|
|
173
|
+
compressed_size -= ::Zip::AESEncryption::AUTHENTICATION_CODE_LENGTH
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
::Zip::DecryptedIo.new(@archive_io, @decrypter, compressed_size)
|
|
147
177
|
end
|
|
148
178
|
|
|
149
|
-
def get_decompressor
|
|
179
|
+
def get_decompressor(io) # :nodoc:
|
|
150
180
|
return ::Zip::NullDecompressor if @current_entry.nil?
|
|
151
181
|
|
|
152
182
|
decompressed_size =
|
|
153
|
-
if @current_entry.incomplete? && @current_entry.crc == 0 &&
|
|
183
|
+
if @current_entry.incomplete? && @current_entry.crc == 0 &&
|
|
184
|
+
@current_entry.size == 0 && @complete_entry
|
|
154
185
|
@complete_entry.size
|
|
155
186
|
else
|
|
156
187
|
@current_entry.size
|
|
157
188
|
end
|
|
158
189
|
|
|
159
|
-
decompressor_class = ::Zip::Decompressor.find_by_compression_method(
|
|
190
|
+
decompressor_class = ::Zip::Decompressor.find_by_compression_method(
|
|
191
|
+
@current_entry.compression_method
|
|
192
|
+
)
|
|
160
193
|
if decompressor_class.nil?
|
|
161
|
-
raise ::Zip::CompressionMethodError,
|
|
162
|
-
"Unsupported compression method #{@current_entry.compression_method}"
|
|
194
|
+
raise ::Zip::CompressionMethodError, @current_entry.compression_method
|
|
163
195
|
end
|
|
164
196
|
|
|
165
|
-
decompressor_class.new(
|
|
197
|
+
decompressor_class.new(io, decompressed_size)
|
|
166
198
|
end
|
|
167
199
|
|
|
168
|
-
def produce_input
|
|
200
|
+
def produce_input # :nodoc:
|
|
169
201
|
@decompressor.read(CHUNK_SIZE)
|
|
170
202
|
end
|
|
171
203
|
|
|
172
|
-
def input_finished?
|
|
173
|
-
@decompressor.eof
|
|
204
|
+
def input_finished? # :nodoc:
|
|
205
|
+
@decompressor.eof?
|
|
174
206
|
end
|
|
175
207
|
end
|
|
176
208
|
end
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Zip
|
|
2
|
-
module IOExtras
|
|
4
|
+
module IOExtras # :nodoc:
|
|
3
5
|
# Implements many of the convenience methods of IO
|
|
4
6
|
# such as gets, getc, readline and readlines
|
|
5
7
|
# depends on: input_finished?, produce_input and read
|
|
6
|
-
module AbstractInputStream
|
|
8
|
+
module AbstractInputStream # :nodoc:
|
|
7
9
|
include Enumerable
|
|
8
10
|
include FakeIO
|
|
9
11
|
|
|
@@ -11,15 +13,15 @@ module Zip
|
|
|
11
13
|
super
|
|
12
14
|
@lineno = 0
|
|
13
15
|
@pos = 0
|
|
14
|
-
@output_buffer = ''
|
|
16
|
+
@output_buffer = +''
|
|
15
17
|
end
|
|
16
18
|
|
|
17
19
|
attr_accessor :lineno
|
|
18
20
|
attr_reader :pos
|
|
19
21
|
|
|
20
|
-
def read(number_of_bytes = nil, buf = '')
|
|
22
|
+
def read(number_of_bytes = nil, buf = +'')
|
|
21
23
|
tbuf = if @output_buffer.bytesize > 0
|
|
22
|
-
if number_of_bytes <= @output_buffer.bytesize
|
|
24
|
+
if number_of_bytes && number_of_bytes <= @output_buffer.bytesize
|
|
23
25
|
@output_buffer.slice!(0, number_of_bytes)
|
|
24
26
|
else
|
|
25
27
|
number_of_bytes -= @output_buffer.bytesize if number_of_bytes
|
|
@@ -34,7 +36,7 @@ module Zip
|
|
|
34
36
|
end
|
|
35
37
|
|
|
36
38
|
if tbuf.nil? || tbuf.empty?
|
|
37
|
-
return nil if number_of_bytes
|
|
39
|
+
return nil if number_of_bytes&.positive?
|
|
38
40
|
|
|
39
41
|
return ''
|
|
40
42
|
end
|
|
@@ -74,15 +76,18 @@ module Zip
|
|
|
74
76
|
a_sep_string = "#{$INPUT_RECORD_SEPARATOR}#{$INPUT_RECORD_SEPARATOR}" if a_sep_string.empty?
|
|
75
77
|
|
|
76
78
|
buffer_index = 0
|
|
77
|
-
over_limit =
|
|
79
|
+
over_limit = number_of_bytes && @output_buffer.bytesize >= number_of_bytes
|
|
78
80
|
while (match_index = @output_buffer.index(a_sep_string, buffer_index)).nil? && !over_limit
|
|
79
81
|
buffer_index = [buffer_index, @output_buffer.bytesize - a_sep_string.bytesize].max
|
|
80
82
|
return @output_buffer.empty? ? nil : flush if input_finished?
|
|
81
83
|
|
|
82
84
|
@output_buffer << produce_input
|
|
83
|
-
over_limit =
|
|
85
|
+
over_limit = number_of_bytes && @output_buffer.bytesize >= number_of_bytes
|
|
84
86
|
end
|
|
85
|
-
sep_index = [
|
|
87
|
+
sep_index = [
|
|
88
|
+
match_index + a_sep_string.bytesize,
|
|
89
|
+
number_of_bytes || @output_buffer.bytesize
|
|
90
|
+
].min
|
|
86
91
|
@pos += sep_index
|
|
87
92
|
@output_buffer.slice!(0...sep_index)
|
|
88
93
|
end
|
|
@@ -93,7 +98,7 @@ module Zip
|
|
|
93
98
|
|
|
94
99
|
def flush
|
|
95
100
|
ret_val = @output_buffer
|
|
96
|
-
@output_buffer = ''
|
|
101
|
+
@output_buffer = +''
|
|
97
102
|
ret_val
|
|
98
103
|
end
|
|
99
104
|
|
|
@@ -112,11 +117,12 @@ module Zip
|
|
|
112
117
|
|
|
113
118
|
alias each each_line
|
|
114
119
|
|
|
115
|
-
def eof
|
|
120
|
+
def eof?
|
|
116
121
|
@output_buffer.empty? && input_finished?
|
|
117
122
|
end
|
|
118
123
|
|
|
119
|
-
|
|
124
|
+
# Alias for compatibility. Remove for version 4.
|
|
125
|
+
alias eof eof?
|
|
120
126
|
end
|
|
121
127
|
end
|
|
122
128
|
end
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Zip
|
|
2
|
-
module IOExtras
|
|
4
|
+
module IOExtras # :nodoc:
|
|
3
5
|
# Implements many of the output convenience methods of IO.
|
|
4
6
|
# relies on <<
|
|
5
|
-
module AbstractOutputStream
|
|
7
|
+
module AbstractOutputStream # :nodoc:
|
|
6
8
|
include FakeIO
|
|
7
9
|
|
|
8
10
|
def write(data)
|
|
@@ -11,11 +13,19 @@ module Zip
|
|
|
11
13
|
end
|
|
12
14
|
|
|
13
15
|
def print(*params)
|
|
14
|
-
self << params.join
|
|
16
|
+
self << params.join
|
|
17
|
+
|
|
18
|
+
# Deflate doesn't like `nil`s or empty strings!
|
|
19
|
+
unless $OUTPUT_RECORD_SEPARATOR.nil? || $OUTPUT_RECORD_SEPARATOR.empty?
|
|
20
|
+
self << $OUTPUT_RECORD_SEPARATOR
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
nil
|
|
15
24
|
end
|
|
16
25
|
|
|
17
26
|
def printf(a_format_string, *params)
|
|
18
27
|
self << format(a_format_string, *params)
|
|
28
|
+
nil
|
|
19
29
|
end
|
|
20
30
|
|
|
21
31
|
def putc(an_object)
|
data/lib/zip/ioextras.rb
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Zip
|
|
2
|
-
module IOExtras
|
|
4
|
+
module IOExtras # :nodoc:
|
|
3
5
|
CHUNK_SIZE = 131_072
|
|
4
6
|
|
|
5
|
-
RANGE_ALL = 0..-1
|
|
6
|
-
|
|
7
7
|
class << self
|
|
8
8
|
def copy_stream(ostream, istream)
|
|
9
|
-
ostream.write(istream.read(CHUNK_SIZE, '')) until istream.eof?
|
|
9
|
+
ostream.write(istream.read(CHUNK_SIZE, +'')) until istream.eof?
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def copy_stream_n(ostream, istream, nbytes)
|
|
13
13
|
toread = nbytes
|
|
14
14
|
while toread > 0 && !istream.eof?
|
|
15
|
-
tr = toread
|
|
16
|
-
ostream.write(istream.read(tr, ''))
|
|
15
|
+
tr = [toread, CHUNK_SIZE].min
|
|
16
|
+
ostream.write(istream.read(tr, +''))
|
|
17
17
|
toread -= tr
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
# Implements kind_of? in order to pretend to be an IO object
|
|
23
|
-
module FakeIO
|
|
23
|
+
module FakeIO # :nodoc:
|
|
24
24
|
def kind_of?(object)
|
|
25
25
|
object == IO || super
|
|
26
26
|
end
|
data/lib/zip/null_compressor.rb
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Zip
|
|
2
|
-
module NullDecompressor
|
|
4
|
+
module NullDecompressor # :nodoc:all
|
|
3
5
|
module_function
|
|
4
6
|
|
|
5
7
|
def read(_length = nil, _outbuf = nil)
|
|
6
8
|
nil
|
|
7
9
|
end
|
|
8
10
|
|
|
9
|
-
def eof
|
|
11
|
+
def eof?
|
|
10
12
|
true
|
|
11
13
|
end
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
# Alias for compatibility. Remove for version 4.
|
|
16
|
+
alias eof eof?
|
|
14
17
|
end
|
|
15
18
|
end
|
|
16
19
|
|
data/lib/zip/output_stream.rb
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'forwardable'
|
|
4
|
+
|
|
5
|
+
##
|
|
1
6
|
module Zip
|
|
2
7
|
# ZipOutputStream is the basic class for writing zip files. It is
|
|
3
8
|
# possible to create a ZipOutputStream object directly, passing
|
|
@@ -16,50 +21,52 @@ module Zip
|
|
|
16
21
|
#
|
|
17
22
|
# java.util.zip.ZipOutputStream is the original inspiration for this
|
|
18
23
|
# class.
|
|
19
|
-
|
|
20
24
|
class OutputStream
|
|
25
|
+
extend Forwardable
|
|
21
26
|
include ::Zip::IOExtras::AbstractOutputStream
|
|
22
27
|
|
|
23
|
-
|
|
28
|
+
def_delegators :@cdir, :comment, :comment=
|
|
24
29
|
|
|
25
30
|
# Opens the indicated zip file. If a file with that name already
|
|
26
31
|
# exists it will be overwritten.
|
|
27
|
-
def initialize(file_name, stream
|
|
32
|
+
def initialize(file_name, stream: false, encrypter: nil, suppress_extra_fields: false)
|
|
28
33
|
super()
|
|
29
34
|
@file_name = file_name
|
|
30
35
|
@output_stream = if stream
|
|
31
|
-
iostream = @file_name.dup
|
|
36
|
+
iostream = Zip::RUNNING_ON_WINDOWS ? @file_name : @file_name.dup
|
|
32
37
|
iostream.reopen(@file_name)
|
|
33
38
|
iostream.rewind
|
|
34
39
|
iostream
|
|
35
40
|
else
|
|
36
41
|
::File.new(@file_name, 'wb')
|
|
37
42
|
end
|
|
38
|
-
@
|
|
43
|
+
@cdir = ::Zip::CentralDirectory.new
|
|
39
44
|
@compressor = ::Zip::NullCompressor.instance
|
|
40
45
|
@encrypter = encrypter || ::Zip::NullEncrypter.new
|
|
46
|
+
@suppress_extra_fields = suppress_extra_fields
|
|
41
47
|
@closed = false
|
|
42
48
|
@current_entry = nil
|
|
43
|
-
@comment = nil
|
|
44
49
|
end
|
|
45
50
|
|
|
46
|
-
# Same as #initialize but if a block is passed the opened
|
|
47
|
-
# stream is passed to the block and closed when the block
|
|
48
|
-
# returns.
|
|
49
51
|
class << self
|
|
50
|
-
|
|
52
|
+
# Same as #initialize but if a block is passed the opened
|
|
53
|
+
# stream is passed to the block and closed when the block
|
|
54
|
+
# returns.
|
|
55
|
+
def open(file_name, encrypter: nil, suppress_extra_fields: false)
|
|
51
56
|
return new(file_name) unless block_given?
|
|
52
57
|
|
|
53
|
-
zos = new(file_name, false, encrypter
|
|
58
|
+
zos = new(file_name, stream: false, encrypter: encrypter,
|
|
59
|
+
suppress_extra_fields: suppress_extra_fields)
|
|
54
60
|
yield zos
|
|
55
61
|
ensure
|
|
56
62
|
zos.close if zos
|
|
57
63
|
end
|
|
58
64
|
|
|
59
65
|
# Same as #open but writes to a filestream instead
|
|
60
|
-
def write_buffer(io = ::StringIO.new
|
|
66
|
+
def write_buffer(io = ::StringIO.new, encrypter: nil, suppress_extra_fields: false)
|
|
61
67
|
io.binmode if io.respond_to?(:binmode)
|
|
62
|
-
zos = new(io, true, encrypter
|
|
68
|
+
zos = new(io, stream: true, encrypter: encrypter,
|
|
69
|
+
suppress_extra_fields: suppress_extra_fields)
|
|
63
70
|
yield zos
|
|
64
71
|
zos.close_buffer
|
|
65
72
|
end
|
|
@@ -71,7 +78,7 @@ module Zip
|
|
|
71
78
|
|
|
72
79
|
finalize_current_entry
|
|
73
80
|
update_local_headers
|
|
74
|
-
|
|
81
|
+
@cdir.write_to_stream(@output_stream, suppress_extra_fields: @suppress_extra_fields)
|
|
75
82
|
@output_stream.close
|
|
76
83
|
@closed = true
|
|
77
84
|
end
|
|
@@ -82,37 +89,41 @@ module Zip
|
|
|
82
89
|
|
|
83
90
|
finalize_current_entry
|
|
84
91
|
update_local_headers
|
|
85
|
-
|
|
92
|
+
@cdir.write_to_stream(@output_stream, suppress_extra_fields: @suppress_extra_fields)
|
|
86
93
|
@closed = true
|
|
94
|
+
@output_stream.flush
|
|
87
95
|
@output_stream
|
|
88
96
|
end
|
|
89
97
|
|
|
90
98
|
# Closes the current entry and opens a new for writing.
|
|
91
99
|
# +entry+ can be a ZipEntry object or a string.
|
|
92
|
-
def put_next_entry(
|
|
100
|
+
def put_next_entry(
|
|
101
|
+
entry_name, comment = '', extra = ExtraField.new,
|
|
102
|
+
compression_method = Entry::DEFLATED, level = Zip.default_compression
|
|
103
|
+
)
|
|
93
104
|
raise Error, 'zip stream is closed' if @closed
|
|
94
105
|
|
|
95
|
-
new_entry =
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
init_next_entry(new_entry
|
|
106
|
+
new_entry =
|
|
107
|
+
if entry_name.kind_of?(Entry) || entry_name.kind_of?(StreamableStream)
|
|
108
|
+
entry_name
|
|
109
|
+
else
|
|
110
|
+
Entry.new(
|
|
111
|
+
@file_name, entry_name.to_s, comment: comment, extra: extra,
|
|
112
|
+
compression_method: compression_method, compression_level: level
|
|
113
|
+
)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
init_next_entry(new_entry)
|
|
106
117
|
@current_entry = new_entry
|
|
107
118
|
end
|
|
108
119
|
|
|
109
|
-
def copy_raw_entry(entry)
|
|
120
|
+
def copy_raw_entry(entry) # :nodoc:
|
|
110
121
|
entry = entry.dup
|
|
111
122
|
raise Error, 'zip stream is closed' if @closed
|
|
112
123
|
raise Error, 'entry is not a ZipEntry' unless entry.kind_of?(Entry)
|
|
113
124
|
|
|
114
125
|
finalize_current_entry
|
|
115
|
-
@
|
|
126
|
+
@cdir << entry
|
|
116
127
|
src_pos = entry.local_header_offset
|
|
117
128
|
entry.write_local_entry(@output_stream)
|
|
118
129
|
@compressor = NullCompressor.instance
|
|
@@ -131,55 +142,54 @@ module Zip
|
|
|
131
142
|
return unless @current_entry
|
|
132
143
|
|
|
133
144
|
finish
|
|
134
|
-
@current_entry.compressed_size = @output_stream.tell -
|
|
135
|
-
@current_entry.local_header_offset -
|
|
145
|
+
@current_entry.compressed_size = @output_stream.tell -
|
|
146
|
+
@current_entry.local_header_offset -
|
|
136
147
|
@current_entry.calculate_local_header_size
|
|
137
148
|
@current_entry.size = @compressor.size
|
|
138
149
|
@current_entry.crc = @compressor.crc
|
|
139
|
-
@output_stream << @encrypter.data_descriptor(
|
|
150
|
+
@output_stream << @encrypter.data_descriptor(
|
|
151
|
+
@current_entry.crc,
|
|
152
|
+
@current_entry.compressed_size,
|
|
153
|
+
@current_entry.size
|
|
154
|
+
)
|
|
140
155
|
@current_entry.gp_flags |= @encrypter.gp_flags
|
|
141
156
|
@current_entry = nil
|
|
142
157
|
@compressor = ::Zip::NullCompressor.instance
|
|
143
158
|
end
|
|
144
159
|
|
|
145
|
-
def init_next_entry(entry
|
|
160
|
+
def init_next_entry(entry)
|
|
146
161
|
finalize_current_entry
|
|
147
|
-
@
|
|
148
|
-
entry.write_local_entry(@output_stream)
|
|
162
|
+
@cdir << entry
|
|
163
|
+
entry.write_local_entry(@output_stream, suppress_extra_fields: @suppress_extra_fields)
|
|
149
164
|
@encrypter.reset!
|
|
150
165
|
@output_stream << @encrypter.header(entry.mtime)
|
|
151
|
-
@compressor = get_compressor(entry
|
|
166
|
+
@compressor = get_compressor(entry)
|
|
152
167
|
end
|
|
153
168
|
|
|
154
|
-
def get_compressor(entry
|
|
169
|
+
def get_compressor(entry)
|
|
155
170
|
case entry.compression_method
|
|
156
171
|
when Entry::DEFLATED
|
|
157
|
-
::Zip::Deflater.new(@output_stream,
|
|
172
|
+
::Zip::Deflater.new(@output_stream, entry.compression_level, @encrypter)
|
|
158
173
|
when Entry::STORED
|
|
159
174
|
::Zip::PassThruCompressor.new(@output_stream)
|
|
160
175
|
else
|
|
161
|
-
raise ::Zip::CompressionMethodError,
|
|
162
|
-
"Invalid compression method: '#{entry.compression_method}'"
|
|
176
|
+
raise ::Zip::CompressionMethodError, entry.compression_method
|
|
163
177
|
end
|
|
164
178
|
end
|
|
165
179
|
|
|
166
180
|
def update_local_headers
|
|
167
181
|
pos = @output_stream.pos
|
|
168
|
-
@
|
|
182
|
+
@cdir.each do |entry|
|
|
169
183
|
@output_stream.pos = entry.local_header_offset
|
|
170
|
-
entry.write_local_entry(@output_stream,
|
|
184
|
+
entry.write_local_entry(@output_stream, suppress_extra_fields: @suppress_extra_fields,
|
|
185
|
+
rewrite: true)
|
|
171
186
|
end
|
|
172
187
|
@output_stream.pos = pos
|
|
173
188
|
end
|
|
174
189
|
|
|
175
|
-
def write_central_directory
|
|
176
|
-
cdir = CentralDirectory.new(@entry_set, @comment)
|
|
177
|
-
cdir.write_to_stream(@output_stream)
|
|
178
|
-
end
|
|
179
|
-
|
|
180
190
|
protected
|
|
181
191
|
|
|
182
|
-
def finish
|
|
192
|
+
def finish # :nodoc:
|
|
183
193
|
@compressor.finish
|
|
184
194
|
end
|
|
185
195
|
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Zip
|
|
2
|
-
class PassThruDecompressor < Decompressor
|
|
4
|
+
class PassThruDecompressor < Decompressor # :nodoc:all
|
|
3
5
|
def initialize(*args)
|
|
4
6
|
super
|
|
5
7
|
@read_so_far = 0
|
|
6
8
|
end
|
|
7
9
|
|
|
8
|
-
def read(length = nil, outbuf = '')
|
|
9
|
-
return (length.nil? || length.zero? ? '' : nil) if eof
|
|
10
|
+
def read(length = nil, outbuf = +'')
|
|
11
|
+
return (length.nil? || length.zero? ? '' : nil) if eof?
|
|
10
12
|
|
|
11
13
|
if length.nil? || (@read_so_far + length) > decompressed_size
|
|
12
14
|
length = decompressed_size - @read_so_far
|
|
@@ -16,11 +18,12 @@ module Zip
|
|
|
16
18
|
input_stream.read(length, outbuf)
|
|
17
19
|
end
|
|
18
20
|
|
|
19
|
-
def eof
|
|
21
|
+
def eof?
|
|
20
22
|
@read_so_far >= decompressed_size
|
|
21
23
|
end
|
|
22
24
|
|
|
23
|
-
|
|
25
|
+
# Alias for compatibility. Remove for version 4.
|
|
26
|
+
alias eof eof?
|
|
24
27
|
end
|
|
25
28
|
|
|
26
29
|
::Zip::Decompressor.register(::Zip::COMPRESSION_METHOD_STORE, ::Zip::PassThruDecompressor)
|