rubyzip 3.1.0 → 3.2.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 +24 -0
- data/README.md +51 -8
- data/Rakefile +4 -6
- data/lib/zip/central_directory.rb +5 -3
- data/lib/zip/crypto/aes_encryption.rb +2 -1
- data/lib/zip/crypto/decrypted_io.rb +12 -21
- data/lib/zip/crypto/null_encryption.rb +0 -10
- data/lib/zip/crypto/traditional_encryption.rb +2 -0
- data/lib/zip/dos_time.rb +3 -2
- data/lib/zip/entry.rb +35 -32
- data/lib/zip/extra_field/aes.rb +5 -0
- data/lib/zip/extra_field/generic.rb +7 -1
- data/lib/zip/extra_field/unknown.rb +3 -1
- data/lib/zip/extra_field/zip64.rb +7 -0
- data/lib/zip/extra_field.rb +11 -4
- data/lib/zip/file.rb +26 -22
- data/lib/zip/filesystem/file.rb +7 -6
- data/lib/zip/filesystem/file_stat.rb +4 -4
- data/lib/zip/inflater.rb +4 -3
- data/lib/zip/input_stream.rb +17 -11
- data/lib/zip/ioextras/abstract_input_stream.rb +3 -2
- data/lib/zip/null_decompressor.rb +3 -2
- data/lib/zip/output_stream.rb +13 -9
- data/lib/zip/pass_thru_decompressor.rb +4 -3
- data/lib/zip/streamable_stream.rb +1 -1
- data/lib/zip/version.rb +1 -1
- data/lib/zip.rb +1 -0
- data/rubyzip.gemspec +3 -3
- data/samples/gtk_ruby_zip.rb +1 -1
- data/samples/qtzip.rb +1 -1
- metadata +11 -11
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 169fa7d5832e775e15c2f8a409a35e9da32c0177117039f089f9fa1646aa281e
|
|
4
|
+
data.tar.gz: 9b4fb8929262cb097e3631ad8d8a9efa98d691488ce79e37092df4de27204aea
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4bc8630670298d013486cf4632084554a10ed2ba327c2b54f65c105602f31ca84b87a42976fa1a606226d7cc5b88ecbc356f8a0932a9a53880e0b68c4b7f6275
|
|
7
|
+
data.tar.gz: 4cc717180899d18767c73913fa16abaafeb3624167fc2c4e14a43dde406eef815d9791e84a7bb9f018a255e32d08109dc01efdd8844cc1503a85ac8d931d4b22
|
data/Changelog.md
CHANGED
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
# 3.2.0 (2025-10-14)
|
|
2
|
+
|
|
3
|
+
- Add option to suppress extra fields. [#653](https://github.com/rubyzip/rubyzip/pull/653) (fixes [#34](https://github.com/rubyzip/rubyzip/issues/34), [#398](https://github.com/rubyzip/rubyzip/issues/398) and [#648](https://github.com/rubyzip/rubyzip/issues/648))
|
|
4
|
+
|
|
5
|
+
Tooling/internal:
|
|
6
|
+
|
|
7
|
+
- Entry: clean up reading and writing the Central Directory headers.
|
|
8
|
+
- Improve Zip64 tests for `OutputStream`.
|
|
9
|
+
- Extra fields: use symbols as indices as opposed to strings.
|
|
10
|
+
- Ensure that `Unknown` extra field has a superclass.
|
|
11
|
+
|
|
12
|
+
# 3.1.1 (2025-09-26)
|
|
13
|
+
|
|
14
|
+
- Improve the IO pipeline when decompressing. [#649](https://github.com/rubyzip/rubyzip/pull/649) (which also fixes [#647](https://github.com/rubyzip/rubyzip/issues/647))
|
|
15
|
+
|
|
16
|
+
Tooling/internal:
|
|
17
|
+
|
|
18
|
+
- Improve the `DecryptedIo` class with various updates and optimizations.
|
|
19
|
+
- Remove the `NullDecrypter` class.
|
|
20
|
+
- Properly convert the test suite to use minitest.
|
|
21
|
+
- Move all test helper code into separate files.
|
|
22
|
+
- Updates to the Actions CI, including new OS versions.
|
|
23
|
+
- Update rubocop versions and fix resultant cop failures. [#646](https://github.com/rubyzip/rubyzip/pull/646)
|
|
24
|
+
|
|
1
25
|
# 3.1.0 (2025-09-06)
|
|
2
26
|
|
|
3
27
|
- Support AES decryption. [#579](https://github.com/rubyzip/rubyzip/pull/579) and [#645](https://github.com/rubyzip/rubyzip/pull/645)
|
data/README.md
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
[](http://badge.fury.io/rb/rubyzip)
|
|
4
4
|
[](https://github.com/rubyzip/rubyzip/actions/workflows/tests.yml)
|
|
5
5
|
[](https://github.com/rubyzip/rubyzip/actions/workflows/lint.yml)
|
|
6
|
-
[](https://github.com/rubocop/rubocop)
|
|
7
|
+
[](https://qlty.sh/gh/rubyzip/projects/rubyzip)
|
|
7
8
|
[](https://coveralls.io/r/rubyzip/rubyzip?branch=master)
|
|
8
9
|
|
|
9
10
|
Rubyzip is a ruby library for reading and writing zip files.
|
|
@@ -66,6 +67,48 @@ Zip::File.open(zipfile_name, create: true) do |zipfile|
|
|
|
66
67
|
end
|
|
67
68
|
```
|
|
68
69
|
|
|
70
|
+
### Creating a Zip file with `Zip::OutputStream`
|
|
71
|
+
|
|
72
|
+
```ruby
|
|
73
|
+
require 'rubygems'
|
|
74
|
+
require 'zip'
|
|
75
|
+
|
|
76
|
+
Zip::OutputStream.open('archive.zip') do |zos|
|
|
77
|
+
# Quick.
|
|
78
|
+
zos.put_next_entry('greeting.txt')
|
|
79
|
+
zos << 'Hello, World!'
|
|
80
|
+
|
|
81
|
+
# More control.
|
|
82
|
+
# You MUST NOT make any calls on your `Entry` after calling `put_next_entry`.
|
|
83
|
+
entry = Zip::Entry.new(nil, 'parting.txt')
|
|
84
|
+
entry.atime = Time.now
|
|
85
|
+
zos.put_next_entry(entry)
|
|
86
|
+
zos.write('TTFN')
|
|
87
|
+
end
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
You can generate a Zip archive in memory using `Zip::OutputStream.write_buffer`.
|
|
91
|
+
|
|
92
|
+
### Suppressing extra fields
|
|
93
|
+
|
|
94
|
+
If you wish to suppress extra fields from being added to your entries, you can do so by passing the `suppress_extra_fields` parameter to any of the archive opening calls within `Zip::File` or `Zip::OutputStream`, e.g.:
|
|
95
|
+
|
|
96
|
+
```ruby
|
|
97
|
+
# Suppress all extra fields.
|
|
98
|
+
Zip::File.open('archive.zip', create: true, suppress_extra_fields: true)
|
|
99
|
+
Zip::OutputStream.open('archive.zip', suppress_extra_fields: true)
|
|
100
|
+
|
|
101
|
+
# Suppress an individual extra field.
|
|
102
|
+
Zip::File.open('archive.zip', create: true, suppress_extra_fields: :zip64)
|
|
103
|
+
Zip::OutputStream.open('archive.zip', suppress_extra_fields: :zip64)
|
|
104
|
+
|
|
105
|
+
# Suppress multiple extra fields.
|
|
106
|
+
Zip::File.open('archive.zip', create: true, suppress_extra_fields: [:ntfs, :zip64])
|
|
107
|
+
Zip::OutputStream.open('archive.zip', suppress_extra_fields: [:ntfs, :zip64])
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Note that there are some extra fields that cannot be suppressed at all (e.g. `:aes`), and some which will only be suppressed if it is safe to do so (e.g. `:zip64`).
|
|
111
|
+
|
|
69
112
|
### Zipping a directory recursively
|
|
70
113
|
|
|
71
114
|
Copy from [here](https://github.com/rubyzip/rubyzip/blob/9d891f7353e66052283562d3e252fe380bb4b199/samples/example_recursive.rb)
|
|
@@ -179,7 +222,7 @@ Zip::File.open('foo.zip') do |zip_file|
|
|
|
179
222
|
end
|
|
180
223
|
```
|
|
181
224
|
|
|
182
|
-
###
|
|
225
|
+
### Reading a Zip file with `Zip::InputStream`
|
|
183
226
|
|
|
184
227
|
`Zip::InputStream` can be used for faster reading of zip file content because it does not read the Central directory up front.
|
|
185
228
|
|
|
@@ -189,14 +232,14 @@ There is one exception where it can not work however, and this is if the file do
|
|
|
189
232
|
|
|
190
233
|
If `Zip::InputStream` finds such an entry in the zip archive it will raise an exception (`Zip::StreamingError`).
|
|
191
234
|
|
|
192
|
-
`Zip::InputStream` is not designed to be used for random access in a zip file. When performing any operations on an entry that you are accessing via `Zip::InputStream
|
|
235
|
+
`Zip::InputStream` is not designed to be used for random access in a zip file. When performing any operations on an entry that you are accessing via `Zip::InputStream#get_next_entry` then you should complete any such operations before the next call to `get_next_entry`.
|
|
193
236
|
|
|
194
237
|
```ruby
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
end
|
|
238
|
+
Zip::InputStream.open('file.zip') do |zip_stream|
|
|
239
|
+
while entry = zip_stream.get_next_entry
|
|
240
|
+
# All required operations on `entry` go here.
|
|
241
|
+
end
|
|
242
|
+
end # The `InputStream` is closed at the end of the block.
|
|
200
243
|
```
|
|
201
244
|
|
|
202
245
|
Any attempt to move about in a zip file opened with `Zip::InputStream` could result in the incorrect entry being accessed and/or Zlib buffer errors. If you need random access in a zip file, use `Zip::File`.
|
data/Rakefile
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'bundler/gem_tasks'
|
|
4
|
-
require '
|
|
4
|
+
require 'minitest/test_task'
|
|
5
5
|
require 'rdoc/task'
|
|
6
6
|
require 'rubocop/rake_task'
|
|
7
7
|
|
|
8
8
|
task default: :test
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
test.
|
|
12
|
-
test.
|
|
13
|
-
test.pattern = 'test/**/*_test.rb'
|
|
14
|
-
test.verbose = true
|
|
10
|
+
Minitest::TestTask.create do |test|
|
|
11
|
+
test.framework = 'require "simplecov"'
|
|
12
|
+
test.test_globs = 'test/**/*_test.rb'
|
|
15
13
|
end
|
|
16
14
|
|
|
17
15
|
RDoc::Task.new do |rdoc|
|
|
@@ -39,9 +39,11 @@ module Zip
|
|
|
39
39
|
read_central_directory_entries(io)
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
-
def write_to_stream(io) # :nodoc:
|
|
42
|
+
def write_to_stream(io, suppress_extra_fields: false) # :nodoc:
|
|
43
43
|
cdir_offset = io.tell
|
|
44
|
-
@entry_set.each
|
|
44
|
+
@entry_set.each do |entry|
|
|
45
|
+
entry.write_c_dir_entry(io, suppress_extra_fields: suppress_extra_fields)
|
|
46
|
+
end
|
|
45
47
|
eocd_offset = io.tell
|
|
46
48
|
cdir_size = eocd_offset - cdir_offset
|
|
47
49
|
if Zip.write_zip64_support &&
|
|
@@ -182,7 +184,7 @@ module Zip
|
|
|
182
184
|
next unless entry
|
|
183
185
|
|
|
184
186
|
offset = if entry.zip64?
|
|
185
|
-
entry.extra[
|
|
187
|
+
entry.extra[:zip64].relative_header_offset
|
|
186
188
|
else
|
|
187
189
|
entry.local_header_offset
|
|
188
190
|
end
|
|
@@ -112,7 +112,8 @@ module Zip
|
|
|
112
112
|
@hmac = OpenSSL::HMAC.new(enc_hmac_key, OpenSSL::Digest.new('SHA1'))
|
|
113
113
|
end
|
|
114
114
|
|
|
115
|
-
def check_integrity(
|
|
115
|
+
def check_integrity!(io)
|
|
116
|
+
auth_code = io.read(AUTHENTICATION_CODE_LENGTH)
|
|
116
117
|
raise Error, 'Integrity fault' if @hmac.digest[0...AUTHENTICATION_CODE_LENGTH] != auth_code
|
|
117
118
|
end
|
|
118
119
|
end
|
|
@@ -7,48 +7,39 @@ module Zip
|
|
|
7
7
|
def initialize(io, decrypter, compressed_size)
|
|
8
8
|
@io = io
|
|
9
9
|
@decrypter = decrypter
|
|
10
|
-
@
|
|
11
|
-
@
|
|
10
|
+
@bytes_remaining = compressed_size
|
|
11
|
+
@buffer = +''
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def read(length = nil, outbuf = +'')
|
|
15
|
-
return (length.nil? || length.zero? ? '' : nil) if eof
|
|
15
|
+
return (length.nil? || length.zero? ? '' : nil) if eof?
|
|
16
16
|
|
|
17
|
-
while length.nil? || (buffer.bytesize < length)
|
|
17
|
+
while length.nil? || (@buffer.bytesize < length)
|
|
18
18
|
break if input_finished?
|
|
19
19
|
|
|
20
|
-
buffer << produce_input
|
|
20
|
+
@buffer << produce_input
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
@decrypter.check_integrity(@io.read(::Zip::AESEncryption::AUTHENTICATION_CODE_LENGTH))
|
|
25
|
-
end
|
|
23
|
+
@decrypter.check_integrity!(@io) if input_finished?
|
|
26
24
|
|
|
27
|
-
outbuf.replace(buffer.slice!(0...(length || buffer.bytesize)))
|
|
25
|
+
outbuf.replace(@buffer.slice!(0...(length || @buffer.bytesize)))
|
|
28
26
|
end
|
|
29
27
|
|
|
30
28
|
private
|
|
31
29
|
|
|
32
|
-
def eof
|
|
33
|
-
buffer.empty? && input_finished?
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def buffer
|
|
37
|
-
@buffer ||= +''
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def pos
|
|
41
|
-
@io.tell - @offset
|
|
30
|
+
def eof?
|
|
31
|
+
@buffer.empty? && input_finished?
|
|
42
32
|
end
|
|
43
33
|
|
|
44
34
|
def input_finished?
|
|
45
|
-
|
|
35
|
+
!@bytes_remaining.positive?
|
|
46
36
|
end
|
|
47
37
|
|
|
48
38
|
def produce_input
|
|
49
|
-
chunk_size = [
|
|
39
|
+
chunk_size = [@bytes_remaining, CHUNK_SIZE].min
|
|
50
40
|
return '' unless chunk_size.positive?
|
|
51
41
|
|
|
42
|
+
@bytes_remaining -= chunk_size
|
|
52
43
|
@decrypter.decrypt(@io.read(chunk_size))
|
|
53
44
|
end
|
|
54
45
|
end
|
|
@@ -28,16 +28,6 @@ module Zip
|
|
|
28
28
|
|
|
29
29
|
def reset!; end
|
|
30
30
|
end
|
|
31
|
-
|
|
32
|
-
class NullDecrypter < Decrypter # :nodoc:
|
|
33
|
-
include NullEncryption
|
|
34
|
-
|
|
35
|
-
def decrypt(data)
|
|
36
|
-
data
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def reset!(_header); end
|
|
40
|
-
end
|
|
41
31
|
end
|
|
42
32
|
|
|
43
33
|
# Copyright (C) 2002, 2003 Thomas Sondergaard
|
data/lib/zip/dos_time.rb
CHANGED
|
@@ -21,7 +21,7 @@ module Zip
|
|
|
21
21
|
def absolute_time?
|
|
22
22
|
# If absolute time is not set, we can assume it is an absolute time
|
|
23
23
|
# because times do have timezone information by default.
|
|
24
|
-
@absolute_time.nil?
|
|
24
|
+
@absolute_time.nil? || @absolute_time
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def to_binary_dos_time
|
|
@@ -36,7 +36,8 @@ module Zip
|
|
|
36
36
|
((year - 1980) << 9)
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
# Deprecated. Remove for version 4.
|
|
40
|
+
def dos_equals(other) # rubocop:disable Naming/PredicateMethod
|
|
40
41
|
warn 'Zip::DOSTime#dos_equals is deprecated. Use `==` instead.'
|
|
41
42
|
self == other
|
|
42
43
|
end
|
data/lib/zip/entry.rb
CHANGED
|
@@ -127,10 +127,10 @@ module Zip
|
|
|
127
127
|
# Returns modification time by default.
|
|
128
128
|
def time(component: :mtime)
|
|
129
129
|
time =
|
|
130
|
-
if @extra[
|
|
131
|
-
@extra[
|
|
132
|
-
elsif @extra[
|
|
133
|
-
@extra[
|
|
130
|
+
if @extra[:universaltime]
|
|
131
|
+
@extra[:universaltime].send(component)
|
|
132
|
+
elsif @extra[:ntfs]
|
|
133
|
+
@extra[:ntfs].send(component)
|
|
134
134
|
end
|
|
135
135
|
|
|
136
136
|
# Standard time field in central directory has local time
|
|
@@ -155,13 +155,13 @@ module Zip
|
|
|
155
155
|
# Sets modification time by default.
|
|
156
156
|
def time=(value, component: :mtime)
|
|
157
157
|
@dirty = true
|
|
158
|
-
unless @extra.member?(
|
|
159
|
-
@extra.create(
|
|
158
|
+
unless @extra.member?(:universaltime) || @extra.member?(:ntfs)
|
|
159
|
+
@extra.create(:universaltime)
|
|
160
160
|
end
|
|
161
161
|
|
|
162
162
|
value = DOSTime.from_time(value)
|
|
163
163
|
comp = "#{component}=" unless component.to_s.end_with?('=')
|
|
164
|
-
(@extra[
|
|
164
|
+
(@extra[:universaltime] || @extra[:ntfs]).send(comp, value)
|
|
165
165
|
@time = value if component == :mtime
|
|
166
166
|
end
|
|
167
167
|
|
|
@@ -179,7 +179,7 @@ module Zip
|
|
|
179
179
|
|
|
180
180
|
# Does this entry return time fields with accurate timezone information?
|
|
181
181
|
def absolute_time?
|
|
182
|
-
@extra.member?(
|
|
182
|
+
@extra.member?(:universaltime) || @extra.member?(:ntfs)
|
|
183
183
|
end
|
|
184
184
|
|
|
185
185
|
# Return the compression method for this entry.
|
|
@@ -200,12 +200,12 @@ module Zip
|
|
|
200
200
|
|
|
201
201
|
# Does this entry use the ZIP64 extensions?
|
|
202
202
|
def zip64?
|
|
203
|
-
!@extra[
|
|
203
|
+
!@extra[:zip64].nil?
|
|
204
204
|
end
|
|
205
205
|
|
|
206
206
|
# Is this entry encrypted with AES encryption?
|
|
207
207
|
def aes?
|
|
208
|
-
!@extra[
|
|
208
|
+
!@extra[:aes].nil?
|
|
209
209
|
end
|
|
210
210
|
|
|
211
211
|
def file_type_is?(type) # :nodoc:
|
|
@@ -392,7 +392,7 @@ module Zip
|
|
|
392
392
|
end
|
|
393
393
|
|
|
394
394
|
def pack_local_entry # :nodoc:
|
|
395
|
-
zip64 = @extra[
|
|
395
|
+
zip64 = @extra[:zip64]
|
|
396
396
|
[::Zip::LOCAL_ENTRY_SIGNATURE,
|
|
397
397
|
@version_needed_to_extract, # version needed to extract
|
|
398
398
|
@gp_flags, # @gp_flags
|
|
@@ -406,9 +406,17 @@ module Zip
|
|
|
406
406
|
@extra ? @extra.local_size : 0].pack('VvvvvvVVVvv')
|
|
407
407
|
end
|
|
408
408
|
|
|
409
|
-
def write_local_entry(io, rewrite: false) # :nodoc:
|
|
409
|
+
def write_local_entry(io, suppress_extra_fields: false, rewrite: false) # :nodoc:
|
|
410
410
|
prep_local_zip64_extra
|
|
411
|
-
|
|
411
|
+
|
|
412
|
+
# If we are rewriting the local header, then we verify that we haven't changed
|
|
413
|
+
# its size. At this point we have to keep extra fields if they are present.
|
|
414
|
+
if rewrite
|
|
415
|
+
verify_local_header_size!
|
|
416
|
+
elsif suppress_extra_fields
|
|
417
|
+
@extra.suppress_fields!(suppress_extra_fields)
|
|
418
|
+
end
|
|
419
|
+
|
|
412
420
|
@local_header_offset = io.tell
|
|
413
421
|
|
|
414
422
|
io << pack_local_entry
|
|
@@ -436,10 +444,7 @@ module Zip
|
|
|
436
444
|
_, # diskNumberStart
|
|
437
445
|
@internal_file_attributes,
|
|
438
446
|
@external_file_attributes,
|
|
439
|
-
@local_header_offset
|
|
440
|
-
@name,
|
|
441
|
-
@extra,
|
|
442
|
-
@comment = buf.unpack('VCCvvvvvVVVvvvvvVV')
|
|
447
|
+
@local_header_offset = buf.unpack('VCCvvvvvVVVvvvvvVV')
|
|
443
448
|
end
|
|
444
449
|
|
|
445
450
|
def set_ftype_from_c_dir_entry # :nodoc:
|
|
@@ -566,7 +571,7 @@ module Zip
|
|
|
566
571
|
end
|
|
567
572
|
|
|
568
573
|
def pack_c_dir_entry # :nodoc:
|
|
569
|
-
zip64 = @extra[
|
|
574
|
+
zip64 = @extra[:zip64]
|
|
570
575
|
[
|
|
571
576
|
@header_signature,
|
|
572
577
|
@version, # version of encoding software
|
|
@@ -585,14 +590,11 @@ module Zip
|
|
|
585
590
|
zip64 && zip64.disk_start_number ? 0xFFFF : 0, # disk number start
|
|
586
591
|
@internal_file_attributes, # file type (binary=0, text=1)
|
|
587
592
|
@external_file_attributes, # native filesystem attributes
|
|
588
|
-
zip64 && zip64.relative_header_offset ? 0xFFFFFFFF : @local_header_offset
|
|
589
|
-
@name,
|
|
590
|
-
@extra,
|
|
591
|
-
@comment
|
|
593
|
+
zip64 && zip64.relative_header_offset ? 0xFFFFFFFF : @local_header_offset
|
|
592
594
|
].pack('VCCvvvvvVVVvvvvvVV')
|
|
593
595
|
end
|
|
594
596
|
|
|
595
|
-
def write_c_dir_entry(io) # :nodoc:
|
|
597
|
+
def write_c_dir_entry(io, suppress_extra_fields: false) # :nodoc:
|
|
596
598
|
prep_cdir_zip64_extra
|
|
597
599
|
|
|
598
600
|
case @fstype
|
|
@@ -614,6 +616,7 @@ module Zip
|
|
|
614
616
|
end
|
|
615
617
|
end
|
|
616
618
|
|
|
619
|
+
@extra.suppress_fields!(suppress_extra_fields) if suppress_extra_fields
|
|
617
620
|
io << pack_c_dir_entry
|
|
618
621
|
|
|
619
622
|
io << @name
|
|
@@ -799,9 +802,9 @@ module Zip
|
|
|
799
802
|
return unless zip64?
|
|
800
803
|
|
|
801
804
|
if for_local_header
|
|
802
|
-
@size, @compressed_size = @extra[
|
|
805
|
+
@size, @compressed_size = @extra[:zip64].parse(@size, @compressed_size)
|
|
803
806
|
else
|
|
804
|
-
@size, @compressed_size, @local_header_offset = @extra[
|
|
807
|
+
@size, @compressed_size, @local_header_offset = @extra[:zip64].parse(
|
|
805
808
|
@size, @compressed_size, @local_header_offset
|
|
806
809
|
)
|
|
807
810
|
end
|
|
@@ -810,15 +813,15 @@ module Zip
|
|
|
810
813
|
def parse_aes_extra # :nodoc:
|
|
811
814
|
return unless aes?
|
|
812
815
|
|
|
813
|
-
if @extra[
|
|
814
|
-
raise Error, "Unsupported encryption method #{@extra[
|
|
816
|
+
if @extra[:aes].vendor_id != 'AE'
|
|
817
|
+
raise Error, "Unsupported encryption method #{@extra[:aes].vendor_id}"
|
|
815
818
|
end
|
|
816
819
|
|
|
817
|
-
unless ::Zip::AESEncryption::VERSIONS.include? @extra[
|
|
818
|
-
raise Error, "Unsupported encryption style #{@extra[
|
|
820
|
+
unless ::Zip::AESEncryption::VERSIONS.include? @extra[:aes].vendor_version
|
|
821
|
+
raise Error, "Unsupported encryption style #{@extra[:aes].vendor_version}"
|
|
819
822
|
end
|
|
820
823
|
|
|
821
|
-
@compression_method = @extra[
|
|
824
|
+
@compression_method = @extra[:aes].compression_method if ftype != :directory
|
|
822
825
|
end
|
|
823
826
|
|
|
824
827
|
# For DEFLATED compression *only*: set the general purpose flags 1 and 2 to
|
|
@@ -851,7 +854,7 @@ module Zip
|
|
|
851
854
|
# If we already have a ZIP64 extra (placeholder) then we must fill it in.
|
|
852
855
|
if zip64? || @size.nil? || @size >= 0xFFFFFFFF || @compressed_size >= 0xFFFFFFFF
|
|
853
856
|
@version_needed_to_extract = VERSION_NEEDED_TO_EXTRACT_ZIP64
|
|
854
|
-
zip64 = @extra[
|
|
857
|
+
zip64 = @extra[:zip64] || @extra.create(:zip64)
|
|
855
858
|
|
|
856
859
|
# Local header always includes size and compressed size.
|
|
857
860
|
zip64.original_size = @size || 0
|
|
@@ -865,7 +868,7 @@ module Zip
|
|
|
865
868
|
if (@size && @size >= 0xFFFFFFFF) || @compressed_size >= 0xFFFFFFFF ||
|
|
866
869
|
@local_header_offset >= 0xFFFFFFFF
|
|
867
870
|
@version_needed_to_extract = VERSION_NEEDED_TO_EXTRACT_ZIP64
|
|
868
|
-
zip64 = @extra[
|
|
871
|
+
zip64 = @extra[:zip64] || @extra.create(:zip64)
|
|
869
872
|
|
|
870
873
|
# Central directory entry entries include whichever fields are necessary.
|
|
871
874
|
zip64.original_size = @size if @size && @size >= 0xFFFFFFFF
|
data/lib/zip/extra_field/aes.rb
CHANGED
|
@@ -33,6 +33,11 @@ module Zip
|
|
|
33
33
|
@encryption_strength, @compression_method = content.unpack('va2Cv')
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
+
# We can never suppress the AES extra field as it is needed to read the file.
|
|
37
|
+
def suppress?
|
|
38
|
+
false
|
|
39
|
+
end
|
|
40
|
+
|
|
36
41
|
def pack_for_local
|
|
37
42
|
[@vendor_version, @vendor_id,
|
|
38
43
|
@encryption_strength, @compression_method].pack('va2Cv')
|
|
@@ -9,7 +9,7 @@ module Zip
|
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def self.name
|
|
12
|
-
@name ||= to_s.split('::')
|
|
12
|
+
@name ||= to_s.split('::').last.downcase.to_sym
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
# return field [size, content] or false
|
|
@@ -24,6 +24,12 @@ module Zip
|
|
|
24
24
|
[binstr[2, 2].unpack1('v'), binstr[4..]]
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
# Default strategy is to suppress all extra fields if we're asked to.
|
|
28
|
+
# Specific extra field types can override this if they need to be kept.
|
|
29
|
+
def suppress?
|
|
30
|
+
true
|
|
31
|
+
end
|
|
32
|
+
|
|
27
33
|
def to_local_bin
|
|
28
34
|
s = pack_for_local
|
|
29
35
|
(self.class.const_get(:HEADER_ID) + [s.bytesize].pack('v')) << s
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative 'generic'
|
|
4
|
+
|
|
3
5
|
module Zip
|
|
4
6
|
# A class to hold unknown extra fields so that they are preserved.
|
|
5
|
-
class ExtraField::Unknown # :nodoc:
|
|
7
|
+
class ExtraField::Unknown < ExtraField::Generic # :nodoc:
|
|
6
8
|
def initialize
|
|
7
9
|
@local_bin = +''
|
|
8
10
|
@cdir_bin = +''
|
|
@@ -56,6 +56,13 @@ module Zip
|
|
|
56
56
|
end
|
|
57
57
|
private :extract
|
|
58
58
|
|
|
59
|
+
# We can suppress the zip64 extra field unless we know the size is large or
|
|
60
|
+
# the relative header offset is large (for central directory entries).
|
|
61
|
+
def suppress?
|
|
62
|
+
!(@original_size && @original_size >= 0xFFFFFFFF) ||
|
|
63
|
+
(@relative_header_offset && @relative_header_offset >= 0xFFFFFFFF)
|
|
64
|
+
end
|
|
65
|
+
|
|
59
66
|
def pack_for_local
|
|
60
67
|
# Local header entries must contain original size and compressed size;
|
|
61
68
|
# other fields do not apply.
|
data/lib/zip/extra_field.rb
CHANGED
|
@@ -19,14 +19,14 @@ module Zip
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def extra_field_type_unknown(binstr, len, index, local)
|
|
22
|
-
self[
|
|
22
|
+
self[:unknown] ||= Unknown.new
|
|
23
23
|
|
|
24
24
|
if !len || len + 4 > binstr[index..].bytesize
|
|
25
|
-
self[
|
|
25
|
+
self[:unknown].merge(binstr[index..], local: local)
|
|
26
26
|
return
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
self[
|
|
29
|
+
self[:unknown].merge(binstr[index, len + 4], local: local)
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def merge(binstr, local: false)
|
|
@@ -57,10 +57,17 @@ module Zip
|
|
|
57
57
|
# signature/size does not prevent known fields from being read back in.
|
|
58
58
|
def ordered_values
|
|
59
59
|
result = []
|
|
60
|
-
each { |k, v| k ==
|
|
60
|
+
each { |k, v| k == :unknown ? result.push(v) : result.unshift(v) }
|
|
61
61
|
result
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
+
# Remove any extra fields that indicate they can be safely suppressed.
|
|
65
|
+
def suppress_fields!(fields)
|
|
66
|
+
reject! do |k, v|
|
|
67
|
+
v.suppress? if fields == true || [*fields].include?(k)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
64
71
|
def to_local_bin
|
|
65
72
|
ordered_values.map! { |v| v.to_local_bin.force_encoding('BINARY') }.join
|
|
66
73
|
end
|
data/lib/zip/file.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'fileutils'
|
|
3
4
|
require 'forwardable'
|
|
4
5
|
|
|
5
6
|
require_relative 'file_split'
|
|
@@ -75,7 +76,8 @@ module Zip
|
|
|
75
76
|
restore_ownership: DEFAULT_RESTORE_OPTIONS[:restore_ownership],
|
|
76
77
|
restore_permissions: DEFAULT_RESTORE_OPTIONS[:restore_permissions],
|
|
77
78
|
restore_times: DEFAULT_RESTORE_OPTIONS[:restore_times],
|
|
78
|
-
compression_level: ::Zip.default_compression
|
|
79
|
+
compression_level: ::Zip.default_compression,
|
|
80
|
+
suppress_extra_fields: false)
|
|
79
81
|
super()
|
|
80
82
|
|
|
81
83
|
@name = path_or_io.respond_to?(:path) ? path_or_io.path : path_or_io
|
|
@@ -83,10 +85,11 @@ module Zip
|
|
|
83
85
|
|
|
84
86
|
initialize_cdir(path_or_io, buffer: buffer)
|
|
85
87
|
|
|
86
|
-
@restore_ownership
|
|
87
|
-
@restore_permissions
|
|
88
|
-
@restore_times
|
|
89
|
-
@compression_level
|
|
88
|
+
@restore_ownership = restore_ownership
|
|
89
|
+
@restore_permissions = restore_permissions
|
|
90
|
+
@restore_times = restore_times
|
|
91
|
+
@compression_level = compression_level
|
|
92
|
+
@suppress_extra_fields = suppress_extra_fields
|
|
90
93
|
end
|
|
91
94
|
|
|
92
95
|
class << self
|
|
@@ -97,13 +100,14 @@ module Zip
|
|
|
97
100
|
restore_ownership: DEFAULT_RESTORE_OPTIONS[:restore_ownership],
|
|
98
101
|
restore_permissions: DEFAULT_RESTORE_OPTIONS[:restore_permissions],
|
|
99
102
|
restore_times: DEFAULT_RESTORE_OPTIONS[:restore_times],
|
|
100
|
-
compression_level: ::Zip.default_compression
|
|
101
|
-
|
|
102
|
-
zf = ::Zip::File.new(file_name, create:
|
|
103
|
-
restore_ownership:
|
|
104
|
-
restore_permissions:
|
|
105
|
-
restore_times:
|
|
106
|
-
compression_level:
|
|
103
|
+
compression_level: ::Zip.default_compression,
|
|
104
|
+
suppress_extra_fields: false)
|
|
105
|
+
zf = ::Zip::File.new(file_name, create: create,
|
|
106
|
+
restore_ownership: restore_ownership,
|
|
107
|
+
restore_permissions: restore_permissions,
|
|
108
|
+
restore_times: restore_times,
|
|
109
|
+
compression_level: compression_level,
|
|
110
|
+
suppress_extra_fields: suppress_extra_fields)
|
|
107
111
|
|
|
108
112
|
return zf unless block_given?
|
|
109
113
|
|
|
@@ -122,8 +126,8 @@ module Zip
|
|
|
122
126
|
restore_ownership: DEFAULT_RESTORE_OPTIONS[:restore_ownership],
|
|
123
127
|
restore_permissions: DEFAULT_RESTORE_OPTIONS[:restore_permissions],
|
|
124
128
|
restore_times: DEFAULT_RESTORE_OPTIONS[:restore_times],
|
|
125
|
-
compression_level: ::Zip.default_compression
|
|
126
|
-
|
|
129
|
+
compression_level: ::Zip.default_compression,
|
|
130
|
+
suppress_extra_fields: false)
|
|
127
131
|
unless IO_METHODS.map { |method| io.respond_to?(method) }.all? || io.kind_of?(String)
|
|
128
132
|
raise 'Zip::File.open_buffer expects a String or IO-like argument' \
|
|
129
133
|
"(responds to #{IO_METHODS.join(', ')}). Found: #{io.class}"
|
|
@@ -132,10 +136,11 @@ module Zip
|
|
|
132
136
|
io = ::StringIO.new(io) if io.kind_of?(::String)
|
|
133
137
|
|
|
134
138
|
zf = ::Zip::File.new(io, create: create, buffer: true,
|
|
135
|
-
restore_ownership:
|
|
136
|
-
restore_permissions:
|
|
137
|
-
restore_times:
|
|
138
|
-
compression_level:
|
|
139
|
+
restore_ownership: restore_ownership,
|
|
140
|
+
restore_permissions: restore_permissions,
|
|
141
|
+
restore_times: restore_times,
|
|
142
|
+
compression_level: compression_level,
|
|
143
|
+
suppress_extra_fields: suppress_extra_fields)
|
|
139
144
|
|
|
140
145
|
return zf unless block_given?
|
|
141
146
|
|
|
@@ -191,7 +196,6 @@ module Zip
|
|
|
191
196
|
extra: nil, compressed_size: nil, crc: nil,
|
|
192
197
|
compression_method: nil, compression_level: nil,
|
|
193
198
|
size: nil, time: nil, &a_proc)
|
|
194
|
-
|
|
195
199
|
new_entry =
|
|
196
200
|
if entry.kind_of?(Entry)
|
|
197
201
|
entry
|
|
@@ -288,7 +292,7 @@ module Zip
|
|
|
288
292
|
return if name.kind_of?(StringIO) || !commit_required?
|
|
289
293
|
|
|
290
294
|
on_success_replace do |tmp_file|
|
|
291
|
-
::Zip::OutputStream.open(tmp_file) do |zos|
|
|
295
|
+
::Zip::OutputStream.open(tmp_file, suppress_extra_fields: @suppress_extra_fields) do |zos|
|
|
292
296
|
@cdir.each do |e|
|
|
293
297
|
e.write_to_zip_output_stream(zos)
|
|
294
298
|
e.clean_up
|
|
@@ -304,7 +308,7 @@ module Zip
|
|
|
304
308
|
def write_buffer(io = ::StringIO.new)
|
|
305
309
|
return io unless commit_required?
|
|
306
310
|
|
|
307
|
-
::Zip::OutputStream.write_buffer(io) do |zos|
|
|
311
|
+
::Zip::OutputStream.write_buffer(io, suppress_extra_fields: @suppress_extra_fields) do |zos|
|
|
308
312
|
@cdir.each { |e| e.write_to_zip_output_stream(zos) }
|
|
309
313
|
zos.comment = comment
|
|
310
314
|
end
|
|
@@ -412,7 +416,7 @@ module Zip
|
|
|
412
416
|
::File.chmod(@file_permissions, name) unless @create
|
|
413
417
|
end
|
|
414
418
|
ensure
|
|
415
|
-
|
|
419
|
+
FileUtils.rm_f(tmp_filename)
|
|
416
420
|
end
|
|
417
421
|
end
|
|
418
422
|
end
|
data/lib/zip/filesystem/file.rb
CHANGED
|
@@ -27,7 +27,7 @@ module Zip
|
|
|
27
27
|
|
|
28
28
|
def unix_mode_cmp(filename, mode)
|
|
29
29
|
e = find_entry(filename)
|
|
30
|
-
e.fstype == FSTYPE_UNIX && (
|
|
30
|
+
e.fstype == FSTYPE_UNIX && (e.external_file_attributes >> 16).anybits?(mode)
|
|
31
31
|
rescue Errno::ENOENT
|
|
32
32
|
false
|
|
33
33
|
end
|
|
@@ -102,18 +102,19 @@ module Zip
|
|
|
102
102
|
@mapped_zip.get_entry(filename).size
|
|
103
103
|
end
|
|
104
104
|
|
|
105
|
-
# Returns nil for not found and nil for directories
|
|
105
|
+
# Returns nil for not found and nil for directories.
|
|
106
|
+
# We disable the cop here for compatibility with `::File.size?`.
|
|
106
107
|
def size?(filename)
|
|
107
108
|
entry = @mapped_zip.find_entry(filename)
|
|
108
|
-
entry.nil? || entry.directory? ? nil : entry.size
|
|
109
|
+
entry.nil? || entry.directory? ? nil : entry.size # rubocop:disable Style/ReturnNilInPredicateMethodDefinition
|
|
109
110
|
end
|
|
110
111
|
|
|
111
112
|
def chown(owner, group, *filenames)
|
|
112
113
|
filenames.each do |filename|
|
|
113
114
|
e = find_entry(filename)
|
|
114
|
-
e.extra.create(
|
|
115
|
-
e.extra[
|
|
116
|
-
e.extra[
|
|
115
|
+
e.extra.create(:iunix) unless e.extra.member?(:iunix)
|
|
116
|
+
e.extra[:iunix].uid = owner
|
|
117
|
+
e.extra[:iunix].gid = group
|
|
117
118
|
end
|
|
118
119
|
filenames.size
|
|
119
120
|
end
|
|
@@ -36,8 +36,8 @@ module Zip
|
|
|
36
36
|
|
|
37
37
|
def gid
|
|
38
38
|
e = find_entry
|
|
39
|
-
if e.extra.member?
|
|
40
|
-
e.extra[
|
|
39
|
+
if e.extra.member? :iunix
|
|
40
|
+
e.extra[:iunix].gid || 0
|
|
41
41
|
else
|
|
42
42
|
0
|
|
43
43
|
end
|
|
@@ -45,8 +45,8 @@ module Zip
|
|
|
45
45
|
|
|
46
46
|
def uid
|
|
47
47
|
e = find_entry
|
|
48
|
-
if e.extra.member?
|
|
49
|
-
e.extra[
|
|
48
|
+
if e.extra.member? :iunix
|
|
49
|
+
e.extra[:iunix].uid || 0
|
|
50
50
|
else
|
|
51
51
|
0
|
|
52
52
|
end
|
data/lib/zip/inflater.rb
CHANGED
|
@@ -10,7 +10,7 @@ module Zip
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def read(length = nil, outbuf = +'')
|
|
13
|
-
return (length.nil? || length.zero? ? '' : nil) if eof
|
|
13
|
+
return (length.nil? || length.zero? ? '' : nil) if eof?
|
|
14
14
|
|
|
15
15
|
while length.nil? || (@buffer.bytesize < length)
|
|
16
16
|
break if input_finished?
|
|
@@ -21,11 +21,12 @@ module Zip
|
|
|
21
21
|
outbuf.replace(@buffer.slice!(0...(length || @buffer.bytesize)))
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
def eof
|
|
24
|
+
def eof?
|
|
25
25
|
@buffer.empty? && input_finished?
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
# Alias for compatibility. Remove for version 4.
|
|
29
|
+
alias eof eof?
|
|
29
30
|
|
|
30
31
|
private
|
|
31
32
|
|
data/lib/zip/input_stream.rb
CHANGED
|
@@ -55,7 +55,7 @@ module Zip
|
|
|
55
55
|
super()
|
|
56
56
|
@archive_io = get_io(context, offset)
|
|
57
57
|
@decompressor = ::Zip::NullDecompressor
|
|
58
|
-
@decrypter = decrypter
|
|
58
|
+
@decrypter = decrypter
|
|
59
59
|
@current_entry = nil
|
|
60
60
|
@complete_entry = nil
|
|
61
61
|
end
|
|
@@ -135,21 +135,27 @@ module Zip
|
|
|
135
135
|
@current_entry = ::Zip::Entry.read_local_entry(@archive_io)
|
|
136
136
|
return if @current_entry.nil?
|
|
137
137
|
|
|
138
|
-
if @current_entry.encrypted? && @decrypter.kind_of?(NullDecrypter)
|
|
139
|
-
raise Error,
|
|
140
|
-
'A password is required to decode this zip file'
|
|
141
|
-
end
|
|
142
|
-
|
|
143
138
|
if @current_entry.incomplete? && @current_entry.compressed_size == 0 && !@complete_entry
|
|
144
139
|
raise StreamingError, @current_entry
|
|
145
140
|
end
|
|
146
141
|
|
|
147
|
-
@
|
|
148
|
-
@decompressor = get_decompressor
|
|
142
|
+
@decompressor = assemble_io
|
|
149
143
|
flush
|
|
150
144
|
@current_entry
|
|
151
145
|
end
|
|
152
146
|
|
|
147
|
+
def assemble_io # :nodoc:
|
|
148
|
+
io = if @current_entry.encrypted?
|
|
149
|
+
raise Error, 'A password is required to decode this zip file.' if @decrypter.nil?
|
|
150
|
+
|
|
151
|
+
get_decrypted_io
|
|
152
|
+
else
|
|
153
|
+
@archive_io
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
get_decompressor(io)
|
|
157
|
+
end
|
|
158
|
+
|
|
153
159
|
def get_decrypted_io # :nodoc:
|
|
154
160
|
header = @archive_io.read(@decrypter.header_bytesize)
|
|
155
161
|
@decrypter.reset!(header)
|
|
@@ -170,7 +176,7 @@ module Zip
|
|
|
170
176
|
::Zip::DecryptedIo.new(@archive_io, @decrypter, compressed_size)
|
|
171
177
|
end
|
|
172
178
|
|
|
173
|
-
def get_decompressor # :nodoc:
|
|
179
|
+
def get_decompressor(io) # :nodoc:
|
|
174
180
|
return ::Zip::NullDecompressor if @current_entry.nil?
|
|
175
181
|
|
|
176
182
|
decompressed_size =
|
|
@@ -188,7 +194,7 @@ module Zip
|
|
|
188
194
|
raise ::Zip::CompressionMethodError, @current_entry.compression_method
|
|
189
195
|
end
|
|
190
196
|
|
|
191
|
-
decompressor_class.new(
|
|
197
|
+
decompressor_class.new(io, decompressed_size)
|
|
192
198
|
end
|
|
193
199
|
|
|
194
200
|
def produce_input # :nodoc:
|
|
@@ -196,7 +202,7 @@ module Zip
|
|
|
196
202
|
end
|
|
197
203
|
|
|
198
204
|
def input_finished? # :nodoc:
|
|
199
|
-
@decompressor.eof
|
|
205
|
+
@decompressor.eof?
|
|
200
206
|
end
|
|
201
207
|
end
|
|
202
208
|
end
|
|
@@ -117,11 +117,12 @@ module Zip
|
|
|
117
117
|
|
|
118
118
|
alias each each_line
|
|
119
119
|
|
|
120
|
-
def eof
|
|
120
|
+
def eof?
|
|
121
121
|
@output_buffer.empty? && input_finished?
|
|
122
122
|
end
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
# Alias for compatibility. Remove for version 4.
|
|
125
|
+
alias eof eof?
|
|
125
126
|
end
|
|
126
127
|
end
|
|
127
128
|
end
|
data/lib/zip/output_stream.rb
CHANGED
|
@@ -29,7 +29,7 @@ module Zip
|
|
|
29
29
|
|
|
30
30
|
# Opens the indicated zip file. If a file with that name already
|
|
31
31
|
# exists it will be overwritten.
|
|
32
|
-
def initialize(file_name, stream: false, encrypter: nil)
|
|
32
|
+
def initialize(file_name, stream: false, encrypter: nil, suppress_extra_fields: false)
|
|
33
33
|
super()
|
|
34
34
|
@file_name = file_name
|
|
35
35
|
@output_stream = if stream
|
|
@@ -43,6 +43,7 @@ module Zip
|
|
|
43
43
|
@cdir = ::Zip::CentralDirectory.new
|
|
44
44
|
@compressor = ::Zip::NullCompressor.instance
|
|
45
45
|
@encrypter = encrypter || ::Zip::NullEncrypter.new
|
|
46
|
+
@suppress_extra_fields = suppress_extra_fields
|
|
46
47
|
@closed = false
|
|
47
48
|
@current_entry = nil
|
|
48
49
|
end
|
|
@@ -51,19 +52,21 @@ module Zip
|
|
|
51
52
|
# Same as #initialize but if a block is passed the opened
|
|
52
53
|
# stream is passed to the block and closed when the block
|
|
53
54
|
# returns.
|
|
54
|
-
def open(file_name, encrypter: nil)
|
|
55
|
+
def open(file_name, encrypter: nil, suppress_extra_fields: false)
|
|
55
56
|
return new(file_name) unless block_given?
|
|
56
57
|
|
|
57
|
-
zos = new(file_name, stream: false, encrypter: encrypter
|
|
58
|
+
zos = new(file_name, stream: false, encrypter: encrypter,
|
|
59
|
+
suppress_extra_fields: suppress_extra_fields)
|
|
58
60
|
yield zos
|
|
59
61
|
ensure
|
|
60
62
|
zos.close if zos
|
|
61
63
|
end
|
|
62
64
|
|
|
63
65
|
# Same as #open but writes to a filestream instead
|
|
64
|
-
def write_buffer(io = ::StringIO.new, encrypter: nil)
|
|
66
|
+
def write_buffer(io = ::StringIO.new, encrypter: nil, suppress_extra_fields: false)
|
|
65
67
|
io.binmode if io.respond_to?(:binmode)
|
|
66
|
-
zos = new(io, stream: true, encrypter: encrypter
|
|
68
|
+
zos = new(io, stream: true, encrypter: encrypter,
|
|
69
|
+
suppress_extra_fields: suppress_extra_fields)
|
|
67
70
|
yield zos
|
|
68
71
|
zos.close_buffer
|
|
69
72
|
end
|
|
@@ -75,7 +78,7 @@ module Zip
|
|
|
75
78
|
|
|
76
79
|
finalize_current_entry
|
|
77
80
|
update_local_headers
|
|
78
|
-
@cdir.write_to_stream(@output_stream)
|
|
81
|
+
@cdir.write_to_stream(@output_stream, suppress_extra_fields: @suppress_extra_fields)
|
|
79
82
|
@output_stream.close
|
|
80
83
|
@closed = true
|
|
81
84
|
end
|
|
@@ -86,7 +89,7 @@ module Zip
|
|
|
86
89
|
|
|
87
90
|
finalize_current_entry
|
|
88
91
|
update_local_headers
|
|
89
|
-
@cdir.write_to_stream(@output_stream)
|
|
92
|
+
@cdir.write_to_stream(@output_stream, suppress_extra_fields: @suppress_extra_fields)
|
|
90
93
|
@closed = true
|
|
91
94
|
@output_stream.flush
|
|
92
95
|
@output_stream
|
|
@@ -157,7 +160,7 @@ module Zip
|
|
|
157
160
|
def init_next_entry(entry)
|
|
158
161
|
finalize_current_entry
|
|
159
162
|
@cdir << entry
|
|
160
|
-
entry.write_local_entry(@output_stream)
|
|
163
|
+
entry.write_local_entry(@output_stream, suppress_extra_fields: @suppress_extra_fields)
|
|
161
164
|
@encrypter.reset!
|
|
162
165
|
@output_stream << @encrypter.header(entry.mtime)
|
|
163
166
|
@compressor = get_compressor(entry)
|
|
@@ -178,7 +181,8 @@ module Zip
|
|
|
178
181
|
pos = @output_stream.pos
|
|
179
182
|
@cdir.each do |entry|
|
|
180
183
|
@output_stream.pos = entry.local_header_offset
|
|
181
|
-
entry.write_local_entry(@output_stream,
|
|
184
|
+
entry.write_local_entry(@output_stream, suppress_extra_fields: @suppress_extra_fields,
|
|
185
|
+
rewrite: true)
|
|
182
186
|
end
|
|
183
187
|
@output_stream.pos = pos
|
|
184
188
|
end
|
|
@@ -8,7 +8,7 @@ module Zip
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def read(length = nil, outbuf = +'')
|
|
11
|
-
return (length.nil? || length.zero? ? '' : nil) if eof
|
|
11
|
+
return (length.nil? || length.zero? ? '' : nil) if eof?
|
|
12
12
|
|
|
13
13
|
if length.nil? || (@read_so_far + length) > decompressed_size
|
|
14
14
|
length = decompressed_size - @read_so_far
|
|
@@ -18,11 +18,12 @@ module Zip
|
|
|
18
18
|
input_stream.read(length, outbuf)
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
def eof
|
|
21
|
+
def eof?
|
|
22
22
|
@read_so_far >= decompressed_size
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
# Alias for compatibility. Remove for version 4.
|
|
26
|
+
alias eof eof?
|
|
26
27
|
end
|
|
27
28
|
|
|
28
29
|
::Zip::Decompressor.register(::Zip::COMPRESSION_METHOD_STORE, ::Zip::PassThruDecompressor)
|
data/lib/zip/version.rb
CHANGED
data/lib/zip.rb
CHANGED
data/rubyzip.gemspec
CHANGED
|
@@ -31,9 +31,9 @@ Gem::Specification.new do |s|
|
|
|
31
31
|
s.add_development_dependency 'minitest', '~> 5.25'
|
|
32
32
|
s.add_development_dependency 'rake', '~> 13.2'
|
|
33
33
|
s.add_development_dependency 'rdoc', '~> 6.11'
|
|
34
|
-
s.add_development_dependency 'rubocop', '~> 1.
|
|
35
|
-
s.add_development_dependency 'rubocop-performance', '~> 1.
|
|
36
|
-
s.add_development_dependency 'rubocop-rake', '~> 0.
|
|
34
|
+
s.add_development_dependency 'rubocop', '~> 1.80.2'
|
|
35
|
+
s.add_development_dependency 'rubocop-performance', '~> 1.26.0'
|
|
36
|
+
s.add_development_dependency 'rubocop-rake', '~> 0.7.1'
|
|
37
37
|
s.add_development_dependency 'simplecov', '~> 0.22.0'
|
|
38
38
|
s.add_development_dependency 'simplecov-lcov', '~> 0.8'
|
|
39
39
|
end
|
data/samples/gtk_ruby_zip.rb
CHANGED
data/samples/qtzip.rb
CHANGED
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.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Robert Haines
|
|
@@ -10,7 +10,7 @@ authors:
|
|
|
10
10
|
autorequire:
|
|
11
11
|
bindir: bin
|
|
12
12
|
cert_chain: []
|
|
13
|
-
date: 2025-
|
|
13
|
+
date: 2025-10-14 00:00:00.000000000 Z
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
16
16
|
name: minitest
|
|
@@ -60,42 +60,42 @@ dependencies:
|
|
|
60
60
|
requirements:
|
|
61
61
|
- - "~>"
|
|
62
62
|
- !ruby/object:Gem::Version
|
|
63
|
-
version: 1.
|
|
63
|
+
version: 1.80.2
|
|
64
64
|
type: :development
|
|
65
65
|
prerelease: false
|
|
66
66
|
version_requirements: !ruby/object:Gem::Requirement
|
|
67
67
|
requirements:
|
|
68
68
|
- - "~>"
|
|
69
69
|
- !ruby/object:Gem::Version
|
|
70
|
-
version: 1.
|
|
70
|
+
version: 1.80.2
|
|
71
71
|
- !ruby/object:Gem::Dependency
|
|
72
72
|
name: rubocop-performance
|
|
73
73
|
requirement: !ruby/object:Gem::Requirement
|
|
74
74
|
requirements:
|
|
75
75
|
- - "~>"
|
|
76
76
|
- !ruby/object:Gem::Version
|
|
77
|
-
version: 1.
|
|
77
|
+
version: 1.26.0
|
|
78
78
|
type: :development
|
|
79
79
|
prerelease: false
|
|
80
80
|
version_requirements: !ruby/object:Gem::Requirement
|
|
81
81
|
requirements:
|
|
82
82
|
- - "~>"
|
|
83
83
|
- !ruby/object:Gem::Version
|
|
84
|
-
version: 1.
|
|
84
|
+
version: 1.26.0
|
|
85
85
|
- !ruby/object:Gem::Dependency
|
|
86
86
|
name: rubocop-rake
|
|
87
87
|
requirement: !ruby/object:Gem::Requirement
|
|
88
88
|
requirements:
|
|
89
89
|
- - "~>"
|
|
90
90
|
- !ruby/object:Gem::Version
|
|
91
|
-
version: 0.
|
|
91
|
+
version: 0.7.1
|
|
92
92
|
type: :development
|
|
93
93
|
prerelease: false
|
|
94
94
|
version_requirements: !ruby/object:Gem::Requirement
|
|
95
95
|
requirements:
|
|
96
96
|
- - "~>"
|
|
97
97
|
- !ruby/object:Gem::Version
|
|
98
|
-
version: 0.
|
|
98
|
+
version: 0.7.1
|
|
99
99
|
- !ruby/object:Gem::Dependency
|
|
100
100
|
name: simplecov
|
|
101
101
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -197,9 +197,9 @@ licenses:
|
|
|
197
197
|
- BSD-2-Clause
|
|
198
198
|
metadata:
|
|
199
199
|
bug_tracker_uri: https://github.com/rubyzip/rubyzip/issues
|
|
200
|
-
changelog_uri: https://github.com/rubyzip/rubyzip/blob/v3.
|
|
201
|
-
documentation_uri: https://www.rubydoc.info/gems/rubyzip/3.
|
|
202
|
-
source_code_uri: https://github.com/rubyzip/rubyzip/tree/v3.
|
|
200
|
+
changelog_uri: https://github.com/rubyzip/rubyzip/blob/v3.2.0/Changelog.md
|
|
201
|
+
documentation_uri: https://www.rubydoc.info/gems/rubyzip/3.2.0
|
|
202
|
+
source_code_uri: https://github.com/rubyzip/rubyzip/tree/v3.2.0
|
|
203
203
|
wiki_uri: https://github.com/rubyzip/rubyzip/wiki
|
|
204
204
|
rubygems_mfa_required: 'true'
|
|
205
205
|
post_install_message:
|