rubyzip 1.3.0 → 3.0.0.alpha
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 +368 -0
- data/README.md +123 -46
- data/Rakefile +13 -6
- data/lib/zip/central_directory.rb +166 -116
- data/lib/zip/compressor.rb +3 -1
- data/lib/zip/constants.rb +77 -21
- data/lib/zip/crypto/decrypted_io.rb +42 -0
- data/lib/zip/crypto/encryption.rb +4 -2
- data/lib/zip/crypto/null_encryption.rb +5 -3
- data/lib/zip/crypto/traditional_encryption.rb +14 -12
- data/lib/zip/decompressor.rb +21 -2
- data/lib/zip/deflater.rb +10 -8
- data/lib/zip/dirtyable.rb +32 -0
- data/lib/zip/dos_time.rb +53 -12
- data/lib/zip/entry.rb +306 -184
- data/lib/zip/entry_set.rb +11 -7
- data/lib/zip/errors.rb +115 -15
- data/lib/zip/extra_field/generic.rb +11 -17
- data/lib/zip/extra_field/ntfs.rb +8 -2
- data/lib/zip/extra_field/old_unix.rb +6 -2
- data/lib/zip/extra_field/universal_time.rb +45 -13
- data/lib/zip/extra_field/unix.rb +7 -3
- data/lib/zip/extra_field/unknown.rb +33 -0
- data/lib/zip/extra_field/zip64.rb +16 -7
- data/lib/zip/extra_field.rb +22 -26
- data/lib/zip/file.rb +196 -240
- data/lib/zip/file_split.rb +97 -0
- data/lib/zip/filesystem/dir.rb +86 -0
- data/lib/zip/filesystem/directory_iterator.rb +48 -0
- data/lib/zip/filesystem/file.rb +262 -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 +31 -584
- data/lib/zip/inflater.rb +27 -37
- data/lib/zip/input_stream.rb +67 -42
- data/lib/zip/ioextras/abstract_input_stream.rb +32 -16
- data/lib/zip/ioextras/abstract_output_stream.rb +5 -3
- data/lib/zip/ioextras.rb +7 -7
- data/lib/zip/null_compressor.rb +3 -1
- data/lib/zip/null_decompressor.rb +4 -10
- data/lib/zip/null_input_stream.rb +3 -1
- data/lib/zip/output_stream.rb +58 -43
- data/lib/zip/pass_thru_compressor.rb +5 -3
- data/lib/zip/pass_thru_decompressor.rb +16 -23
- data/lib/zip/streamable_directory.rb +6 -4
- data/lib/zip/streamable_stream.rb +9 -10
- data/lib/zip/version.rb +3 -1
- data/lib/zip.rb +19 -4
- data/rubyzip.gemspec +38 -0
- data/samples/example.rb +9 -4
- data/samples/example_filesystem.rb +3 -2
- data/samples/example_recursive.rb +3 -1
- data/samples/gtk_ruby_zip.rb +22 -20
- data/samples/qtzip.rb +12 -11
- data/samples/write_simple.rb +3 -4
- data/samples/zipfind.rb +24 -22
- metadata +86 -179
- data/TODO +0 -15
- data/lib/zip/extra_field/zip64_placeholder.rb +0 -15
- data/test/basic_zip_file_test.rb +0 -60
- data/test/case_sensitivity_test.rb +0 -69
- data/test/central_directory_entry_test.rb +0 -69
- data/test/central_directory_test.rb +0 -100
- data/test/crypto/null_encryption_test.rb +0 -57
- data/test/crypto/traditional_encryption_test.rb +0 -80
- data/test/data/WarnInvalidDate.zip +0 -0
- data/test/data/file1.txt +0 -46
- data/test/data/file1.txt.deflatedData +0 -0
- data/test/data/file2.txt +0 -1504
- data/test/data/globTest/foo/bar/baz/foo.txt +0 -0
- data/test/data/globTest/foo.txt +0 -0
- data/test/data/globTest/food.txt +0 -0
- data/test/data/globTest.zip +0 -0
- data/test/data/gpbit3stored.zip +0 -0
- data/test/data/mimetype +0 -1
- data/test/data/notzippedruby.rb +0 -7
- data/test/data/ntfs.zip +0 -0
- data/test/data/oddExtraField.zip +0 -0
- data/test/data/path_traversal/Makefile +0 -10
- data/test/data/path_traversal/jwilk/README.md +0 -5
- data/test/data/path_traversal/jwilk/absolute1.zip +0 -0
- data/test/data/path_traversal/jwilk/absolute2.zip +0 -0
- data/test/data/path_traversal/jwilk/dirsymlink.zip +0 -0
- data/test/data/path_traversal/jwilk/dirsymlink2a.zip +0 -0
- data/test/data/path_traversal/jwilk/dirsymlink2b.zip +0 -0
- data/test/data/path_traversal/jwilk/relative0.zip +0 -0
- data/test/data/path_traversal/jwilk/relative2.zip +0 -0
- data/test/data/path_traversal/jwilk/symlink.zip +0 -0
- data/test/data/path_traversal/relative1.zip +0 -0
- data/test/data/path_traversal/tilde.zip +0 -0
- data/test/data/path_traversal/tuzovakaoff/README.md +0 -3
- data/test/data/path_traversal/tuzovakaoff/absolutepath.zip +0 -0
- data/test/data/path_traversal/tuzovakaoff/symlink.zip +0 -0
- data/test/data/rubycode.zip +0 -0
- data/test/data/rubycode2.zip +0 -0
- data/test/data/test.xls +0 -0
- data/test/data/testDirectory.bin +0 -0
- data/test/data/zip64-sample.zip +0 -0
- data/test/data/zipWithDirs.zip +0 -0
- data/test/data/zipWithEncryption.zip +0 -0
- data/test/deflater_test.rb +0 -65
- data/test/encryption_test.rb +0 -42
- data/test/entry_set_test.rb +0 -163
- data/test/entry_test.rb +0 -154
- data/test/errors_test.rb +0 -35
- data/test/extra_field_test.rb +0 -76
- data/test/file_extract_directory_test.rb +0 -54
- data/test/file_extract_test.rb +0 -145
- data/test/file_permissions_test.rb +0 -65
- data/test/file_split_test.rb +0 -57
- data/test/file_test.rb +0 -666
- data/test/filesystem/dir_iterator_test.rb +0 -58
- data/test/filesystem/directory_test.rb +0 -139
- data/test/filesystem/file_mutating_test.rb +0 -87
- data/test/filesystem/file_nonmutating_test.rb +0 -508
- data/test/filesystem/file_stat_test.rb +0 -64
- data/test/gentestfiles.rb +0 -126
- data/test/inflater_test.rb +0 -14
- data/test/input_stream_test.rb +0 -182
- data/test/ioextras/abstract_input_stream_test.rb +0 -102
- data/test/ioextras/abstract_output_stream_test.rb +0 -106
- data/test/ioextras/fake_io_test.rb +0 -18
- data/test/local_entry_test.rb +0 -154
- data/test/output_stream_test.rb +0 -128
- data/test/pass_thru_compressor_test.rb +0 -30
- data/test/pass_thru_decompressor_test.rb +0 -14
- data/test/path_traversal_test.rb +0 -141
- data/test/samples/example_recursive_test.rb +0 -37
- data/test/settings_test.rb +0 -95
- data/test/test_helper.rb +0 -234
- data/test/unicode_file_names_and_comments_test.rb +0 -62
- data/test/zip64_full_test.rb +0 -51
- data/test/zip64_support_test.rb +0 -14
@@ -1,45 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
|
5
|
+
require_relative 'dirtyable'
|
6
|
+
|
1
7
|
module Zip
|
2
|
-
class CentralDirectory
|
3
|
-
|
8
|
+
class CentralDirectory # :nodoc:
|
9
|
+
extend Forwardable
|
10
|
+
include Dirtyable
|
11
|
+
|
12
|
+
END_OF_CD_SIG = 0x06054b50
|
13
|
+
ZIP64_END_OF_CD_SIG = 0x06064b50
|
14
|
+
ZIP64_EOCD_LOCATOR_SIG = 0x07064b50
|
4
15
|
|
5
|
-
END_OF_CDS = 0x06054b50
|
6
|
-
ZIP64_END_OF_CDS = 0x06064b50
|
7
|
-
ZIP64_EOCD_LOCATOR = 0x07064b50
|
8
|
-
MAX_END_OF_CDS_SIZE = 65_536 + 18
|
9
16
|
STATIC_EOCD_SIZE = 22
|
17
|
+
ZIP64_STATIC_EOCD_SIZE = 56
|
18
|
+
ZIP64_EOCD_LOC_SIZE = 20
|
19
|
+
MAX_FILE_COMMENT_SIZE = (1 << 16) - 1
|
20
|
+
MAX_END_OF_CD_SIZE =
|
21
|
+
MAX_FILE_COMMENT_SIZE + STATIC_EOCD_SIZE + ZIP64_EOCD_LOC_SIZE
|
10
22
|
|
11
|
-
|
23
|
+
attr_accessor :comment
|
12
24
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
25
|
+
def_delegators :@entry_set,
|
26
|
+
:<<, :delete, :each, :entries, :find_entry, :glob,
|
27
|
+
:include?, :size
|
28
|
+
|
29
|
+
mark_dirty :<<, :comment=, :delete
|
17
30
|
|
18
31
|
def initialize(entries = EntrySet.new, comment = '') #:nodoc:
|
19
|
-
super()
|
32
|
+
super(dirty_on_create: false)
|
20
33
|
@entry_set = entries.kind_of?(EntrySet) ? entries : EntrySet.new(entries)
|
21
34
|
@comment = comment
|
22
35
|
end
|
23
36
|
|
37
|
+
def read_from_stream(io)
|
38
|
+
read_eocds(io)
|
39
|
+
read_central_directory_entries(io)
|
40
|
+
end
|
41
|
+
|
24
42
|
def write_to_stream(io) #:nodoc:
|
25
43
|
cdir_offset = io.tell
|
26
44
|
@entry_set.each { |entry| entry.write_c_dir_entry(io) }
|
27
45
|
eocd_offset = io.tell
|
28
46
|
cdir_size = eocd_offset - cdir_offset
|
29
|
-
if
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
write_64_e_o_c_d(io, cdir_offset, cdir_size)
|
34
|
-
write_64_eocd_locator(io, eocd_offset)
|
35
|
-
end
|
47
|
+
if Zip.write_zip64_support &&
|
48
|
+
(cdir_offset > 0xFFFFFFFF || cdir_size > 0xFFFFFFFF || @entry_set.size > 0xFFFF)
|
49
|
+
write_64_e_o_c_d(io, cdir_offset, cdir_size)
|
50
|
+
write_64_eocd_locator(io, eocd_offset)
|
36
51
|
end
|
37
52
|
write_e_o_c_d(io, cdir_offset, cdir_size)
|
38
53
|
end
|
39
54
|
|
55
|
+
# Reads the End of Central Directory Record (and the Zip64 equivalent if
|
56
|
+
# needs be) and returns the number of entries in the archive. This is a
|
57
|
+
# convenience method that avoids reading in all of the entry data to get a
|
58
|
+
# very quick entry count.
|
59
|
+
def count_entries(io)
|
60
|
+
read_eocds(io)
|
61
|
+
@size
|
62
|
+
end
|
63
|
+
|
64
|
+
def ==(other) #:nodoc:
|
65
|
+
return false unless other.kind_of?(CentralDirectory)
|
66
|
+
|
67
|
+
@entry_set.entries.sort == other.entries.sort && comment == other.comment
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
40
72
|
def write_e_o_c_d(io, offset, cdir_size) #:nodoc:
|
41
73
|
tmp = [
|
42
|
-
|
74
|
+
END_OF_CD_SIG,
|
43
75
|
0, # @numberOfThisDisk
|
44
76
|
0, # @numberOfDiskWithStartOfCDir
|
45
77
|
@entry_set ? [@entry_set.size, 0xFFFF].min : 0,
|
@@ -52,11 +84,9 @@ module Zip
|
|
52
84
|
io << @comment
|
53
85
|
end
|
54
86
|
|
55
|
-
private :write_e_o_c_d
|
56
|
-
|
57
87
|
def write_64_e_o_c_d(io, offset, cdir_size) #:nodoc:
|
58
88
|
tmp = [
|
59
|
-
|
89
|
+
ZIP64_END_OF_CD_SIG,
|
60
90
|
44, # size of zip64 end of central directory record (excludes signature and field itself)
|
61
91
|
VERSION_MADE_BY,
|
62
92
|
VERSION_NEEDED_TO_EXTRACT_ZIP64,
|
@@ -65,16 +95,14 @@ module Zip
|
|
65
95
|
@entry_set ? @entry_set.size : 0, # number of entries on this disk
|
66
96
|
@entry_set ? @entry_set.size : 0, # number of entries total
|
67
97
|
cdir_size, # size of central directory
|
68
|
-
offset
|
98
|
+
offset # offset of start of central directory in its disk
|
69
99
|
]
|
70
100
|
io << tmp.pack('VQ<vvVVQ<Q<Q<Q<')
|
71
101
|
end
|
72
102
|
|
73
|
-
private :write_64_e_o_c_d
|
74
|
-
|
75
103
|
def write_64_eocd_locator(io, zip64_eocd_offset)
|
76
104
|
tmp = [
|
77
|
-
|
105
|
+
ZIP64_EOCD_LOCATOR_SIG,
|
78
106
|
0, # number of disk containing the start of zip64 eocd record
|
79
107
|
zip64_eocd_offset, # offset of the start of zip64 eocd record in its disk
|
80
108
|
1 # total number of disks
|
@@ -82,123 +110,145 @@ module Zip
|
|
82
110
|
io << tmp.pack('VVQ<V')
|
83
111
|
end
|
84
112
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
@
|
90
|
-
@
|
91
|
-
@
|
92
|
-
@
|
93
|
-
@
|
94
|
-
@
|
95
|
-
@
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
113
|
+
def unpack_64_e_o_c_d(buffer) #:nodoc:
|
114
|
+
_, # ZIP64_END_OF_CD_SIG. We know we have this at this point.
|
115
|
+
@size_of_zip64_e_o_c_d,
|
116
|
+
@version_made_by,
|
117
|
+
@version_needed_for_extract,
|
118
|
+
@number_of_this_disk,
|
119
|
+
@number_of_disk_with_start_of_cdir,
|
120
|
+
@total_number_of_entries_in_cdir_on_this_disk,
|
121
|
+
@size,
|
122
|
+
@size_in_bytes,
|
123
|
+
@cdir_offset = buffer.unpack('VQ<vvVVQ<Q<Q<Q<')
|
124
|
+
|
125
|
+
zip64_extensible_data_size =
|
126
|
+
@size_of_zip64_e_o_c_d - ZIP64_STATIC_EOCD_SIZE + 12
|
127
|
+
@zip64_extensible_data = if zip64_extensible_data_size.zero?
|
128
|
+
''
|
129
|
+
else
|
130
|
+
buffer.slice(
|
131
|
+
ZIP64_STATIC_EOCD_SIZE,
|
132
|
+
zip64_extensible_data_size
|
133
|
+
)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def unpack_64_eocd_locator(buffer) #:nodoc:
|
138
|
+
_, # ZIP64_EOCD_LOCATOR_SIG. We know we have this at this point.
|
139
|
+
_, zip64_eocd_offset, = buffer.unpack('VVQ<V')
|
140
|
+
|
141
|
+
zip64_eocd_offset
|
100
142
|
end
|
101
143
|
|
102
|
-
def
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
comment_length
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
144
|
+
def unpack_e_o_c_d(buffer) #:nodoc:
|
145
|
+
_, # END_OF_CD_SIG. We know we have this at this point.
|
146
|
+
num_disk,
|
147
|
+
num_disk_cdir,
|
148
|
+
num_cdir_disk,
|
149
|
+
num_entries,
|
150
|
+
size_in_bytes,
|
151
|
+
cdir_offset,
|
152
|
+
comment_length = buffer.unpack('VvvvvVVv')
|
153
|
+
|
154
|
+
@number_of_this_disk = num_disk unless num_disk == 0xFFFF
|
155
|
+
@number_of_disk_with_start_of_cdir = num_disk_cdir unless num_disk_cdir == 0xFFFF
|
156
|
+
@total_number_of_entries_in_cdir_on_this_disk = num_cdir_disk unless num_cdir_disk == 0xFFFF
|
157
|
+
@size = num_entries unless num_entries == 0xFFFF
|
158
|
+
@size_in_bytes = size_in_bytes unless size_in_bytes == 0xFFFFFFFF
|
159
|
+
@cdir_offset = cdir_offset unless cdir_offset == 0xFFFFFFFF
|
160
|
+
|
161
|
+
@comment = if comment_length.positive?
|
162
|
+
buffer.slice(STATIC_EOCD_SIZE, comment_length)
|
163
|
+
else
|
164
|
+
''
|
165
|
+
end
|
117
166
|
end
|
118
167
|
|
119
168
|
def read_central_directory_entries(io) #:nodoc:
|
169
|
+
# `StringIO` doesn't raise `EINVAL` if you seek beyond the current end,
|
170
|
+
# so we need to catch that *and* query `io#eof?` here.
|
171
|
+
eof = false
|
120
172
|
begin
|
121
173
|
io.seek(@cdir_offset, IO::SEEK_SET)
|
122
174
|
rescue Errno::EINVAL
|
123
|
-
|
175
|
+
eof = true
|
124
176
|
end
|
177
|
+
raise Error, 'Zip consistency problem while reading central directory entry' if eof || io.eof?
|
178
|
+
|
125
179
|
@entry_set = EntrySet.new
|
126
180
|
@size.times do
|
127
|
-
|
128
|
-
|
129
|
-
|
181
|
+
entry = Entry.read_c_dir_entry(io)
|
182
|
+
next unless entry
|
183
|
+
|
184
|
+
offset = if entry.zip64?
|
185
|
+
entry.extra['Zip64'].relative_header_offset
|
186
|
+
else
|
187
|
+
entry.local_header_offset
|
188
|
+
end
|
130
189
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
190
|
+
unless offset.nil?
|
191
|
+
io_save = io.tell
|
192
|
+
io.seek(offset, IO::SEEK_SET)
|
193
|
+
entry.read_extra_field(read_local_extra_field(io), local: true)
|
194
|
+
io.seek(io_save, IO::SEEK_SET)
|
195
|
+
end
|
196
|
+
|
197
|
+
@entry_set << entry
|
137
198
|
end
|
138
|
-
read_central_directory_entries(io)
|
139
199
|
end
|
140
200
|
|
141
|
-
def
|
142
|
-
|
143
|
-
|
144
|
-
buf = buf.slice!((sig_index + 4)..(buf.bytesize))
|
201
|
+
def read_local_extra_field(io)
|
202
|
+
buf = io.read(::Zip::LOCAL_ENTRY_STATIC_HEADER_LENGTH) || ''
|
203
|
+
return '' unless buf.bytesize == ::Zip::LOCAL_ENTRY_STATIC_HEADER_LENGTH
|
145
204
|
|
146
|
-
|
147
|
-
|
148
|
-
end
|
205
|
+
head, _, _, _, _, _, _, _, _, _, n_len, e_len = buf.unpack('VCCvvvvVVVvv')
|
206
|
+
return '' unless head == ::Zip::LOCAL_ENTRY_SIGNATURE
|
149
207
|
|
150
|
-
|
208
|
+
io.seek(n_len, IO::SEEK_CUR) # Skip over the entry name.
|
209
|
+
io.read(e_len)
|
151
210
|
end
|
152
211
|
|
153
|
-
def
|
154
|
-
|
155
|
-
end
|
212
|
+
def read_eocds(io) #:nodoc:
|
213
|
+
base_location, data = eocd_data(io)
|
156
214
|
|
157
|
-
|
158
|
-
|
159
|
-
io.seek(-MAX_END_OF_CDS_SIZE, IO::SEEK_END)
|
160
|
-
rescue Errno::EINVAL
|
161
|
-
io.seek(0, IO::SEEK_SET)
|
162
|
-
end
|
163
|
-
io.read
|
164
|
-
end
|
215
|
+
eocd_location = data.rindex([END_OF_CD_SIG].pack('V'))
|
216
|
+
raise Error, 'Zip end of central directory signature not found' unless eocd_location
|
165
217
|
|
166
|
-
|
167
|
-
zip_64_start = buf.rindex([ZIP64_END_OF_CDS].pack('V'))
|
168
|
-
raise Error, 'Zip64 end of central directory signature not found' unless zip_64_start
|
169
|
-
zip_64_locator = buf.rindex([ZIP64_EOCD_LOCATOR].pack('V'))
|
170
|
-
raise Error, 'Zip64 end of central directory signature locator not found' unless zip_64_locator
|
171
|
-
buf = buf.slice!((zip_64_start + 4)..zip_64_locator)
|
218
|
+
zip64_eocd_locator = data.rindex([ZIP64_EOCD_LOCATOR_SIG].pack('V'))
|
172
219
|
|
173
|
-
|
174
|
-
|
175
|
-
end
|
220
|
+
if zip64_eocd_locator
|
221
|
+
zip64_eocd_location = data.rindex([ZIP64_END_OF_CD_SIG].pack('V'))
|
176
222
|
|
177
|
-
|
178
|
-
|
223
|
+
zip64_eocd_data =
|
224
|
+
if zip64_eocd_location
|
225
|
+
data.slice(zip64_eocd_location..zip64_eocd_locator)
|
226
|
+
else
|
227
|
+
zip64_eocd_location = unpack_64_eocd_locator(
|
228
|
+
data.slice(zip64_eocd_locator..eocd_location)
|
229
|
+
)
|
230
|
+
unless zip64_eocd_location
|
231
|
+
raise Error, 'Zip64 end of central directory signature not found'
|
232
|
+
end
|
179
233
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
end
|
234
|
+
io.seek(zip64_eocd_location, IO::SEEK_SET)
|
235
|
+
io.read(base_location + zip64_eocd_locator - zip64_eocd_location)
|
236
|
+
end
|
184
237
|
|
185
|
-
|
186
|
-
|
187
|
-
def size
|
188
|
-
@entry_set.size
|
189
|
-
end
|
238
|
+
unpack_64_e_o_c_d(zip64_eocd_data)
|
239
|
+
end
|
190
240
|
|
191
|
-
|
192
|
-
cdir = new
|
193
|
-
cdir.read_from_stream(io)
|
194
|
-
return cdir
|
195
|
-
rescue Error
|
196
|
-
return nil
|
241
|
+
unpack_e_o_c_d(data.slice(eocd_location..-1))
|
197
242
|
end
|
198
243
|
|
199
|
-
def
|
200
|
-
|
201
|
-
|
244
|
+
def eocd_data(io)
|
245
|
+
begin
|
246
|
+
io.seek(-MAX_END_OF_CD_SIZE, IO::SEEK_END)
|
247
|
+
rescue Errno::EINVAL
|
248
|
+
io.seek(0, IO::SEEK_SET)
|
249
|
+
end
|
250
|
+
|
251
|
+
[io.tell, io.read]
|
202
252
|
end
|
203
253
|
end
|
204
254
|
end
|
data/lib/zip/compressor.rb
CHANGED
data/lib/zip/constants.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Zip
|
2
4
|
RUNNING_ON_WINDOWS = RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/i
|
3
5
|
|
@@ -11,6 +13,8 @@ module Zip
|
|
11
13
|
VERSION_NEEDED_TO_EXTRACT = 20
|
12
14
|
VERSION_NEEDED_TO_EXTRACT_ZIP64 = 45
|
13
15
|
|
16
|
+
SPLIT_FILE_SIGNATURE = 0x08074b50
|
17
|
+
|
14
18
|
FILE_TYPE_FILE = 0o10
|
15
19
|
FILE_TYPE_DIR = 0o04
|
16
20
|
FILE_TYPE_SYMLINK = 0o12
|
@@ -38,26 +42,78 @@ module Zip
|
|
38
42
|
FSTYPE_ATHEOS = 30
|
39
43
|
|
40
44
|
FSTYPES = {
|
41
|
-
FSTYPE_FAT => 'FAT'
|
42
|
-
FSTYPE_AMIGA => 'Amiga'
|
43
|
-
FSTYPE_VMS => 'VMS (Vax or Alpha AXP)'
|
44
|
-
FSTYPE_UNIX => 'Unix'
|
45
|
-
FSTYPE_VM_CMS => 'VM/CMS'
|
46
|
-
FSTYPE_ATARI => 'Atari ST'
|
47
|
-
FSTYPE_HPFS => 'OS/2 or NT HPFS'
|
48
|
-
FSTYPE_MAC => 'Macintosh'
|
49
|
-
FSTYPE_Z_SYSTEM => 'Z-System'
|
50
|
-
FSTYPE_CPM => 'CP/M'
|
51
|
-
FSTYPE_TOPS20 => 'TOPS-20'
|
52
|
-
FSTYPE_NTFS => 'NTFS'
|
53
|
-
FSTYPE_QDOS => 'SMS/QDOS'
|
54
|
-
FSTYPE_ACORN => 'Acorn RISC OS'
|
55
|
-
FSTYPE_VFAT => 'Win32 VFAT'
|
56
|
-
FSTYPE_MVS => 'MVS'
|
57
|
-
FSTYPE_BEOS => 'BeOS'
|
58
|
-
FSTYPE_TANDEM => 'Tandem NSK'
|
59
|
-
FSTYPE_THEOS => 'Theos'
|
60
|
-
FSTYPE_MAC_OSX => 'Mac OS/X (Darwin)'
|
61
|
-
FSTYPE_ATHEOS => 'AtheOS'
|
45
|
+
FSTYPE_FAT => 'FAT',
|
46
|
+
FSTYPE_AMIGA => 'Amiga',
|
47
|
+
FSTYPE_VMS => 'VMS (Vax or Alpha AXP)',
|
48
|
+
FSTYPE_UNIX => 'Unix',
|
49
|
+
FSTYPE_VM_CMS => 'VM/CMS',
|
50
|
+
FSTYPE_ATARI => 'Atari ST',
|
51
|
+
FSTYPE_HPFS => 'OS/2 or NT HPFS',
|
52
|
+
FSTYPE_MAC => 'Macintosh',
|
53
|
+
FSTYPE_Z_SYSTEM => 'Z-System',
|
54
|
+
FSTYPE_CPM => 'CP/M',
|
55
|
+
FSTYPE_TOPS20 => 'TOPS-20',
|
56
|
+
FSTYPE_NTFS => 'NTFS',
|
57
|
+
FSTYPE_QDOS => 'SMS/QDOS',
|
58
|
+
FSTYPE_ACORN => 'Acorn RISC OS',
|
59
|
+
FSTYPE_VFAT => 'Win32 VFAT',
|
60
|
+
FSTYPE_MVS => 'MVS',
|
61
|
+
FSTYPE_BEOS => 'BeOS',
|
62
|
+
FSTYPE_TANDEM => 'Tandem NSK',
|
63
|
+
FSTYPE_THEOS => 'Theos',
|
64
|
+
FSTYPE_MAC_OSX => 'Mac OS/X (Darwin)',
|
65
|
+
FSTYPE_ATHEOS => 'AtheOS'
|
66
|
+
}.freeze
|
67
|
+
|
68
|
+
COMPRESSION_METHOD_STORE = 0
|
69
|
+
COMPRESSION_METHOD_SHRINK = 1
|
70
|
+
COMPRESSION_METHOD_REDUCE_1 = 2
|
71
|
+
COMPRESSION_METHOD_REDUCE_2 = 3
|
72
|
+
COMPRESSION_METHOD_REDUCE_3 = 4
|
73
|
+
COMPRESSION_METHOD_REDUCE_4 = 5
|
74
|
+
COMPRESSION_METHOD_IMPLODE = 6
|
75
|
+
# RESERVED = 7
|
76
|
+
COMPRESSION_METHOD_DEFLATE = 8
|
77
|
+
COMPRESSION_METHOD_DEFLATE_64 = 9
|
78
|
+
COMPRESSION_METHOD_PKWARE_DCLI = 10
|
79
|
+
# RESERVED = 11
|
80
|
+
COMPRESSION_METHOD_BZIP2 = 12
|
81
|
+
# RESERVED = 13
|
82
|
+
COMPRESSION_METHOD_LZMA = 14
|
83
|
+
# RESERVED = 15
|
84
|
+
COMPRESSION_METHOD_IBM_CMPSC = 16
|
85
|
+
# RESERVED = 17
|
86
|
+
COMPRESSION_METHOD_IBM_TERSE = 18
|
87
|
+
COMPRESSION_METHOD_IBM_LZ77 = 19
|
88
|
+
COMPRESSION_METHOD_JPEG = 96
|
89
|
+
COMPRESSION_METHOD_WAVPACK = 97
|
90
|
+
COMPRESSION_METHOD_PPMD = 98
|
91
|
+
COMPRESSION_METHOD_AES = 99
|
92
|
+
|
93
|
+
COMPRESSION_METHODS = {
|
94
|
+
COMPRESSION_METHOD_STORE => 'Store (no compression)',
|
95
|
+
COMPRESSION_METHOD_SHRINK => 'Shrink',
|
96
|
+
COMPRESSION_METHOD_REDUCE_1 => 'Reduce with compression factor 1',
|
97
|
+
COMPRESSION_METHOD_REDUCE_2 => 'Reduce with compression factor 2',
|
98
|
+
COMPRESSION_METHOD_REDUCE_3 => 'Reduce with compression factor 3',
|
99
|
+
COMPRESSION_METHOD_REDUCE_4 => 'Reduce with compression factor 4',
|
100
|
+
COMPRESSION_METHOD_IMPLODE => 'Implode',
|
101
|
+
# RESERVED = 7
|
102
|
+
COMPRESSION_METHOD_DEFLATE => 'Deflate',
|
103
|
+
COMPRESSION_METHOD_DEFLATE_64 => 'Deflate64(tm)',
|
104
|
+
COMPRESSION_METHOD_PKWARE_DCLI => 'PKWARE Data Compression Library Imploding (old IBM TERSE)',
|
105
|
+
# RESERVED = 11
|
106
|
+
COMPRESSION_METHOD_BZIP2 => 'BZIP2',
|
107
|
+
# RESERVED = 13
|
108
|
+
COMPRESSION_METHOD_LZMA => 'LZMA',
|
109
|
+
# RESERVED = 15
|
110
|
+
COMPRESSION_METHOD_IBM_CMPSC => 'IBM z/OS CMPSC Compression',
|
111
|
+
# RESERVED = 17
|
112
|
+
COMPRESSION_METHOD_IBM_TERSE => 'IBM TERSE (new)',
|
113
|
+
COMPRESSION_METHOD_IBM_LZ77 => 'IBM LZ77 z Architecture (PFS)',
|
114
|
+
COMPRESSION_METHOD_JPEG => 'JPEG variant',
|
115
|
+
COMPRESSION_METHOD_WAVPACK => 'WavPack compressed data',
|
116
|
+
COMPRESSION_METHOD_PPMD => 'PPMd version I, Rev 1',
|
117
|
+
COMPRESSION_METHOD_AES => 'AES encryption'
|
62
118
|
}.freeze
|
63
119
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Zip
|
4
|
+
class DecryptedIo # :nodoc:all
|
5
|
+
CHUNK_SIZE = 32_768
|
6
|
+
|
7
|
+
def initialize(io, decrypter)
|
8
|
+
@io = io
|
9
|
+
@decrypter = decrypter
|
10
|
+
end
|
11
|
+
|
12
|
+
def read(length = nil, outbuf = +'')
|
13
|
+
return (length.nil? || length.zero? ? '' : nil) if eof
|
14
|
+
|
15
|
+
while length.nil? || (buffer.bytesize < length)
|
16
|
+
break if input_finished?
|
17
|
+
|
18
|
+
buffer << produce_input
|
19
|
+
end
|
20
|
+
|
21
|
+
outbuf.replace(buffer.slice!(0...(length || output_buffer.bytesize)))
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def eof
|
27
|
+
buffer.empty? && input_finished?
|
28
|
+
end
|
29
|
+
|
30
|
+
def buffer
|
31
|
+
@buffer ||= +''
|
32
|
+
end
|
33
|
+
|
34
|
+
def input_finished?
|
35
|
+
@io.eof
|
36
|
+
end
|
37
|
+
|
38
|
+
def produce_input
|
39
|
+
@decrypter.decrypt(@io.read(CHUNK_SIZE))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -1,5 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Zip
|
2
|
-
module NullEncryption
|
4
|
+
module NullEncryption # :nodoc:
|
3
5
|
def header_bytesize
|
4
6
|
0
|
5
7
|
end
|
@@ -9,7 +11,7 @@ module Zip
|
|
9
11
|
end
|
10
12
|
end
|
11
13
|
|
12
|
-
class NullEncrypter < Encrypter
|
14
|
+
class NullEncrypter < Encrypter # :nodoc:
|
13
15
|
include NullEncryption
|
14
16
|
|
15
17
|
def header(_mtime)
|
@@ -27,7 +29,7 @@ module Zip
|
|
27
29
|
def reset!; end
|
28
30
|
end
|
29
31
|
|
30
|
-
class NullDecrypter < Decrypter
|
32
|
+
class NullDecrypter < Decrypter # :nodoc:
|
31
33
|
include NullEncryption
|
32
34
|
|
33
35
|
def decrypt(data)
|
@@ -1,5 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Zip
|
2
|
-
module TraditionalEncryption
|
4
|
+
module TraditionalEncryption # :nodoc:
|
3
5
|
def initialize(password)
|
4
6
|
@password = password
|
5
7
|
reset_keys!
|
@@ -24,8 +26,8 @@ module Zip
|
|
24
26
|
end
|
25
27
|
end
|
26
28
|
|
27
|
-
def update_keys(
|
28
|
-
@key0 = ~Zlib.crc32(
|
29
|
+
def update_keys(num)
|
30
|
+
@key0 = ~Zlib.crc32(num, ~@key0)
|
29
31
|
@key1 = ((@key1 + (@key0 & 0xff)) * 134_775_813 + 1) & 0xffffffff
|
30
32
|
@key2 = ~Zlib.crc32((@key1 >> 24).chr, ~@key2)
|
31
33
|
end
|
@@ -36,7 +38,7 @@ module Zip
|
|
36
38
|
end
|
37
39
|
end
|
38
40
|
|
39
|
-
class TraditionalEncrypter < Encrypter
|
41
|
+
class TraditionalEncrypter < Encrypter # :nodoc:
|
40
42
|
include TraditionalEncryption
|
41
43
|
|
42
44
|
def header(mtime)
|
@@ -63,14 +65,14 @@ module Zip
|
|
63
65
|
|
64
66
|
private
|
65
67
|
|
66
|
-
def encode(
|
68
|
+
def encode(num)
|
67
69
|
t = decrypt_byte
|
68
|
-
update_keys(
|
69
|
-
t ^
|
70
|
+
update_keys(num.chr)
|
71
|
+
t ^ num
|
70
72
|
end
|
71
73
|
end
|
72
74
|
|
73
|
-
class TraditionalDecrypter < Decrypter
|
75
|
+
class TraditionalDecrypter < Decrypter # :nodoc:
|
74
76
|
include TraditionalEncryption
|
75
77
|
|
76
78
|
def decrypt(data)
|
@@ -86,10 +88,10 @@ module Zip
|
|
86
88
|
|
87
89
|
private
|
88
90
|
|
89
|
-
def decode(
|
90
|
-
|
91
|
-
update_keys(
|
92
|
-
|
91
|
+
def decode(num)
|
92
|
+
num ^= decrypt_byte
|
93
|
+
update_keys(num.chr)
|
94
|
+
num
|
93
95
|
end
|
94
96
|
end
|
95
97
|
end
|
data/lib/zip/decompressor.rb
CHANGED
@@ -1,9 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Zip
|
2
|
-
class Decompressor
|
4
|
+
class Decompressor # :nodoc:all
|
3
5
|
CHUNK_SIZE = 32_768
|
4
|
-
|
6
|
+
|
7
|
+
def self.decompressor_classes
|
8
|
+
@decompressor_classes ||= {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.register(compression_method, decompressor_class)
|
12
|
+
decompressor_classes[compression_method] = decompressor_class
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.find_by_compression_method(compression_method)
|
16
|
+
decompressor_classes[compression_method]
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :decompressed_size, :input_stream
|
20
|
+
|
21
|
+
def initialize(input_stream, decompressed_size = nil)
|
5
22
|
super()
|
23
|
+
|
6
24
|
@input_stream = input_stream
|
25
|
+
@decompressed_size = decompressed_size
|
7
26
|
end
|
8
27
|
end
|
9
28
|
end
|