rubyzip 3.2.2 → 3.6.0
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 +53 -0
- data/LICENSE.md +1 -1
- data/README.md +48 -9
- data/lib/rubyzip.rb +3 -0
- data/lib/zip/central_directory.rb +32 -17
- data/lib/zip/crypto/aes_encryption.rb +5 -2
- data/lib/zip/crypto/decrypted_io.rb +12 -6
- data/lib/zip/crypto/traditional_encryption.rb +3 -1
- data/lib/zip/dos_time.rb +19 -1
- data/lib/zip/entry.rb +8 -8
- data/lib/zip/errors.rb +16 -0
- data/lib/zip/extra_field/old_unix.rb +2 -2
- data/lib/zip/extra_field/unix.rb +2 -2
- data/lib/zip/extra_field/zip64.rb +2 -2
- data/lib/zip/inflater.rb +5 -5
- data/lib/zip/input_stream.rb +25 -5
- data/lib/zip/ioextras/abstract_input_stream.rb +142 -60
- data/lib/zip/ioextras.rb +5 -2
- data/lib/zip/output_stream.rb +7 -1
- data/lib/zip/pass_thru_decompressor.rb +6 -6
- data/lib/zip/version.rb +1 -1
- data/lib/zip.rb +3 -1
- metadata +6 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8ad461af594aaafc83f2a9af47a7e72681431835864534c174970e2245748764
|
|
4
|
+
data.tar.gz: 6c5bd535a0b049a606bd64ca90106e633c385926d91c32ca3db72314d2b031f0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ecd014dee5b905fe5a85af4273e564a0abbdb9c75409a489363352999b2298bfa6baf311c4722c022ddd87807e402d2b9b03f54ec2c110a4cf8c882ed916d301
|
|
7
|
+
data.tar.gz: 7711a4b6bd467e46d020b9d36b58ad726c0ff312d2247e49c9030cbdfb53d206dd5e16ddbb265f34f3f9802c728de06517926de352db7fac8f43ceafd3a3f496
|
data/Changelog.md
CHANGED
|
@@ -1,3 +1,56 @@
|
|
|
1
|
+
# 3.6.0 (2026-09-01)
|
|
2
|
+
|
|
3
|
+
- Forward options from OutputStream.open without a block. [#674](https://github.com/rubyzip/rubyzip/pull/674)
|
|
4
|
+
- Clamp DOS date and time to the range the fields can hold. [#671](https://github.com/rubyzip/rubyzip/pull/671)
|
|
5
|
+
|
|
6
|
+
Tooling/internal:
|
|
7
|
+
|
|
8
|
+
- Unix owner ids no longer default to 0 (root).
|
|
9
|
+
- Legacy Unix owner ids no longer default to 0 (root).
|
|
10
|
+
- Handle short reads in `copy_stream_n`. [#675](https://github.com/rubyzip/rubyzip/pull/675)
|
|
11
|
+
- Retain required Zip64 offset fields. [#676](https://github.com/rubyzip/rubyzip/pull/676)
|
|
12
|
+
- Load openssl only when AES encryption is used. [#672](https://github.com/rubyzip/rubyzip/pull/672)
|
|
13
|
+
|
|
14
|
+
# 3.5.0 (2026-08-18)
|
|
15
|
+
|
|
16
|
+
- Fix the link to Ruby doc in README to the latest version. [#670](https://github.com/rubyzip/rubyzip/pull/670)
|
|
17
|
+
- Support passing decrypter to encrypted entry via get_input_stream. [#667](https://github.com/rubyzip/rubyzip/pull/667)
|
|
18
|
+
|
|
19
|
+
# 3.4.1 (2026-06-27)
|
|
20
|
+
|
|
21
|
+
- Fixed `DecryptedIo` to only perform integrity once. [#665](https://github.com/rubyzip/rubyzip/pull/665)
|
|
22
|
+
|
|
23
|
+
# 3.4.0 (2026-06-14)
|
|
24
|
+
|
|
25
|
+
- Prevent entries from being extracted outside specified directory. [#664](https://github.com/rubyzip/rubyzip/issues/664). Thanks to @connorshea for additional reporting on this.
|
|
26
|
+
- Use `SecureRandom` in place of insecure `Random`.
|
|
27
|
+
- Stop reading the central directory on first error.
|
|
28
|
+
- Add a check on number of declared entries in a zip file. Thanks to @connorshea for reporting this.
|
|
29
|
+
- Add note to README re reporting security issues privately.
|
|
30
|
+
- Add lib/rubyzip.rb for Bundler auto-require. [#660](https://github.com/rubyzip/rubyzip/pull/660)
|
|
31
|
+
|
|
32
|
+
Tooling/internal:
|
|
33
|
+
|
|
34
|
+
- Replace the test Excel spreadsheet fixture.
|
|
35
|
+
- Use `assert_silent` shorthand when expecting no output from a test.
|
|
36
|
+
- Clean up CentralDirectory instance variables.
|
|
37
|
+
- Add Ruby 4.0 to the Windows CI and update CI matrix in the README.
|
|
38
|
+
- List ZIP docs that we store here and link to online versions. [#657](https://github.com/rubyzip/rubyzip/issues/657)
|
|
39
|
+
|
|
40
|
+
# 3.3.1 (2026-05-30)
|
|
41
|
+
|
|
42
|
+
- Reinstate default param for `InputStream#sysread`. [#663](https://github.com/rubyzip/rubyzip/issues/663)
|
|
43
|
+
|
|
44
|
+
# 3.3.0 (2026-05-02)
|
|
45
|
+
|
|
46
|
+
- Refactor `InputStream` and `AbstractInputStream`.
|
|
47
|
+
[#661](https://github.com/rubyzip/rubyzip/pull/661)
|
|
48
|
+
|
|
49
|
+
Tooling/internal:
|
|
50
|
+
|
|
51
|
+
- Update Actions to use checkout@v5.
|
|
52
|
+
- Add Ruby4.0 to the CI matrix. [#659](https://github.com/rubyzip/rubyzip/pull/659)
|
|
53
|
+
|
|
1
54
|
# 3.2.2 (2025-11-02)
|
|
2
55
|
|
|
3
56
|
- Fix reading EOCDs when header signatures are in an Entry payload. [#656](https://github.com/rubyzip/rubyzip/issues/656)
|
data/LICENSE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
BSD 2-Clause License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2002-
|
|
3
|
+
Copyright (c) 2002-2026, The Rubyzip Developers
|
|
4
4
|
|
|
5
5
|
Redistribution and use in source and binary forms, with or without
|
|
6
6
|
modification, are permitted provided that the following conditions are met:
|
data/README.md
CHANGED
|
@@ -11,6 +11,10 @@ Rubyzip is a ruby library for reading and writing zip files.
|
|
|
11
11
|
|
|
12
12
|
## Important notes
|
|
13
13
|
|
|
14
|
+
### Reporting security issues with this library
|
|
15
|
+
|
|
16
|
+
If you think you have found a security issue with this library, please don't submit a public issue or PR. Email me directly at hainesr@gmail.com with as much information as you can provide - steps for replication are particularly helpful if you can - and we'll get it sorted ASAP. Thank you.
|
|
17
|
+
|
|
14
18
|
### Updating to version 3.0
|
|
15
19
|
|
|
16
20
|
The public API of some classes has been modernized to use named parameters for optional arguments. Please check your usage of the following Rubyzip classes:
|
|
@@ -40,9 +44,12 @@ gem install rubyzip
|
|
|
40
44
|
Or in your Gemfile:
|
|
41
45
|
|
|
42
46
|
```ruby
|
|
43
|
-
gem 'rubyzip'
|
|
47
|
+
gem 'rubyzip', require: 'zip' # For versions before 3.4.
|
|
48
|
+
gem 'rubyzip' # For version 3.4 and after.
|
|
44
49
|
```
|
|
45
50
|
|
|
51
|
+
From version 3.4 onwards, you can `require` either 'zip' or 'rubyzip' to use this library. Before version 3.4 you need to `require 'zip'` explicitly.
|
|
52
|
+
|
|
46
53
|
## Usage
|
|
47
54
|
|
|
48
55
|
### Basic zip archive creation
|
|
@@ -194,7 +201,7 @@ On Posix file systems the default file permissions applied to a new archive
|
|
|
194
201
|
are (0666 - umask), which mimics the behavior of standard tools such as `touch`.
|
|
195
202
|
|
|
196
203
|
On Windows the default file permissions are set to 0644 as suggested by the
|
|
197
|
-
[Ruby File documentation](
|
|
204
|
+
[Ruby File documentation](https://docs.ruby-lang.org/en/master/File.html#class-file-file-permissions).
|
|
198
205
|
|
|
199
206
|
When modifying a zip archive the file permissions of the archive are preserved.
|
|
200
207
|
|
|
@@ -294,6 +301,25 @@ Zip::InputStream.open(buffer, decrypter: dec) do |input|
|
|
|
294
301
|
end
|
|
295
302
|
```
|
|
296
303
|
|
|
304
|
+
#### Passing a decrypter to an individual `Entry`
|
|
305
|
+
|
|
306
|
+
You can also decrypt an `Entry` by passing a decrypter into `Zip::Entry#get_input_stream`. This allows you to decrypt files from a Zip file with different passwords.
|
|
307
|
+
|
|
308
|
+
```ruby
|
|
309
|
+
Zip::File.open('aes-encrypted-file.zip') do |zip|
|
|
310
|
+
entries = [['file1.txt', 'password1'], ['file2.txt', 'password2']]
|
|
311
|
+
|
|
312
|
+
entries.each do |entry_name, password|
|
|
313
|
+
entry = zip.find_entry(entry_name)
|
|
314
|
+
dec = Zip::AESDecrypter.new(password, Zip::AESEncryption::STRENGTH_256_BIT)
|
|
315
|
+
zis = entry.get_input_stream(decrypter: dec)
|
|
316
|
+
|
|
317
|
+
puts "Contents of '#{entry.name}':"
|
|
318
|
+
puts zis.read
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
```
|
|
322
|
+
|
|
297
323
|
_This is an evolving feature and the interface for encryption may change in future versions._
|
|
298
324
|
|
|
299
325
|
## Known issues
|
|
@@ -341,7 +367,7 @@ Zip.continue_on_exists_proc = true
|
|
|
341
367
|
|
|
342
368
|
### Non-ASCII Names
|
|
343
369
|
|
|
344
|
-
|
|
370
|
+
To store non-English filenames as UTF-8 (decoded correctly by Windows 7 or later and other modern tools), set this option:
|
|
345
371
|
|
|
346
372
|
```ruby
|
|
347
373
|
Zip.unicode_names = true
|
|
@@ -363,7 +389,19 @@ Some zip files might have an invalid date format, which will raise a warning. Yo
|
|
|
363
389
|
Zip.warn_invalid_date = false
|
|
364
390
|
```
|
|
365
391
|
|
|
366
|
-
###
|
|
392
|
+
### Validating Declared Number of Entries
|
|
393
|
+
|
|
394
|
+
When reading a zip file it is potentially dangerous to trust what it tells you about how many entries it contains. A malformed zip file could claim a high number of entries in an attempt to waste internal resources, or crash the processing application.
|
|
395
|
+
|
|
396
|
+
By default rubyzip will warn if the number of declared entries is impossible based on the actual size of the Central Directory headers. You can set this check to raise an error:
|
|
397
|
+
|
|
398
|
+
```ruby
|
|
399
|
+
Zip.validate_declared_number_of_entries = true
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
It is likely that the default behaviour for this check will be changed to raise an error in version 4.
|
|
403
|
+
|
|
404
|
+
### Entry Size Validation
|
|
367
405
|
|
|
368
406
|
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:
|
|
369
407
|
|
|
@@ -445,11 +483,12 @@ Rubyzip 2.4 is known to work on MRI 2.4 to 3.4 on Linux and Mac, and JRuby and T
|
|
|
445
483
|
|
|
446
484
|
Please see the table below for what we think the current situation is. Note: an empty cell means "unknown", not "does not work".
|
|
447
485
|
|
|
448
|
-
| OS/Ruby | 3.0 | 3.1 | 3.2 | 3.3 | 3.4 | Head | JRuby 10.0.1.0 | JRuby Head | Truffleruby
|
|
449
|
-
|
|
450
|
-
|Ubuntu 24.04| CI | CI | CI | CI | CI | ci | CI | ci | CI | ci |
|
|
451
|
-
|Mac OS
|
|
452
|
-
|Windows Server 2022| CI | | | |
|
|
486
|
+
| OS/Ruby | 3.0 | 3.1 | 3.2 | 3.3 | 3.4 | 4.0 | Head | JRuby 10.0.1.0 | JRuby Head | Truffleruby 34.0.1 | Truffleruby Head |
|
|
487
|
+
|---------|-----|-----|-----|-----|-----|-----|------|----------------|------------|-------------------|------------------|
|
|
488
|
+
|Ubuntu 24.04| CI | CI | CI | CI | CI | CI | ci | CI | ci | CI | ci |
|
|
489
|
+
|Mac OS 15.7.7| CI | x | x | CI | CI | CI | ci | x | | x | |
|
|
490
|
+
|Windows Server 2022| CI | | | | | | | | | | |
|
|
491
|
+
|Windows Server 2025| | | | CI | | CI | CI mswin</br>CI ucrt | | | | |
|
|
453
492
|
|
|
454
493
|
Key: `CI` - tested in CI, should work; `ci` - tested in CI, might fail; `x` - known working; `o` - known failing.
|
|
455
494
|
|
data/lib/rubyzip.rb
ADDED
|
@@ -114,18 +114,20 @@ module Zip
|
|
|
114
114
|
|
|
115
115
|
def unpack_64_e_o_c_d(buffer) # :nodoc:
|
|
116
116
|
_, # ZIP64_END_OF_CD_SIG. We know we have this at this point.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
117
|
+
size_of_zip64_e_o_c_d,
|
|
118
|
+
_version_made_by,
|
|
119
|
+
_version_needed_for_extract,
|
|
120
|
+
_number_of_this_disk,
|
|
121
|
+
_number_of_disk_with_start_of_cdir,
|
|
122
|
+
_total_number_of_entries_in_cdir_on_this_disk,
|
|
123
123
|
@size,
|
|
124
|
-
|
|
124
|
+
size_in_bytes,
|
|
125
125
|
@cdir_offset = buffer.unpack('VQ<vvVVQ<Q<Q<Q<')
|
|
126
126
|
|
|
127
|
+
validate_size!(@size, size_in_bytes)
|
|
128
|
+
|
|
127
129
|
zip64_extensible_data_size =
|
|
128
|
-
|
|
130
|
+
size_of_zip64_e_o_c_d - ZIP64_STATIC_EOCD_SIZE + 12
|
|
129
131
|
@zip64_extensible_data = if zip64_extensible_data_size.zero?
|
|
130
132
|
''
|
|
131
133
|
else
|
|
@@ -145,25 +147,29 @@ module Zip
|
|
|
145
147
|
|
|
146
148
|
# Unpack the EOCD and return a boolean indicating whether this header is
|
|
147
149
|
# complete without needing Zip64 extensions.
|
|
148
|
-
def unpack_e_o_c_d(buffer) # :nodoc:
|
|
150
|
+
def unpack_e_o_c_d(buffer) # :nodoc:
|
|
149
151
|
_, # END_OF_CD_SIG. We know we have this at this point.
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
152
|
+
number_of_this_disk,
|
|
153
|
+
number_of_disk_with_start_of_cdir,
|
|
154
|
+
total_number_of_entries_in_cdir_on_this_disk,
|
|
153
155
|
@size,
|
|
154
|
-
|
|
156
|
+
size_in_bytes,
|
|
155
157
|
@cdir_offset,
|
|
156
158
|
comment_length = buffer.unpack('VvvvvVVv')
|
|
157
159
|
|
|
160
|
+
complete = !([number_of_this_disk, number_of_disk_with_start_of_cdir,
|
|
161
|
+
total_number_of_entries_in_cdir_on_this_disk, @size].any?(0xFFFF) ||
|
|
162
|
+
size_in_bytes == 0xFFFFFFFF || @cdir_offset == 0xFFFFFFFF)
|
|
163
|
+
|
|
164
|
+
validate_size!(@size, size_in_bytes) if complete
|
|
165
|
+
|
|
158
166
|
@comment = if comment_length.positive?
|
|
159
167
|
buffer.slice(STATIC_EOCD_SIZE, comment_length)
|
|
160
168
|
else
|
|
161
169
|
''
|
|
162
170
|
end
|
|
163
171
|
|
|
164
|
-
|
|
165
|
-
@total_number_of_entries_in_cdir_on_this_disk, @size].any?(0xFFFF) ||
|
|
166
|
-
@size_in_bytes == 0xFFFFFFFF || @cdir_offset == 0xFFFFFFFF)
|
|
172
|
+
complete
|
|
167
173
|
end
|
|
168
174
|
|
|
169
175
|
def read_central_directory_entries(io) # :nodoc:
|
|
@@ -180,7 +186,7 @@ module Zip
|
|
|
180
186
|
@entry_set = EntrySet.new
|
|
181
187
|
@size.times do
|
|
182
188
|
entry = Entry.read_c_dir_entry(io)
|
|
183
|
-
|
|
189
|
+
break unless entry
|
|
184
190
|
|
|
185
191
|
offset = if entry.zip64?
|
|
186
192
|
entry.extra[:zip64].relative_header_offset
|
|
@@ -258,6 +264,15 @@ module Zip
|
|
|
258
264
|
|
|
259
265
|
[io.tell, io.read]
|
|
260
266
|
end
|
|
267
|
+
|
|
268
|
+
def validate_size!(size, size_in_bytes)
|
|
269
|
+
return if size * CDIR_ENTRY_STATIC_HEADER_LENGTH <= size_in_bytes
|
|
270
|
+
|
|
271
|
+
error = EntryNumberMismatchError.new(size, size_in_bytes)
|
|
272
|
+
raise error if Zip.validate_declared_number_of_entries
|
|
273
|
+
|
|
274
|
+
warn error.message
|
|
275
|
+
end
|
|
261
276
|
end
|
|
262
277
|
end
|
|
263
278
|
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'openssl'
|
|
4
|
-
|
|
5
3
|
module Zip
|
|
6
4
|
module AESEncryption # :nodoc:
|
|
7
5
|
VERIFIER_LENGTH = 2
|
|
@@ -45,6 +43,11 @@ module Zip
|
|
|
45
43
|
}.freeze
|
|
46
44
|
|
|
47
45
|
def initialize(password, strength)
|
|
46
|
+
# Loaded here rather than at the top of the file so that `require 'zip'`
|
|
47
|
+
# does not pull in openssl. Only AES-encrypted archives need it, and it
|
|
48
|
+
# is the single largest cost of loading this library.
|
|
49
|
+
require 'openssl'
|
|
50
|
+
|
|
48
51
|
@password = password
|
|
49
52
|
@strength = strength
|
|
50
53
|
@bits = BITS[@strength]
|
|
@@ -8,21 +8,27 @@ module Zip
|
|
|
8
8
|
@io = io
|
|
9
9
|
@decrypter = decrypter
|
|
10
10
|
@bytes_remaining = compressed_size
|
|
11
|
-
@buffer = +''
|
|
11
|
+
@buffer = +''.b
|
|
12
|
+
# Ensures that integrity is only checked once,
|
|
13
|
+
# even if read is called multiple times after the input has finished.
|
|
14
|
+
@integrity_verified = false
|
|
12
15
|
end
|
|
13
16
|
|
|
14
|
-
def read(
|
|
15
|
-
return (
|
|
17
|
+
def read(maxlen = nil)
|
|
18
|
+
return (maxlen.nil? || maxlen.zero? ? '' : nil) if eof?
|
|
16
19
|
|
|
17
|
-
while
|
|
20
|
+
while maxlen.nil? || (@buffer.bytesize < maxlen)
|
|
18
21
|
break if input_finished?
|
|
19
22
|
|
|
20
23
|
@buffer << produce_input
|
|
21
24
|
end
|
|
22
25
|
|
|
23
|
-
|
|
26
|
+
if input_finished? && !@integrity_verified
|
|
27
|
+
@decrypter.check_integrity!(@io)
|
|
28
|
+
@integrity_verified = true
|
|
29
|
+
end
|
|
24
30
|
|
|
25
|
-
|
|
31
|
+
@buffer.slice!(0...(maxlen || @buffer.bytesize))
|
|
26
32
|
end
|
|
27
33
|
|
|
28
34
|
private
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
|
|
3
5
|
module Zip
|
|
4
6
|
module TraditionalEncryption # :nodoc:
|
|
5
7
|
def initialize(password)
|
|
@@ -44,7 +46,7 @@ module Zip
|
|
|
44
46
|
def header(mtime)
|
|
45
47
|
[].tap do |header|
|
|
46
48
|
(header_bytesize - 2).times do
|
|
47
|
-
header <<
|
|
49
|
+
header << SecureRandom.rand(0..255)
|
|
48
50
|
end
|
|
49
51
|
header << (mtime.to_binary_dos_time & 0xff)
|
|
50
52
|
header << (mtime.to_binary_dos_time >> 8)
|
data/lib/zip/dos_time.rb
CHANGED
|
@@ -16,6 +16,18 @@ module Zip
|
|
|
16
16
|
# bits 5-8 month (1-12)
|
|
17
17
|
# bits 9-15 year (four digit year minus 1980)
|
|
18
18
|
|
|
19
|
+
# The MS-DOS date field is seven bits of "year minus 1980", so only
|
|
20
|
+
# 1980-01-01 00:00:00 through 2107-12-31 23:59:58 can be represented.
|
|
21
|
+
# Times outside that are clamped rather than allowed to wrap: the real
|
|
22
|
+
# time is still carried in the universal time (UT) extra field, but the
|
|
23
|
+
# DOS fields have to stay in range for readers that only look at those.
|
|
24
|
+
DOS_EPOCH_YEAR = 1980
|
|
25
|
+
DOS_MAX_YEAR = 2107
|
|
26
|
+
DOS_MIN_DATE = 0x0021 # 1980-01-01
|
|
27
|
+
DOS_MAX_DATE = 0xff9f # 2107-12-31
|
|
28
|
+
DOS_MIN_TIME = 0x0000 # 00:00:00
|
|
29
|
+
DOS_MAX_TIME = 0xbf7d # 23:59:58
|
|
30
|
+
|
|
19
31
|
attr_writer :absolute_time # :nodoc:
|
|
20
32
|
|
|
21
33
|
def absolute_time?
|
|
@@ -25,15 +37,21 @@ module Zip
|
|
|
25
37
|
end
|
|
26
38
|
|
|
27
39
|
def to_binary_dos_time
|
|
40
|
+
return DOS_MIN_TIME if year < DOS_EPOCH_YEAR
|
|
41
|
+
return DOS_MAX_TIME if year > DOS_MAX_YEAR
|
|
42
|
+
|
|
28
43
|
(sec / 2) +
|
|
29
44
|
(min << 5) +
|
|
30
45
|
(hour << 11)
|
|
31
46
|
end
|
|
32
47
|
|
|
33
48
|
def to_binary_dos_date
|
|
49
|
+
return DOS_MIN_DATE if year < DOS_EPOCH_YEAR
|
|
50
|
+
return DOS_MAX_DATE if year > DOS_MAX_YEAR
|
|
51
|
+
|
|
34
52
|
day +
|
|
35
53
|
(month << 5) +
|
|
36
|
-
((year -
|
|
54
|
+
((year - DOS_EPOCH_YEAR) << 9)
|
|
37
55
|
end
|
|
38
56
|
|
|
39
57
|
# Deprecated. Remove for version 4.
|
data/lib/zip/entry.rb
CHANGED
|
@@ -290,7 +290,7 @@ module Zip
|
|
|
290
290
|
dest_dir = ::File.absolute_path(destination_directory || '.')
|
|
291
291
|
extract_path = ::File.absolute_path(::File.join(dest_dir, entry_path))
|
|
292
292
|
|
|
293
|
-
unless extract_path.start_with?(dest_dir)
|
|
293
|
+
unless extract_path.start_with?(dest_dir + ::File::SEPARATOR) || dest_dir == ::File::SEPARATOR
|
|
294
294
|
warn "WARNING: skipped extracting '#{@name}' to '#{extract_path}' as unsafe."
|
|
295
295
|
return self
|
|
296
296
|
end
|
|
@@ -638,8 +638,9 @@ module Zip
|
|
|
638
638
|
end
|
|
639
639
|
|
|
640
640
|
# Returns an IO like object for the given ZipEntry.
|
|
641
|
+
# Optional decrypter can be provided for an encrypted entry
|
|
641
642
|
# Warning: may behave weird with symlinks.
|
|
642
|
-
def get_input_stream(&block)
|
|
643
|
+
def get_input_stream(decrypter: nil, &block)
|
|
643
644
|
if ftype == :directory
|
|
644
645
|
yield ::Zip::NullInputStream if block
|
|
645
646
|
::Zip::NullInputStream
|
|
@@ -656,7 +657,7 @@ module Zip
|
|
|
656
657
|
raise "unknown @file_type #{ftype}"
|
|
657
658
|
end
|
|
658
659
|
else
|
|
659
|
-
zis = ::Zip::InputStream.new(@zipfile, offset: local_header_offset)
|
|
660
|
+
zis = ::Zip::InputStream.new(@zipfile, offset: local_header_offset, decrypter: decrypter)
|
|
660
661
|
zis.instance_variable_set(:@complete_entry, self)
|
|
661
662
|
zis.get_next_entry
|
|
662
663
|
if block
|
|
@@ -751,21 +752,20 @@ module Zip
|
|
|
751
752
|
|
|
752
753
|
def create_file(dest_path, _continue_on_exists_proc = proc { Zip.continue_on_exists_proc })
|
|
753
754
|
if ::File.exist?(dest_path) && !yield(self, dest_path)
|
|
754
|
-
raise
|
|
755
|
+
raise DestinationExistsError, dest_path
|
|
755
756
|
end
|
|
756
757
|
|
|
757
758
|
::File.open(dest_path, 'wb') do |os|
|
|
758
759
|
get_input_stream do |is|
|
|
759
760
|
bytes_written = 0
|
|
760
761
|
warned = false
|
|
761
|
-
buf =
|
|
762
|
-
while (buf = is.sysread(::Zip::Decompressor::CHUNK_SIZE, buf))
|
|
762
|
+
while (buf = is.sysread(Decompressor::CHUNK_SIZE))
|
|
763
763
|
os << buf
|
|
764
764
|
bytes_written += buf.bytesize
|
|
765
765
|
next unless bytes_written > size && !warned
|
|
766
766
|
|
|
767
|
-
error =
|
|
768
|
-
raise error if
|
|
767
|
+
error = EntrySizeError.new(self)
|
|
768
|
+
raise error if Zip.validate_entry_sizes
|
|
769
769
|
|
|
770
770
|
warn "WARNING: #{error.message}"
|
|
771
771
|
warned = true
|
data/lib/zip/errors.rb
CHANGED
|
@@ -109,6 +109,22 @@ module Zip
|
|
|
109
109
|
end
|
|
110
110
|
end
|
|
111
111
|
|
|
112
|
+
class EntryNumberMismatchError < Error
|
|
113
|
+
# Create a new EntryNumberMismatchError with the specified size and size in bytes.
|
|
114
|
+
def initialize(size, size_in_bytes)
|
|
115
|
+
super()
|
|
116
|
+
@size = size
|
|
117
|
+
@size_in_bytes = size_in_bytes
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# The message returned by this error.
|
|
121
|
+
def message
|
|
122
|
+
"Zip consistency problem: an impossibly high number of entries (#{@size}) " \
|
|
123
|
+
'is declared in this file, compared to the size of the central directory ' \
|
|
124
|
+
"(#{@size_in_bytes} bytes)."
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
112
128
|
# Error raised if a split archive is read. Rubyzip does not support reading
|
|
113
129
|
# split archives.
|
|
114
130
|
class SplitArchiveError < Error
|
data/lib/zip/extra_field/unix.rb
CHANGED
|
@@ -59,8 +59,8 @@ module Zip
|
|
|
59
59
|
# We can suppress the zip64 extra field unless we know the size is large or
|
|
60
60
|
# the relative header offset is large (for central directory entries).
|
|
61
61
|
def suppress?
|
|
62
|
-
!(@original_size && @original_size >= 0xFFFFFFFF) ||
|
|
63
|
-
(@relative_header_offset && @relative_header_offset >= 0xFFFFFFFF)
|
|
62
|
+
!((@original_size && @original_size >= 0xFFFFFFFF) ||
|
|
63
|
+
(@relative_header_offset && @relative_header_offset >= 0xFFFFFFFF))
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
def pack_for_local
|
data/lib/zip/inflater.rb
CHANGED
|
@@ -5,20 +5,20 @@ module Zip
|
|
|
5
5
|
def initialize(*args)
|
|
6
6
|
super
|
|
7
7
|
|
|
8
|
-
@buffer = +''
|
|
8
|
+
@buffer = +''.b
|
|
9
9
|
@zlib_inflater = ::Zlib::Inflate.new(-Zlib::MAX_WBITS)
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
def read(
|
|
13
|
-
return (
|
|
12
|
+
def read(maxlen = nil)
|
|
13
|
+
return (maxlen.nil? || maxlen.zero? ? '' : nil) if eof?
|
|
14
14
|
|
|
15
|
-
while
|
|
15
|
+
while maxlen.nil? || (@buffer.bytesize < maxlen)
|
|
16
16
|
break if input_finished?
|
|
17
17
|
|
|
18
18
|
@buffer << produce_input
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
@buffer.slice!(0...(maxlen || @buffer.bytesize))
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def eof?
|
data/lib/zip/input_stream.rb
CHANGED
|
@@ -89,9 +89,29 @@ module Zip
|
|
|
89
89
|
open_entry
|
|
90
90
|
end
|
|
91
91
|
|
|
92
|
-
#
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
# Modelled after IO#sysread.
|
|
93
|
+
#
|
|
94
|
+
# Reads up to maxlen bytes from the stream; returns a string
|
|
95
|
+
# (either a new string or the given out_string).
|
|
96
|
+
# Its encoding is the unchanged encoding of out_string, if out_string is
|
|
97
|
+
# given; ASCII-8BIT, otherwise. Output contains maxlen bytes from the
|
|
98
|
+
# stream, if available; otherwise contains all available bytes, if any
|
|
99
|
+
# available; otherwise is an empty string.
|
|
100
|
+
#
|
|
101
|
+
# This method should not be used with buffered input stream-reader methods,
|
|
102
|
+
# such as #read, #readline, #gets.
|
|
103
|
+
def sysread(maxlen = nil, out_string = nil)
|
|
104
|
+
# Remove the default value for maxlen for version 4.
|
|
105
|
+
return (maxlen.nil? || maxlen.zero? ? '' : nil) if eof?
|
|
106
|
+
|
|
107
|
+
output = produce_input(maxlen)
|
|
108
|
+
|
|
109
|
+
if out_string.nil?
|
|
110
|
+
output.force_encoding(Encoding::ASCII_8BIT)
|
|
111
|
+
else
|
|
112
|
+
encoding = out_string.encoding
|
|
113
|
+
out_string.replace(output).force_encoding(encoding)
|
|
114
|
+
end
|
|
95
115
|
end
|
|
96
116
|
|
|
97
117
|
# Returns the size of the current entry, or `nil` if there isn't one.
|
|
@@ -197,8 +217,8 @@ module Zip
|
|
|
197
217
|
decompressor_class.new(io, decompressed_size)
|
|
198
218
|
end
|
|
199
219
|
|
|
200
|
-
def produce_input # :nodoc:
|
|
201
|
-
@decompressor.read(
|
|
220
|
+
def produce_input(maxlen = CHUNK_SIZE) # :nodoc:
|
|
221
|
+
@decompressor.read(maxlen)
|
|
202
222
|
end
|
|
203
223
|
|
|
204
224
|
def input_finished? # :nodoc:
|
|
@@ -3,126 +3,208 @@
|
|
|
3
3
|
module Zip
|
|
4
4
|
module IOExtras # :nodoc:
|
|
5
5
|
# Implements many of the convenience methods of IO
|
|
6
|
-
# such as gets, getc, readline and readlines
|
|
6
|
+
# such as gets, getc, read, readline and readlines
|
|
7
7
|
# depends on: input_finished?, produce_input and read
|
|
8
|
-
module AbstractInputStream
|
|
8
|
+
module AbstractInputStream
|
|
9
9
|
include Enumerable
|
|
10
10
|
include FakeIO
|
|
11
11
|
|
|
12
|
-
def initialize
|
|
12
|
+
def initialize # :nodoc:
|
|
13
13
|
super
|
|
14
14
|
@lineno = 0
|
|
15
15
|
@pos = 0
|
|
16
|
-
@output_buffer = +''
|
|
16
|
+
@output_buffer = +''.b
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
+
# Returns (or sets) the current line number in the decompressed
|
|
20
|
+
# (possibly decrypted) data stream. See the Line Number documentation
|
|
21
|
+
# for the IO class for more information.
|
|
19
22
|
attr_accessor :lineno
|
|
23
|
+
|
|
24
|
+
# Returns the current position (in bytes) in the decompressed (possibly
|
|
25
|
+
# decrypted) data stream.
|
|
20
26
|
attr_reader :pos
|
|
21
27
|
|
|
22
|
-
|
|
28
|
+
# Reads bytes from the stream decompressed (possibly decrypted) data
|
|
29
|
+
# stream. If `maxlen` is `nil`, reads all bytes; otherwise, reads up to
|
|
30
|
+
# `maxlen` bytes. If `maxlen` is zero, returns an empty string.
|
|
31
|
+
#
|
|
32
|
+
# Returns a string (either a new string or the given `out_string`)
|
|
33
|
+
# containing the bytes read. The string's encoding is the unchanged
|
|
34
|
+
# encoding of `out_string`, if `out_string` is given; `ASCII-8BIT`,
|
|
35
|
+
# otherwise.
|
|
36
|
+
def read(maxlen = nil, out_string = nil) # rubocop:disable Metrics/PerceivedComplexity
|
|
37
|
+
return (maxlen.nil? || maxlen.zero? ? '' : nil) if eof?
|
|
38
|
+
|
|
23
39
|
tbuf = if @output_buffer.bytesize > 0
|
|
24
|
-
if
|
|
25
|
-
@output_buffer.slice!(0,
|
|
40
|
+
if maxlen && maxlen <= @output_buffer.bytesize
|
|
41
|
+
@output_buffer.slice!(0, maxlen)
|
|
26
42
|
else
|
|
27
|
-
|
|
28
|
-
rbuf =
|
|
43
|
+
maxlen -= @output_buffer.bytesize if maxlen
|
|
44
|
+
rbuf = produce_input(maxlen)
|
|
29
45
|
out = @output_buffer
|
|
30
46
|
out << rbuf if rbuf
|
|
31
|
-
@output_buffer = ''
|
|
47
|
+
@output_buffer = +''.b
|
|
32
48
|
out
|
|
33
49
|
end
|
|
34
50
|
else
|
|
35
|
-
|
|
51
|
+
produce_input(maxlen)
|
|
36
52
|
end
|
|
37
53
|
|
|
38
54
|
if tbuf.nil? || tbuf.empty?
|
|
39
|
-
return nil if
|
|
55
|
+
return nil if maxlen&.positive?
|
|
40
56
|
|
|
41
57
|
return ''
|
|
42
58
|
end
|
|
43
59
|
|
|
44
60
|
@pos += tbuf.length
|
|
45
61
|
|
|
46
|
-
if
|
|
47
|
-
|
|
62
|
+
if out_string.nil?
|
|
63
|
+
tbuf.force_encoding(Encoding::ASCII_8BIT)
|
|
48
64
|
else
|
|
49
|
-
|
|
65
|
+
encoding = out_string.encoding
|
|
66
|
+
out_string.replace(tbuf).force_encoding(encoding)
|
|
50
67
|
end
|
|
51
|
-
buf
|
|
52
68
|
end
|
|
53
69
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
70
|
+
# Reads and returns all remaining lines from the stream. See the Line IO
|
|
71
|
+
# documentation in the IO class for more information.
|
|
72
|
+
#
|
|
73
|
+
# With no arguments given, returns lines as determined by line
|
|
74
|
+
# separator `$/`, or `nil` if none.
|
|
75
|
+
#
|
|
76
|
+
# With only string argument `sep` given, returns lines as
|
|
77
|
+
# determined by line separator `sep`, or `nil` if none. See the
|
|
78
|
+
# Line Separator documentation in the IO class for more information.
|
|
79
|
+
# The two special values for `sep` (`nil` and `""`) are honoured.
|
|
80
|
+
#
|
|
81
|
+
# With only integer argument `limit` given, limits the number of bytes
|
|
82
|
+
# in each line; see the Line Limit documentation in the IO class for more
|
|
83
|
+
# information.
|
|
84
|
+
#
|
|
85
|
+
# With arguments `sep` and `limit` given, combines the two behaviors.
|
|
86
|
+
#
|
|
87
|
+
# Optional keyword argument `chomp` specifies whether line separators
|
|
88
|
+
# are to be omitted.
|
|
89
|
+
def readlines(sep = $INPUT_RECORD_SEPARATOR, limit = nil, chomp: false)
|
|
90
|
+
each(sep, limit, chomp: chomp).to_a
|
|
58
91
|
end
|
|
59
92
|
|
|
60
|
-
|
|
61
|
-
|
|
93
|
+
# Reads and returns a line from the stream. See the Line IO
|
|
94
|
+
# documentation in the IO class for more information.
|
|
95
|
+
#
|
|
96
|
+
# With no arguments given, returns the next line as determined by line
|
|
97
|
+
# separator `$/`, or `nil` if none.
|
|
98
|
+
#
|
|
99
|
+
# With only string argument `sep` given, returns the next line as
|
|
100
|
+
# determined by line separator `sep`, or `nil` if none. See the
|
|
101
|
+
# Line Separator documentation in the IO class for more information.
|
|
102
|
+
# The two special values for `sep` (`nil` and `""`) are honoured.
|
|
103
|
+
#
|
|
104
|
+
# With only integer argument `limit` given, limits the number of bytes
|
|
105
|
+
# in the line; see the Line Limit documentation in the IO class for more
|
|
106
|
+
# information.
|
|
107
|
+
#
|
|
108
|
+
# With arguments `sep` and `limit` given, combines the two behaviors.
|
|
109
|
+
#
|
|
110
|
+
# Optional keyword argument `chomp` specifies whether line separators
|
|
111
|
+
# are to be omitted.
|
|
112
|
+
def gets(sep = $INPUT_RECORD_SEPARATOR, limit = nil, chomp: false)
|
|
113
|
+
if sep.nil?
|
|
114
|
+
return nil if eof?
|
|
115
|
+
|
|
116
|
+
@lineno = @lineno.next
|
|
117
|
+
return read(limit)
|
|
118
|
+
end
|
|
62
119
|
|
|
63
|
-
if
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
elsif
|
|
67
|
-
|
|
68
|
-
a_sep_string = $INPUT_RECORD_SEPARATOR
|
|
69
|
-
else
|
|
70
|
-
number_of_bytes = nil
|
|
71
|
-
a_sep_string = a_sep_string.to_str if a_sep_string
|
|
120
|
+
if sep.respond_to?(:to_int)
|
|
121
|
+
limit = sep.to_int
|
|
122
|
+
sep = $INPUT_RECORD_SEPARATOR
|
|
123
|
+
elsif sep&.empty?
|
|
124
|
+
sep = "#{$INPUT_RECORD_SEPARATOR}#{$INPUT_RECORD_SEPARATOR}"
|
|
72
125
|
end
|
|
73
126
|
|
|
74
|
-
|
|
127
|
+
buffer_index = 0
|
|
128
|
+
while (sep_index = @output_buffer.index(sep, buffer_index)).nil?
|
|
129
|
+
break if limit && @output_buffer.bytesize >= limit
|
|
75
130
|
|
|
76
|
-
|
|
131
|
+
if input_finished?
|
|
132
|
+
return nil if @output_buffer.empty?
|
|
77
133
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
return @output_buffer.empty? ? nil : flush if input_finished?
|
|
134
|
+
@lineno = @lineno.next
|
|
135
|
+
@pos += @output_buffer.bytesize
|
|
136
|
+
return @output_buffer.slice!(0..)
|
|
137
|
+
end
|
|
83
138
|
|
|
139
|
+
buffer_index = [buffer_index, @output_buffer.bytesize - sep.bytesize].max
|
|
84
140
|
@output_buffer << produce_input
|
|
85
|
-
over_limit = number_of_bytes && @output_buffer.bytesize >= number_of_bytes
|
|
86
141
|
end
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
@pos +=
|
|
92
|
-
@output_buffer.slice!(0
|
|
142
|
+
|
|
143
|
+
limit ||= @output_buffer.bytesize
|
|
144
|
+
cut_index = sep_index ? [sep_index + sep.bytesize, limit].min : limit
|
|
145
|
+
@lineno = @lineno.next
|
|
146
|
+
@pos += cut_index
|
|
147
|
+
chomp ? @output_buffer.slice!(0, cut_index).chomp(sep) : @output_buffer.slice!(0, cut_index)
|
|
93
148
|
end
|
|
94
149
|
|
|
95
|
-
def ungetc(byte)
|
|
150
|
+
def ungetc(byte) # :nodoc:
|
|
96
151
|
@output_buffer = byte.chr + @output_buffer
|
|
97
152
|
end
|
|
98
153
|
|
|
99
|
-
def flush
|
|
100
|
-
|
|
101
|
-
@output_buffer = +''
|
|
102
|
-
ret_val
|
|
154
|
+
def flush # :nodoc:
|
|
155
|
+
@output_buffer.slice!(0..)
|
|
103
156
|
end
|
|
104
157
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
158
|
+
# Reads a line as with #gets, but raises `EOFError` if already at
|
|
159
|
+
# end-of-stream.
|
|
160
|
+
#
|
|
161
|
+
# Optional keyword argument `chomp` specifies whether line separators
|
|
162
|
+
# are to be omitted.
|
|
163
|
+
def readline(sep = $INPUT_RECORD_SEPARATOR, limit = nil, chomp: false)
|
|
164
|
+
raise EOFError if eof?
|
|
108
165
|
|
|
109
|
-
|
|
166
|
+
gets(sep, limit, chomp: chomp)
|
|
110
167
|
end
|
|
111
168
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
169
|
+
# Calls the block with each remaining line read from the stream.
|
|
170
|
+
# Does nothing if already at end-of-stream. See the Line IO
|
|
171
|
+
# documentation in the IO class for more information.
|
|
172
|
+
#
|
|
173
|
+
# With no arguments given, reads lines as determined by line separator
|
|
174
|
+
# `$/`. With only string argument `sep` given, reads lines as determined
|
|
175
|
+
# by line separator `sep`. See the Line Separator documentation in the
|
|
176
|
+
# IO class for more information. The two special values for `sep`
|
|
177
|
+
# (`nil` and `""`) are honoured.
|
|
178
|
+
#
|
|
179
|
+
# With only integer argument `limit` given, limits the number of bytes
|
|
180
|
+
# in each line; see the Line Limit documentation in the IO class for
|
|
181
|
+
# more information.
|
|
182
|
+
#
|
|
183
|
+
# With arguments `sep` and `limit` given, combines the two behaviors.
|
|
184
|
+
#
|
|
185
|
+
# Optional keyword argument `chomp` specifies whether line separators
|
|
186
|
+
# are to be omitted.
|
|
187
|
+
#
|
|
188
|
+
# Returns an `Enumerator` if no block is given.
|
|
189
|
+
def each(sep = $INPUT_RECORD_SEPARATOR, limit = nil, chomp: false)
|
|
190
|
+
return to_enum(:each, sep, limit, chomp: chomp) unless block_given?
|
|
191
|
+
|
|
192
|
+
while (line = gets(sep, limit, chomp: chomp))
|
|
193
|
+
yield line
|
|
194
|
+
end
|
|
116
195
|
end
|
|
117
196
|
|
|
118
|
-
alias each
|
|
197
|
+
alias each_line each
|
|
119
198
|
|
|
199
|
+
# Returns `true` if the stream is positioned at its end, `false`
|
|
200
|
+
# otherwise. See Position documentation in the IO class for more
|
|
201
|
+
# information.
|
|
120
202
|
def eof?
|
|
121
203
|
@output_buffer.empty? && input_finished?
|
|
122
204
|
end
|
|
123
205
|
|
|
124
206
|
# Alias for compatibility. Remove for version 4.
|
|
125
|
-
alias eof eof?
|
|
207
|
+
alias eof eof? # :nodoc:
|
|
126
208
|
end
|
|
127
209
|
end
|
|
128
210
|
end
|
data/lib/zip/ioextras.rb
CHANGED
|
@@ -13,8 +13,11 @@ module Zip
|
|
|
13
13
|
toread = nbytes
|
|
14
14
|
while toread > 0 && !istream.eof?
|
|
15
15
|
tr = [toread, CHUNK_SIZE].min
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
chunk = istream.read(tr, +'')
|
|
17
|
+
break if !chunk || chunk.empty?
|
|
18
|
+
|
|
19
|
+
ostream.write(chunk)
|
|
20
|
+
toread -= chunk.bytesize
|
|
18
21
|
end
|
|
19
22
|
end
|
|
20
23
|
end
|
data/lib/zip/output_stream.rb
CHANGED
|
@@ -53,7 +53,13 @@ module Zip
|
|
|
53
53
|
# stream is passed to the block and closed when the block
|
|
54
54
|
# returns.
|
|
55
55
|
def open(file_name, encrypter: nil, suppress_extra_fields: false)
|
|
56
|
-
|
|
56
|
+
unless block_given?
|
|
57
|
+
return new(
|
|
58
|
+
file_name,
|
|
59
|
+
encrypter: encrypter,
|
|
60
|
+
suppress_extra_fields: suppress_extra_fields
|
|
61
|
+
)
|
|
62
|
+
end
|
|
57
63
|
|
|
58
64
|
zos = new(file_name, stream: false, encrypter: encrypter,
|
|
59
65
|
suppress_extra_fields: suppress_extra_fields)
|
|
@@ -7,15 +7,15 @@ module Zip
|
|
|
7
7
|
@read_so_far = 0
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
-
def read(
|
|
11
|
-
return (
|
|
10
|
+
def read(maxlen = nil)
|
|
11
|
+
return (maxlen.nil? || maxlen.zero? ? '' : nil) if eof?
|
|
12
12
|
|
|
13
|
-
if
|
|
14
|
-
|
|
13
|
+
if maxlen.nil? || (@read_so_far + maxlen) > decompressed_size
|
|
14
|
+
maxlen = decompressed_size - @read_so_far
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
@read_so_far +=
|
|
18
|
-
input_stream.read(
|
|
17
|
+
@read_so_far += maxlen
|
|
18
|
+
input_stream.read(maxlen)
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def eof?
|
data/lib/zip/version.rb
CHANGED
data/lib/zip.rb
CHANGED
|
@@ -54,7 +54,8 @@ module Zip
|
|
|
54
54
|
:warn_invalid_date,
|
|
55
55
|
:case_insensitive_match,
|
|
56
56
|
:force_entry_names_encoding,
|
|
57
|
-
:validate_entry_sizes
|
|
57
|
+
:validate_entry_sizes,
|
|
58
|
+
:validate_declared_number_of_entries
|
|
58
59
|
|
|
59
60
|
DEFAULT_RESTORE_OPTIONS = {
|
|
60
61
|
restore_ownership: false,
|
|
@@ -78,6 +79,7 @@ module Zip
|
|
|
78
79
|
@case_insensitive_match = false
|
|
79
80
|
@force_entry_names_encoding = nil
|
|
80
81
|
@validate_entry_sizes = true
|
|
82
|
+
@validate_declared_number_of_entries = false # Set this to `true` in v4.0.0?
|
|
81
83
|
end
|
|
82
84
|
|
|
83
85
|
# Set options for RubyZip in one block.
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rubyzip
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Robert Haines
|
|
@@ -135,6 +135,7 @@ files:
|
|
|
135
135
|
- LICENSE.md
|
|
136
136
|
- README.md
|
|
137
137
|
- Rakefile
|
|
138
|
+
- lib/rubyzip.rb
|
|
138
139
|
- lib/zip.rb
|
|
139
140
|
- lib/zip/central_directory.rb
|
|
140
141
|
- lib/zip/compressor.rb
|
|
@@ -195,9 +196,9 @@ licenses:
|
|
|
195
196
|
- BSD-2-Clause
|
|
196
197
|
metadata:
|
|
197
198
|
bug_tracker_uri: https://github.com/rubyzip/rubyzip/issues
|
|
198
|
-
changelog_uri: https://github.com/rubyzip/rubyzip/blob/v3.
|
|
199
|
-
documentation_uri: https://www.rubydoc.info/gems/rubyzip/3.
|
|
200
|
-
source_code_uri: https://github.com/rubyzip/rubyzip/tree/v3.
|
|
199
|
+
changelog_uri: https://github.com/rubyzip/rubyzip/blob/v3.6.0/Changelog.md
|
|
200
|
+
documentation_uri: https://www.rubydoc.info/gems/rubyzip/3.6.0
|
|
201
|
+
source_code_uri: https://github.com/rubyzip/rubyzip/tree/v3.6.0
|
|
201
202
|
wiki_uri: https://github.com/rubyzip/rubyzip/wiki
|
|
202
203
|
rubygems_mfa_required: 'true'
|
|
203
204
|
rdoc_options: []
|
|
@@ -214,7 +215,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
214
215
|
- !ruby/object:Gem::Version
|
|
215
216
|
version: '0'
|
|
216
217
|
requirements: []
|
|
217
|
-
rubygems_version:
|
|
218
|
+
rubygems_version: 4.0.17
|
|
218
219
|
specification_version: 4
|
|
219
220
|
summary: rubyzip is a ruby module for reading and writing zip files
|
|
220
221
|
test_files: []
|