rubyzip 2.4.1 → 3.0.0.rc1
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 +419 -0
- data/LICENSE.md +24 -0
- data/README.md +137 -37
- data/Rakefile +11 -7
- data/lib/zip/central_directory.rb +169 -123
- data/lib/zip/compressor.rb +3 -1
- data/lib/zip/constants.rb +29 -21
- data/lib/zip/crypto/decrypted_io.rb +4 -2
- data/lib/zip/crypto/encryption.rb +4 -2
- data/lib/zip/crypto/null_encryption.rb +6 -4
- data/lib/zip/crypto/traditional_encryption.rb +8 -6
- data/lib/zip/decompressor.rb +4 -3
- data/lib/zip/deflater.rb +10 -8
- data/lib/zip/dirtyable.rb +32 -0
- data/lib/zip/dos_time.rb +43 -4
- data/lib/zip/entry.rb +333 -242
- data/lib/zip/entry_set.rb +11 -9
- data/lib/zip/errors.rb +136 -16
- data/lib/zip/extra_field/generic.rb +6 -13
- 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 +33 -0
- data/lib/zip/extra_field/zip64.rb +12 -5
- data/lib/zip/extra_field.rb +16 -22
- data/lib/zip/file.rb +166 -264
- 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 +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 +27 -596
- data/lib/zip/inflater.rb +7 -5
- data/lib/zip/input_stream.rb +50 -50
- data/lib/zip/ioextras/abstract_input_stream.rb +16 -11
- 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 +3 -1
- data/lib/zip/null_input_stream.rb +3 -1
- data/lib/zip/output_stream.rb +55 -56
- data/lib/zip/pass_thru_compressor.rb +3 -1
- data/lib/zip/pass_thru_decompressor.rb +4 -2
- data/lib/zip/streamable_directory.rb +3 -1
- data/lib/zip/streamable_stream.rb +3 -0
- data/lib/zip/version.rb +3 -1
- data/lib/zip.rb +18 -22
- 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 +4 -2
- data/samples/qtzip.rb +6 -5
- data/samples/write_simple.rb +2 -1
- data/samples/zipfind.rb +1 -0
- metadata +87 -51
- data/TODO +0 -15
- data/lib/zip/extra_field/zip64_placeholder.rb +0 -15
@@ -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
|
-
def initialize(entries = EntrySet.new, comment = '')
|
19
|
-
super()
|
31
|
+
def initialize(entries = EntrySet.new, comment = '') # :nodoc:
|
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
|
|
24
|
-
def
|
37
|
+
def read_from_stream(io)
|
38
|
+
read_eocds(io)
|
39
|
+
read_central_directory_entries(io)
|
40
|
+
end
|
41
|
+
|
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
|
|
40
|
-
|
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
|
+
|
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
|
-
|
56
|
-
|
57
|
-
def write_64_e_o_c_d(io, offset, cdir_size) #:nodoc:
|
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,
|
@@ -70,11 +100,9 @@ module Zip
|
|
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,127 +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
|
-
def read_central_directory_entries(io)
|
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
|
189
|
+
|
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
|
130
196
|
|
131
|
-
|
132
|
-
buf = start_buf(io)
|
133
|
-
if zip64_file?(buf)
|
134
|
-
read_64_e_o_c_d(buf)
|
135
|
-
else
|
136
|
-
read_e_o_c_d(buf)
|
197
|
+
@entry_set << entry
|
137
198
|
end
|
138
|
-
read_central_directory_entries(io)
|
139
199
|
end
|
140
200
|
|
141
|
-
def
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
buf = buf.slice!((sig_index + 4)..(buf.bytesize))
|
146
|
-
|
147
|
-
def buf.read(count)
|
148
|
-
slice!(0, count)
|
149
|
-
end
|
150
|
-
|
151
|
-
buf
|
152
|
-
end
|
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
|
153
204
|
|
154
|
-
|
155
|
-
|
156
|
-
end
|
205
|
+
head, _, _, _, _, _, _, _, _, _, n_len, e_len = buf.unpack('VCCvvvvVVVvv')
|
206
|
+
return '' unless head == ::Zip::LOCAL_ENTRY_SIGNATURE
|
157
207
|
|
158
|
-
|
159
|
-
|
160
|
-
io.seek(-MAX_END_OF_CDS_SIZE, IO::SEEK_END)
|
161
|
-
rescue Errno::EINVAL
|
162
|
-
io.seek(0, IO::SEEK_SET)
|
163
|
-
end
|
164
|
-
io.read
|
208
|
+
io.seek(n_len, IO::SEEK_CUR) # Skip over the entry name.
|
209
|
+
io.read(e_len)
|
165
210
|
end
|
166
211
|
|
167
|
-
def
|
168
|
-
|
169
|
-
raise Error, 'Zip64 end of central directory signature not found' unless zip_64_start
|
212
|
+
def read_eocds(io) # :nodoc:
|
213
|
+
base_location, data = eocd_data(io)
|
170
214
|
|
171
|
-
|
172
|
-
raise Error, '
|
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
|
173
217
|
|
174
|
-
|
218
|
+
zip64_eocd_locator = data.rindex([ZIP64_EOCD_LOCATOR_SIG].pack('V'))
|
175
219
|
|
176
|
-
|
177
|
-
|
178
|
-
end
|
220
|
+
if zip64_eocd_locator
|
221
|
+
zip64_eocd_location = data.rindex([ZIP64_END_OF_CD_SIG].pack('V'))
|
179
222
|
|
180
|
-
|
181
|
-
|
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
|
182
233
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
end
|
234
|
+
io.seek(zip64_eocd_location, IO::SEEK_SET)
|
235
|
+
io.read(base_location + zip64_eocd_locator - zip64_eocd_location)
|
236
|
+
end
|
187
237
|
|
188
|
-
|
189
|
-
|
190
|
-
def size
|
191
|
-
@entry_set.size
|
192
|
-
end
|
238
|
+
unpack_64_e_o_c_d(zip64_eocd_data)
|
239
|
+
end
|
193
240
|
|
194
|
-
|
195
|
-
cdir = new
|
196
|
-
cdir.read_from_stream(io)
|
197
|
-
cdir
|
198
|
-
rescue Error
|
199
|
-
nil
|
241
|
+
unpack_e_o_c_d(data.slice(eocd_location..-1))
|
200
242
|
end
|
201
243
|
|
202
|
-
def
|
203
|
-
|
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
|
204
250
|
|
205
|
-
|
251
|
+
[io.tell, io.read]
|
206
252
|
end
|
207
253
|
end
|
208
254
|
end
|
data/lib/zip/compressor.rb
CHANGED
data/lib/zip/constants.rb
CHANGED
@@ -1,4 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Zip
|
4
|
+
# :stopdoc:
|
5
|
+
|
2
6
|
RUNNING_ON_WINDOWS = RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/i
|
3
7
|
|
4
8
|
CENTRAL_DIRECTORY_ENTRY_SIGNATURE = 0x02014b50
|
@@ -11,6 +15,8 @@ module Zip
|
|
11
15
|
VERSION_NEEDED_TO_EXTRACT = 20
|
12
16
|
VERSION_NEEDED_TO_EXTRACT_ZIP64 = 45
|
13
17
|
|
18
|
+
SPLIT_FILE_SIGNATURE = 0x08074b50
|
19
|
+
|
14
20
|
FILE_TYPE_FILE = 0o10
|
15
21
|
FILE_TYPE_DIR = 0o04
|
16
22
|
FILE_TYPE_SYMLINK = 0o12
|
@@ -38,27 +44,27 @@ module Zip
|
|
38
44
|
FSTYPE_ATHEOS = 30
|
39
45
|
|
40
46
|
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'
|
47
|
+
FSTYPE_FAT => 'FAT',
|
48
|
+
FSTYPE_AMIGA => 'Amiga',
|
49
|
+
FSTYPE_VMS => 'VMS (Vax or Alpha AXP)',
|
50
|
+
FSTYPE_UNIX => 'Unix',
|
51
|
+
FSTYPE_VM_CMS => 'VM/CMS',
|
52
|
+
FSTYPE_ATARI => 'Atari ST',
|
53
|
+
FSTYPE_HPFS => 'OS/2 or NT HPFS',
|
54
|
+
FSTYPE_MAC => 'Macintosh',
|
55
|
+
FSTYPE_Z_SYSTEM => 'Z-System',
|
56
|
+
FSTYPE_CPM => 'CP/M',
|
57
|
+
FSTYPE_TOPS20 => 'TOPS-20',
|
58
|
+
FSTYPE_NTFS => 'NTFS',
|
59
|
+
FSTYPE_QDOS => 'SMS/QDOS',
|
60
|
+
FSTYPE_ACORN => 'Acorn RISC OS',
|
61
|
+
FSTYPE_VFAT => 'Win32 VFAT',
|
62
|
+
FSTYPE_MVS => 'MVS',
|
63
|
+
FSTYPE_BEOS => 'BeOS',
|
64
|
+
FSTYPE_TANDEM => 'Tandem NSK',
|
65
|
+
FSTYPE_THEOS => 'Theos',
|
66
|
+
FSTYPE_MAC_OSX => 'Mac OS/X (Darwin)',
|
67
|
+
FSTYPE_ATHEOS => 'AtheOS'
|
62
68
|
}.freeze
|
63
69
|
|
64
70
|
COMPRESSION_METHOD_STORE = 0
|
@@ -112,4 +118,6 @@ module Zip
|
|
112
118
|
COMPRESSION_METHOD_PPMD => 'PPMd version I, Rev 1',
|
113
119
|
COMPRESSION_METHOD_AES => 'AES encryption'
|
114
120
|
}.freeze
|
121
|
+
|
122
|
+
# :startdoc:
|
115
123
|
end
|
@@ -1,5 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Zip
|
2
|
-
class DecryptedIo
|
4
|
+
class DecryptedIo # :nodoc:all
|
3
5
|
CHUNK_SIZE = 32_768
|
4
6
|
|
5
7
|
def initialize(io, decrypter)
|
@@ -16,7 +18,7 @@ module Zip
|
|
16
18
|
buffer << produce_input
|
17
19
|
end
|
18
20
|
|
19
|
-
outbuf.replace(buffer.slice!(0...(length ||
|
21
|
+
outbuf.replace(buffer.slice!(0...(length || buffer.bytesize)))
|
20
22
|
end
|
21
23
|
|
22
24
|
private
|
@@ -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)
|
@@ -20,14 +22,14 @@ module Zip
|
|
20
22
|
data
|
21
23
|
end
|
22
24
|
|
23
|
-
def data_descriptor(_crc32, _compressed_size,
|
25
|
+
def data_descriptor(_crc32, _compressed_size, _uncompressed_size)
|
24
26
|
''
|
25
27
|
end
|
26
28
|
|
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!
|
@@ -26,7 +28,7 @@ module Zip
|
|
26
28
|
|
27
29
|
def update_keys(num)
|
28
30
|
@key0 = ~Zlib.crc32(num, ~@key0)
|
29
|
-
@key1 = ((@key1 + (@key0 & 0xff)) * 134_775_813 + 1) & 0xffffffff
|
31
|
+
@key1 = (((@key1 + (@key0 & 0xff)) * 134_775_813) + 1) & 0xffffffff
|
30
32
|
@key2 = ~Zlib.crc32((@key1 >> 24).chr, ~@key2)
|
31
33
|
end
|
32
34
|
|
@@ -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)
|
@@ -53,8 +55,8 @@ module Zip
|
|
53
55
|
data.unpack('C*').map { |x| encode x }.pack('C*')
|
54
56
|
end
|
55
57
|
|
56
|
-
def data_descriptor(crc32, compressed_size,
|
57
|
-
[0x08074b50, crc32, compressed_size,
|
58
|
+
def data_descriptor(crc32, compressed_size, uncompressed_size)
|
59
|
+
[0x08074b50, crc32, compressed_size, uncompressed_size].pack('VVVV')
|
58
60
|
end
|
59
61
|
|
60
62
|
def reset!
|
@@ -70,7 +72,7 @@ module Zip
|
|
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)
|
data/lib/zip/decompressor.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
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
|
|
5
7
|
def self.decompressor_classes
|
@@ -14,8 +16,7 @@ module Zip
|
|
14
16
|
decompressor_classes[compression_method]
|
15
17
|
end
|
16
18
|
|
17
|
-
attr_reader :input_stream
|
18
|
-
attr_reader :decompressed_size
|
19
|
+
attr_reader :decompressed_size, :input_stream
|
19
20
|
|
20
21
|
def initialize(input_stream, decompressed_size = nil)
|
21
22
|
super()
|
data/lib/zip/deflater.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Zip
|
2
|
-
class Deflater < Compressor
|
4
|
+
class Deflater < Compressor # :nodoc:all
|
3
5
|
def initialize(output_stream, level = Zip.default_compression, encrypter = NullEncrypter.new)
|
4
6
|
super()
|
5
7
|
@output_stream = output_stream
|
@@ -13,16 +15,16 @@ module Zip
|
|
13
15
|
val = data.to_s
|
14
16
|
@crc = Zlib.crc32(val, @crc)
|
15
17
|
@size += val.bytesize
|
16
|
-
buffer = @zlib_deflater.deflate(data)
|
17
|
-
if buffer.empty?
|
18
|
-
|
19
|
-
|
20
|
-
@output_stream << @encrypter.encrypt(buffer)
|
21
|
-
end
|
18
|
+
buffer = @zlib_deflater.deflate(data, Zlib::SYNC_FLUSH)
|
19
|
+
return @output_stream if buffer.empty?
|
20
|
+
|
21
|
+
@output_stream << @encrypter.encrypt(buffer)
|
22
22
|
end
|
23
23
|
|
24
24
|
def finish
|
25
|
-
|
25
|
+
buffer = @zlib_deflater.finish
|
26
|
+
@output_stream << @encrypter.encrypt(buffer) unless buffer.empty?
|
27
|
+
@zlib_deflater.close
|
26
28
|
end
|
27
29
|
|
28
30
|
attr_reader :size, :crc
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Zip
|
4
|
+
module Dirtyable # :nodoc:all
|
5
|
+
def initialize(dirty_on_create: true)
|
6
|
+
@dirty = dirty_on_create
|
7
|
+
end
|
8
|
+
|
9
|
+
def dirty?
|
10
|
+
@dirty
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods # :nodoc:
|
14
|
+
def mark_dirty(*symbols) # :nodoc:
|
15
|
+
# Move the original method and call it after we've set the dirty flag.
|
16
|
+
symbols.each do |symbol|
|
17
|
+
orig_name = "orig_#{symbol}"
|
18
|
+
alias_method orig_name, symbol
|
19
|
+
|
20
|
+
define_method(symbol) do |param|
|
21
|
+
@dirty = true
|
22
|
+
send(orig_name, param)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.included(base)
|
29
|
+
base.extend(ClassMethods)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|