rubyzip 1.3.0 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +2 -8
- data/Rakefile +3 -0
- data/lib/zip.rb +4 -3
- data/lib/zip/central_directory.rb +9 -5
- data/lib/zip/constants.rb +52 -0
- data/lib/zip/crypto/decrypted_io.rb +40 -0
- data/lib/zip/crypto/traditional_encryption.rb +9 -9
- data/lib/zip/decompressor.rb +19 -1
- data/lib/zip/dos_time.rb +12 -7
- data/lib/zip/entry.rb +57 -38
- data/lib/zip/entry_set.rb +2 -0
- data/lib/zip/errors.rb +1 -0
- data/lib/zip/extra_field.rb +11 -9
- data/lib/zip/extra_field/generic.rb +10 -9
- data/lib/zip/extra_field/ntfs.rb +4 -0
- data/lib/zip/extra_field/old_unix.rb +3 -1
- data/lib/zip/extra_field/universal_time.rb +42 -12
- data/lib/zip/extra_field/unix.rb +3 -1
- data/lib/zip/extra_field/zip64.rb +4 -2
- data/lib/zip/file.rb +79 -54
- data/lib/zip/filesystem.rb +193 -177
- data/lib/zip/inflater.rb +24 -36
- data/lib/zip/input_stream.rb +33 -26
- data/lib/zip/ioextras.rb +1 -1
- data/lib/zip/ioextras/abstract_input_stream.rb +19 -8
- data/lib/zip/ioextras/abstract_output_stream.rb +1 -1
- data/lib/zip/null_decompressor.rb +1 -9
- data/lib/zip/output_stream.rb +14 -5
- data/lib/zip/pass_thru_compressor.rb +2 -2
- data/lib/zip/pass_thru_decompressor.rb +13 -22
- data/lib/zip/streamable_directory.rb +3 -3
- data/lib/zip/streamable_stream.rb +6 -10
- data/lib/zip/version.rb +1 -1
- data/samples/example.rb +2 -2
- data/samples/example_filesystem.rb +1 -1
- data/samples/gtk_ruby_zip.rb +19 -19
- data/samples/qtzip.rb +6 -6
- data/samples/write_simple.rb +2 -4
- data/samples/zipfind.rb +23 -22
- metadata +28 -169
- 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.zip +0 -0
- data/test/data/globTest/foo.txt +0 -0
- data/test/data/globTest/foo/bar/baz/foo.txt +0 -0
- data/test/data/globTest/food.txt +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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8ffab0e42187d7bbd5b732ff0b9395a6215587a18ce5dd9d64da46d1c628a69c
|
4
|
+
data.tar.gz: 3032ae5d4f62644c1a19d5de25d515844ecb8da0f59c774fcaa6b16904f496b7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 218f77b0c4ae423f2baed702bf56bb598e2c3d22a01fc9af8c2fa33432580e2706a820474fa1222480370517a9f28bda692a2f296d89620459d9c6239fcfff37
|
7
|
+
data.tar.gz: a4ffa820bb2272b07bd918827d03751261f87d694966ef6c88f74f4ec569c94b0420c9288a92e5370653d00790478bdd85d3e89a9ef42ccd94834facb622c336
|
data/README.md
CHANGED
@@ -20,7 +20,7 @@ gem 'zip-zip' # will load compatibility for old rubyzip API.
|
|
20
20
|
|
21
21
|
## Requirements
|
22
22
|
|
23
|
-
- Ruby
|
23
|
+
- Ruby 2.4 or greater (for rubyzip 2.0; use 1.x for older rubies)
|
24
24
|
|
25
25
|
## Installation
|
26
26
|
|
@@ -265,13 +265,7 @@ Zip.warn_invalid_date = false
|
|
265
265
|
|
266
266
|
### Size Validation
|
267
267
|
|
268
|
-
|
269
|
-
|
270
|
-
If you set
|
271
|
-
```
|
272
|
-
Zip.validate_entry_sizes = true
|
273
|
-
```
|
274
|
-
then `rubyzip`'s `extract` method checks that an entry's reported uncompressed size is not (significantly) smaller than its actual size. This is to help you protect your application against [zip bombs](https://en.wikipedia.org/wiki/Zip_bomb). Before `extract`ing an entry, you should check that its size is in the range you expect. For example, if your application supports processing up to 100 files at once, each up to 10MiB, your zip extraction code might look like:
|
268
|
+
By default (in rubyzip >= 2.0), rubyzip's `extract` method checks that an entry's reported uncompressed size is not (significantly) smaller than its actual size. This is to help you protect your application against [zip bombs](https://en.wikipedia.org/wiki/Zip_bomb). Before `extract`ing an entry, you should check that its size is in the range you expect. For example, if your application supports processing up to 100 files at once, each up to 10MiB, your zip extraction code might look like:
|
275
269
|
|
276
270
|
```ruby
|
277
271
|
MAX_FILE_SIZE = 10 * 1024**2 # 10MiB
|
data/Rakefile
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'bundler/gem_tasks'
|
2
2
|
require 'rake/testtask'
|
3
|
+
require 'rubocop/rake_task'
|
3
4
|
|
4
5
|
task default: :test
|
5
6
|
|
@@ -10,6 +11,8 @@ Rake::TestTask.new(:test) do |test|
|
|
10
11
|
test.verbose = true
|
11
12
|
end
|
12
13
|
|
14
|
+
RuboCop::RakeTask.new
|
15
|
+
|
13
16
|
# Rake::TestTask.new(:zip64_full_test) do |test|
|
14
17
|
# test.libs << File.join(File.dirname(__FILE__), 'lib')
|
15
18
|
# test.libs << File.join(File.dirname(__FILE__), 'test')
|
data/lib/zip.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
|
+
require 'English'
|
1
2
|
require 'delegate'
|
2
3
|
require 'singleton'
|
3
4
|
require 'tempfile'
|
4
|
-
require 'tmpdir'
|
5
5
|
require 'fileutils'
|
6
6
|
require 'stringio'
|
7
7
|
require 'zlib'
|
8
|
+
require 'zip/constants'
|
8
9
|
require 'zip/dos_time'
|
9
10
|
require 'zip/ioextras'
|
10
11
|
require 'rbconfig'
|
@@ -22,6 +23,7 @@ require 'zip/null_compressor'
|
|
22
23
|
require 'zip/null_input_stream'
|
23
24
|
require 'zip/pass_thru_compressor'
|
24
25
|
require 'zip/pass_thru_decompressor'
|
26
|
+
require 'zip/crypto/decrypted_io'
|
25
27
|
require 'zip/crypto/encryption'
|
26
28
|
require 'zip/crypto/null_encryption'
|
27
29
|
require 'zip/crypto/traditional_encryption'
|
@@ -29,7 +31,6 @@ require 'zip/inflater'
|
|
29
31
|
require 'zip/deflater'
|
30
32
|
require 'zip/streamable_stream'
|
31
33
|
require 'zip/streamable_directory'
|
32
|
-
require 'zip/constants'
|
33
34
|
require 'zip/errors'
|
34
35
|
|
35
36
|
module Zip
|
@@ -55,7 +56,7 @@ module Zip
|
|
55
56
|
@write_zip64_support = false
|
56
57
|
@warn_invalid_date = true
|
57
58
|
@case_insensitive_match = false
|
58
|
-
@validate_entry_sizes =
|
59
|
+
@validate_entry_sizes = true
|
59
60
|
end
|
60
61
|
|
61
62
|
def setup
|
@@ -65,7 +65,7 @@ module Zip
|
|
65
65
|
@entry_set ? @entry_set.size : 0, # number of entries on this disk
|
66
66
|
@entry_set ? @entry_set.size : 0, # number of entries total
|
67
67
|
cdir_size, # size of central directory
|
68
|
-
offset
|
68
|
+
offset # offset of start of central directory in its disk
|
69
69
|
]
|
70
70
|
io << tmp.pack('VQ<vvVVQ<Q<Q<Q<')
|
71
71
|
end
|
@@ -141,6 +141,7 @@ module Zip
|
|
141
141
|
def get_e_o_c_d(buf) #:nodoc:
|
142
142
|
sig_index = buf.rindex([END_OF_CDS].pack('V'))
|
143
143
|
raise Error, 'Zip end of central directory signature not found' unless sig_index
|
144
|
+
|
144
145
|
buf = buf.slice!((sig_index + 4)..(buf.bytesize))
|
145
146
|
|
146
147
|
def buf.read(count)
|
@@ -166,8 +167,10 @@ module Zip
|
|
166
167
|
def get_64_e_o_c_d(buf) #:nodoc:
|
167
168
|
zip_64_start = buf.rindex([ZIP64_END_OF_CDS].pack('V'))
|
168
169
|
raise Error, 'Zip64 end of central directory signature not found' unless zip_64_start
|
170
|
+
|
169
171
|
zip_64_locator = buf.rindex([ZIP64_EOCD_LOCATOR].pack('V'))
|
170
172
|
raise Error, 'Zip64 end of central directory signature locator not found' unless zip_64_locator
|
173
|
+
|
171
174
|
buf = buf.slice!((zip_64_start + 4)..zip_64_locator)
|
172
175
|
|
173
176
|
def buf.read(count)
|
@@ -178,8 +181,8 @@ module Zip
|
|
178
181
|
end
|
179
182
|
|
180
183
|
# For iterating over the entries.
|
181
|
-
def each(&
|
182
|
-
@entry_set.each(&
|
184
|
+
def each(&a_proc)
|
185
|
+
@entry_set.each(&a_proc)
|
183
186
|
end
|
184
187
|
|
185
188
|
# Returns the number of entries in the central directory (and
|
@@ -191,13 +194,14 @@ module Zip
|
|
191
194
|
def self.read_from_stream(io) #:nodoc:
|
192
195
|
cdir = new
|
193
196
|
cdir.read_from_stream(io)
|
194
|
-
|
197
|
+
cdir
|
195
198
|
rescue Error
|
196
|
-
|
199
|
+
nil
|
197
200
|
end
|
198
201
|
|
199
202
|
def ==(other) #:nodoc:
|
200
203
|
return false unless other.kind_of?(CentralDirectory)
|
204
|
+
|
201
205
|
@entry_set.entries.sort == other.entries.sort && comment == other.comment
|
202
206
|
end
|
203
207
|
end
|
data/lib/zip/constants.rb
CHANGED
@@ -60,4 +60,56 @@ module Zip
|
|
60
60
|
FSTYPE_MAC_OSX => 'Mac OS/X (Darwin)'.freeze,
|
61
61
|
FSTYPE_ATHEOS => 'AtheOS'.freeze
|
62
62
|
}.freeze
|
63
|
+
|
64
|
+
COMPRESSION_METHOD_STORE = 0
|
65
|
+
COMPRESSION_METHOD_SHRINK = 1
|
66
|
+
COMPRESSION_METHOD_REDUCE_1 = 2
|
67
|
+
COMPRESSION_METHOD_REDUCE_2 = 3
|
68
|
+
COMPRESSION_METHOD_REDUCE_3 = 4
|
69
|
+
COMPRESSION_METHOD_REDUCE_4 = 5
|
70
|
+
COMPRESSION_METHOD_IMPLODE = 6
|
71
|
+
# RESERVED = 7
|
72
|
+
COMPRESSION_METHOD_DEFLATE = 8
|
73
|
+
COMPRESSION_METHOD_DEFLATE_64 = 9
|
74
|
+
COMPRESSION_METHOD_PKWARE_DCLI = 10
|
75
|
+
# RESERVED = 11
|
76
|
+
COMPRESSION_METHOD_BZIP2 = 12
|
77
|
+
# RESERVED = 13
|
78
|
+
COMPRESSION_METHOD_LZMA = 14
|
79
|
+
# RESERVED = 15
|
80
|
+
COMPRESSION_METHOD_IBM_CMPSC = 16
|
81
|
+
# RESERVED = 17
|
82
|
+
COMPRESSION_METHOD_IBM_TERSE = 18
|
83
|
+
COMPRESSION_METHOD_IBM_LZ77 = 19
|
84
|
+
COMPRESSION_METHOD_JPEG = 96
|
85
|
+
COMPRESSION_METHOD_WAVPACK = 97
|
86
|
+
COMPRESSION_METHOD_PPMD = 98
|
87
|
+
COMPRESSION_METHOD_AES = 99
|
88
|
+
|
89
|
+
COMPRESSION_METHODS = {
|
90
|
+
COMPRESSION_METHOD_STORE => 'Store (no compression)',
|
91
|
+
COMPRESSION_METHOD_SHRINK => 'Shrink',
|
92
|
+
COMPRESSION_METHOD_REDUCE_1 => 'Reduce with compression factor 1',
|
93
|
+
COMPRESSION_METHOD_REDUCE_2 => 'Reduce with compression factor 2',
|
94
|
+
COMPRESSION_METHOD_REDUCE_3 => 'Reduce with compression factor 3',
|
95
|
+
COMPRESSION_METHOD_REDUCE_4 => 'Reduce with compression factor 4',
|
96
|
+
COMPRESSION_METHOD_IMPLODE => 'Implode',
|
97
|
+
# RESERVED = 7
|
98
|
+
COMPRESSION_METHOD_DEFLATE => 'Deflate',
|
99
|
+
COMPRESSION_METHOD_DEFLATE_64 => 'Deflate64(tm)',
|
100
|
+
COMPRESSION_METHOD_PKWARE_DCLI => 'PKWARE Data Compression Library Imploding (old IBM TERSE)',
|
101
|
+
# RESERVED = 11
|
102
|
+
COMPRESSION_METHOD_BZIP2 => 'BZIP2',
|
103
|
+
# RESERVED = 13
|
104
|
+
COMPRESSION_METHOD_LZMA => 'LZMA',
|
105
|
+
# RESERVED = 15
|
106
|
+
COMPRESSION_METHOD_IBM_CMPSC => 'IBM z/OS CMPSC Compression',
|
107
|
+
# RESERVED = 17
|
108
|
+
COMPRESSION_METHOD_IBM_TERSE => 'IBM TERSE (new)',
|
109
|
+
COMPRESSION_METHOD_IBM_LZ77 => 'IBM LZ77 z Architecture (PFS)',
|
110
|
+
COMPRESSION_METHOD_JPEG => 'JPEG variant',
|
111
|
+
COMPRESSION_METHOD_WAVPACK => 'WavPack compressed data',
|
112
|
+
COMPRESSION_METHOD_PPMD => 'PPMd version I, Rev 1',
|
113
|
+
COMPRESSION_METHOD_AES => 'AES encryption'
|
114
|
+
}.freeze
|
63
115
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Zip
|
2
|
+
class DecryptedIo #:nodoc:all
|
3
|
+
CHUNK_SIZE = 32_768
|
4
|
+
|
5
|
+
def initialize(io, decrypter)
|
6
|
+
@io = io
|
7
|
+
@decrypter = decrypter
|
8
|
+
end
|
9
|
+
|
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
|
+
|
16
|
+
buffer << produce_input
|
17
|
+
end
|
18
|
+
|
19
|
+
outbuf.replace(buffer.slice!(0...(length || output_buffer.bytesize)))
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def eof
|
25
|
+
buffer.empty? && input_finished?
|
26
|
+
end
|
27
|
+
|
28
|
+
def buffer
|
29
|
+
@buffer ||= +''
|
30
|
+
end
|
31
|
+
|
32
|
+
def input_finished?
|
33
|
+
@io.eof
|
34
|
+
end
|
35
|
+
|
36
|
+
def produce_input
|
37
|
+
@decrypter.decrypt(@io.read(CHUNK_SIZE))
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -24,8 +24,8 @@ module Zip
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
-
def update_keys(
|
28
|
-
@key0 = ~Zlib.crc32(
|
27
|
+
def update_keys(num)
|
28
|
+
@key0 = ~Zlib.crc32(num, ~@key0)
|
29
29
|
@key1 = ((@key1 + (@key0 & 0xff)) * 134_775_813 + 1) & 0xffffffff
|
30
30
|
@key2 = ~Zlib.crc32((@key1 >> 24).chr, ~@key2)
|
31
31
|
end
|
@@ -63,10 +63,10 @@ module Zip
|
|
63
63
|
|
64
64
|
private
|
65
65
|
|
66
|
-
def encode(
|
66
|
+
def encode(num)
|
67
67
|
t = decrypt_byte
|
68
|
-
update_keys(
|
69
|
-
t ^
|
68
|
+
update_keys(num.chr)
|
69
|
+
t ^ num
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
@@ -86,10 +86,10 @@ module Zip
|
|
86
86
|
|
87
87
|
private
|
88
88
|
|
89
|
-
def decode(
|
90
|
-
|
91
|
-
update_keys(
|
92
|
-
|
89
|
+
def decode(num)
|
90
|
+
num ^= decrypt_byte
|
91
|
+
update_keys(num.chr)
|
92
|
+
num
|
93
93
|
end
|
94
94
|
end
|
95
95
|
end
|
data/lib/zip/decompressor.rb
CHANGED
@@ -1,9 +1,27 @@
|
|
1
1
|
module Zip
|
2
2
|
class Decompressor #:nodoc:all
|
3
3
|
CHUNK_SIZE = 32_768
|
4
|
-
|
4
|
+
|
5
|
+
def self.decompressor_classes
|
6
|
+
@decompressor_classes ||= {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.register(compression_method, decompressor_class)
|
10
|
+
decompressor_classes[compression_method] = decompressor_class
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.find_by_compression_method(compression_method)
|
14
|
+
decompressor_classes[compression_method]
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :input_stream
|
18
|
+
attr_reader :decompressed_size
|
19
|
+
|
20
|
+
def initialize(input_stream, decompressed_size = nil)
|
5
21
|
super()
|
22
|
+
|
6
23
|
@input_stream = input_stream
|
24
|
+
@decompressed_size = decompressed_size
|
7
25
|
end
|
8
26
|
end
|
9
27
|
end
|
data/lib/zip/dos_time.rb
CHANGED
@@ -29,13 +29,18 @@ module Zip
|
|
29
29
|
to_i / 2 == other.to_i / 2
|
30
30
|
end
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
32
|
+
# Create a DOSTime instance from a vanilla Time instance.
|
33
|
+
def self.from_time(time)
|
34
|
+
local(time.year, time.month, time.day, time.hour, time.min, time.sec)
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.parse_binary_dos_format(bin_dos_date, bin_dos_time)
|
38
|
+
second = 2 * (0b11111 & bin_dos_time)
|
39
|
+
minute = (0b11111100000 & bin_dos_time) >> 5
|
40
|
+
hour = (0b1111100000000000 & bin_dos_time) >> 11
|
41
|
+
day = (0b11111 & bin_dos_date)
|
42
|
+
month = (0b111100000 & bin_dos_date) >> 5
|
43
|
+
year = ((0b1111111000000000 & bin_dos_date) >> 9) + 1980
|
39
44
|
begin
|
40
45
|
local(year, month, day, hour, minute, second)
|
41
46
|
end
|
data/lib/zip/entry.rb
CHANGED
@@ -34,7 +34,7 @@ module Zip
|
|
34
34
|
end
|
35
35
|
@follow_symlinks = false
|
36
36
|
|
37
|
-
@restore_times =
|
37
|
+
@restore_times = false
|
38
38
|
@restore_permissions = false
|
39
39
|
@restore_ownership = false
|
40
40
|
# BUG: need an extra field to support uid/gid's
|
@@ -48,6 +48,7 @@ module Zip
|
|
48
48
|
|
49
49
|
def check_name(name)
|
50
50
|
return unless name.start_with?('/')
|
51
|
+
|
51
52
|
raise ::Zip::EntryNameError, "Illegal ZipEntry name '#{name}', name must not start with /"
|
52
53
|
end
|
53
54
|
|
@@ -69,7 +70,15 @@ module Zip
|
|
69
70
|
@time = args[8] || ::Zip::DOSTime.now
|
70
71
|
|
71
72
|
@ftype = name_is_directory? ? :directory : :file
|
72
|
-
@extra = ::Zip::ExtraField.new(@extra.to_s) unless @extra.
|
73
|
+
@extra = ::Zip::ExtraField.new(@extra.to_s) unless @extra.kind_of?(::Zip::ExtraField)
|
74
|
+
end
|
75
|
+
|
76
|
+
def encrypted?
|
77
|
+
gp_flags & 1 == 1
|
78
|
+
end
|
79
|
+
|
80
|
+
def incomplete?
|
81
|
+
gp_flags & 8 == 8
|
73
82
|
end
|
74
83
|
|
75
84
|
def time
|
@@ -91,11 +100,12 @@ module Zip
|
|
91
100
|
@extra.create('UniversalTime')
|
92
101
|
end
|
93
102
|
(@extra['UniversalTime'] || @extra['NTFS']).mtime = value
|
94
|
-
@time
|
103
|
+
@time = value
|
95
104
|
end
|
96
105
|
|
97
106
|
def file_type_is?(type)
|
98
107
|
raise InternalError, "current filetype is unknown: #{inspect}" unless @ftype
|
108
|
+
|
99
109
|
@ftype == type
|
100
110
|
end
|
101
111
|
|
@@ -116,6 +126,7 @@ module Zip
|
|
116
126
|
def name_safe?
|
117
127
|
cleanpath = Pathname.new(@name).cleanpath
|
118
128
|
return false unless cleanpath.relative?
|
129
|
+
|
119
130
|
root = ::File::SEPARATOR
|
120
131
|
naive_expanded_path = ::File.join(root, cleanpath.to_s)
|
121
132
|
::File.absolute_path(cleanpath.to_s, root) == naive_expanded_path
|
@@ -145,6 +156,7 @@ module Zip
|
|
145
156
|
# that we didn't change the header size (and thus clobber file data or something)
|
146
157
|
def verify_local_header_size!
|
147
158
|
return if @local_header_size.nil?
|
159
|
+
|
148
160
|
new_size = calculate_local_header_size
|
149
161
|
raise Error, "local header size changed (#{@local_header_size} -> #{new_size})" if @local_header_size != new_size
|
150
162
|
end
|
@@ -163,19 +175,16 @@ module Zip
|
|
163
175
|
# is passed.
|
164
176
|
def extract(dest_path = nil, &block)
|
165
177
|
if dest_path.nil? && !name_safe?
|
166
|
-
|
178
|
+
warn "WARNING: skipped '#{@name}' as unsafe."
|
167
179
|
return self
|
168
180
|
end
|
169
181
|
|
170
182
|
dest_path ||= @name
|
171
183
|
block ||= proc { ::Zip.on_exists_proc }
|
172
184
|
|
173
|
-
|
174
|
-
__send__("create_#{@ftype}", dest_path, &block)
|
175
|
-
else
|
176
|
-
raise "unknown file type #{inspect}"
|
177
|
-
end
|
185
|
+
raise "unknown file type #{inspect}" unless directory? || file? || symlink?
|
178
186
|
|
187
|
+
__send__("create_#{@ftype}", dest_path, &block)
|
179
188
|
self
|
180
189
|
end
|
181
190
|
|
@@ -185,15 +194,15 @@ module Zip
|
|
185
194
|
|
186
195
|
class << self
|
187
196
|
def read_zip_short(io) # :nodoc:
|
188
|
-
io.read(2).
|
197
|
+
io.read(2).unpack1('v')
|
189
198
|
end
|
190
199
|
|
191
200
|
def read_zip_long(io) # :nodoc:
|
192
|
-
io.read(4).
|
201
|
+
io.read(4).unpack1('V')
|
193
202
|
end
|
194
203
|
|
195
204
|
def read_zip_64_long(io) # :nodoc:
|
196
|
-
io.read(8).
|
205
|
+
io.read(8).unpack1('Q<')
|
197
206
|
end
|
198
207
|
|
199
208
|
def read_c_dir_entry(io) #:nodoc:all
|
@@ -218,8 +227,6 @@ module Zip
|
|
218
227
|
end
|
219
228
|
end
|
220
229
|
|
221
|
-
public
|
222
|
-
|
223
230
|
def unpack_local_entry(buf)
|
224
231
|
@header_signature,
|
225
232
|
@version,
|
@@ -249,6 +256,7 @@ module Zip
|
|
249
256
|
unless @header_signature == ::Zip::LOCAL_ENTRY_SIGNATURE
|
250
257
|
raise ::Zip::Error, "Zip local header magic not found at location '#{local_header_offset}'"
|
251
258
|
end
|
259
|
+
|
252
260
|
set_time(@last_mod_date, @last_mod_time)
|
253
261
|
|
254
262
|
@name = io.read(@name_length)
|
@@ -261,13 +269,14 @@ module Zip
|
|
261
269
|
|
262
270
|
if extra && extra.bytesize != @extra_length
|
263
271
|
raise ::Zip::Error, 'Truncated local zip entry header'
|
272
|
+
end
|
273
|
+
|
274
|
+
if @extra.kind_of?(::Zip::ExtraField)
|
275
|
+
@extra.merge(extra) if extra
|
264
276
|
else
|
265
|
-
|
266
|
-
@extra.merge(extra) if extra
|
267
|
-
else
|
268
|
-
@extra = ::Zip::ExtraField.new(extra)
|
269
|
-
end
|
277
|
+
@extra = ::Zip::ExtraField.new(extra)
|
270
278
|
end
|
279
|
+
|
271
280
|
parse_zip64_extra(true)
|
272
281
|
@local_header_size = calculate_local_header_size
|
273
282
|
end
|
@@ -354,21 +363,24 @@ module Zip
|
|
354
363
|
|
355
364
|
def check_c_dir_entry_static_header_length(buf)
|
356
365
|
return if buf.bytesize == ::Zip::CDIR_ENTRY_STATIC_HEADER_LENGTH
|
366
|
+
|
357
367
|
raise Error, 'Premature end of file. Not enough data for zip cdir entry header'
|
358
368
|
end
|
359
369
|
|
360
370
|
def check_c_dir_entry_signature
|
361
371
|
return if header_signature == ::Zip::CENTRAL_DIRECTORY_ENTRY_SIGNATURE
|
372
|
+
|
362
373
|
raise Error, "Zip local header magic not found at location '#{local_header_offset}'"
|
363
374
|
end
|
364
375
|
|
365
376
|
def check_c_dir_entry_comment_size
|
366
377
|
return if @comment && @comment.bytesize == @comment_length
|
378
|
+
|
367
379
|
raise ::Zip::Error, 'Truncated cdir zip entry header'
|
368
380
|
end
|
369
381
|
|
370
382
|
def read_c_dir_extra_field(io)
|
371
|
-
if @extra.
|
383
|
+
if @extra.kind_of?(::Zip::ExtraField)
|
372
384
|
@extra.merge(io.read(@extra_length))
|
373
385
|
else
|
374
386
|
@extra = ::Zip::ExtraField.new(io.read(@extra_length))
|
@@ -402,20 +414,25 @@ module Zip
|
|
402
414
|
|
403
415
|
def get_extra_attributes_from_path(path) # :nodoc:
|
404
416
|
return if Zip::RUNNING_ON_WINDOWS
|
417
|
+
|
405
418
|
stat = file_stat(path)
|
406
419
|
@unix_uid = stat.uid
|
407
420
|
@unix_gid = stat.gid
|
408
421
|
@unix_perms = stat.mode & 0o7777
|
422
|
+
@time = ::Zip::DOSTime.from_time(stat.mtime)
|
409
423
|
end
|
410
424
|
|
411
|
-
def
|
412
|
-
# BUG: does not update timestamps into account
|
425
|
+
def set_unix_attributes_on_path(dest_path)
|
413
426
|
# ignore setuid/setgid bits by default. honor if @restore_ownership
|
414
427
|
unix_perms_mask = 0o1777
|
415
428
|
unix_perms_mask = 0o7777 if @restore_ownership
|
416
429
|
::FileUtils.chmod(@unix_perms & unix_perms_mask, dest_path) if @restore_permissions && @unix_perms
|
417
430
|
::FileUtils.chown(@unix_uid, @unix_gid, dest_path) if @restore_ownership && @unix_uid && @unix_gid && ::Process.egid == 0
|
418
|
-
|
431
|
+
|
432
|
+
# Restore the timestamp on a file. This will either have come from the
|
433
|
+
# original source file that was copied into the archive, or from the
|
434
|
+
# creation date of the archive if there was no original source file.
|
435
|
+
::FileUtils.touch(dest_path, mtime: time) if @restore_times
|
419
436
|
end
|
420
437
|
|
421
438
|
def set_extra_attributes_on_path(dest_path) # :nodoc:
|
@@ -423,7 +440,7 @@ module Zip
|
|
423
440
|
|
424
441
|
case @fstype
|
425
442
|
when ::Zip::FSTYPE_UNIX
|
426
|
-
|
443
|
+
set_unix_attributes_on_path(dest_path)
|
427
444
|
end
|
428
445
|
end
|
429
446
|
|
@@ -484,6 +501,7 @@ module Zip
|
|
484
501
|
|
485
502
|
def ==(other)
|
486
503
|
return false unless other.class == self.class
|
504
|
+
|
487
505
|
# Compares contents of local entry and exposed fields
|
488
506
|
keys_equal = %w[compression_method crc compressed_size size name extra filepath].all? do |k|
|
489
507
|
other.__send__(k.to_sym) == __send__(k.to_sym)
|
@@ -591,7 +609,7 @@ module Zip
|
|
591
609
|
def set_time(binary_dos_date, binary_dos_time)
|
592
610
|
@time = ::Zip::DOSTime.parse_binary_dos_format(binary_dos_date, binary_dos_time)
|
593
611
|
rescue ArgumentError
|
594
|
-
warn '
|
612
|
+
warn 'WARNING: invalid date/time in zip entry.' if ::Zip.warn_invalid_date
|
595
613
|
end
|
596
614
|
|
597
615
|
def create_file(dest_path, _continue_on_exists_proc = proc { Zip.continue_on_exists_proc })
|
@@ -601,30 +619,29 @@ module Zip
|
|
601
619
|
end
|
602
620
|
::File.open(dest_path, 'wb') do |os|
|
603
621
|
get_input_stream do |is|
|
604
|
-
set_extra_attributes_on_path(dest_path)
|
605
|
-
|
606
622
|
bytes_written = 0
|
607
623
|
warned = false
|
608
|
-
buf = ''
|
624
|
+
buf = +''
|
609
625
|
while (buf = is.sysread(::Zip::Decompressor::CHUNK_SIZE, buf))
|
610
626
|
os << buf
|
611
627
|
bytes_written += buf.bytesize
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
end
|
620
|
-
end
|
628
|
+
next unless bytes_written > size && !warned
|
629
|
+
|
630
|
+
message = "entry '#{name}' should be #{size}B, but is larger when inflated."
|
631
|
+
raise ::Zip::EntrySizeError, message if ::Zip.validate_entry_sizes
|
632
|
+
|
633
|
+
warn "WARNING: #{message}"
|
634
|
+
warned = true
|
621
635
|
end
|
622
636
|
end
|
623
637
|
end
|
638
|
+
|
639
|
+
set_extra_attributes_on_path(dest_path)
|
624
640
|
end
|
625
641
|
|
626
642
|
def create_directory(dest_path)
|
627
643
|
return if ::File.directory?(dest_path)
|
644
|
+
|
628
645
|
if ::File.exist?(dest_path)
|
629
646
|
if block_given? && yield(self, dest_path)
|
630
647
|
::FileUtils.rm_f dest_path
|
@@ -642,13 +659,14 @@ module Zip
|
|
642
659
|
def create_symlink(dest_path)
|
643
660
|
# TODO: Symlinks pose security challenges. Symlink support temporarily
|
644
661
|
# removed in view of https://github.com/rubyzip/rubyzip/issues/369 .
|
645
|
-
|
662
|
+
warn "WARNING: skipped symlink '#{dest_path}'."
|
646
663
|
end
|
647
664
|
|
648
665
|
# apply missing data from the zip64 extra information field, if present
|
649
666
|
# (required when file sizes exceed 2**32, but can be used for all files)
|
650
667
|
def parse_zip64_extra(for_local_header) #:nodoc:all
|
651
668
|
return if @extra['Zip64'].nil?
|
669
|
+
|
652
670
|
if for_local_header
|
653
671
|
@size, @compressed_size = @extra['Zip64'].parse(@size, @compressed_size)
|
654
672
|
else
|
@@ -663,6 +681,7 @@ module Zip
|
|
663
681
|
# create a zip64 extra information field if we need one
|
664
682
|
def prep_zip64_extra(for_local_header) #:nodoc:all
|
665
683
|
return unless ::Zip.write_zip64_support
|
684
|
+
|
666
685
|
need_zip64 = @size >= 0xFFFFFFFF || @compressed_size >= 0xFFFFFFFF
|
667
686
|
need_zip64 ||= @local_header_offset >= 0xFFFFFFFF unless for_local_header
|
668
687
|
if need_zip64
|