rubyzip 3.0.2 → 3.2.2
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 +49 -0
- data/README.md +64 -13
- data/Rakefile +4 -6
- data/lib/zip/central_directory.rb +44 -36
- data/lib/zip/crypto/aes_encryption.rb +120 -0
- data/lib/zip/crypto/decrypted_io.rb +17 -13
- 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 +52 -28
- data/lib/zip/extra_field/aes.rb +50 -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 +12 -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 +31 -12
- 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 +2 -1
- data/lib/zip.rb +2 -0
- data/rubyzip.gemspec +3 -3
- data/samples/gtk_ruby_zip.rb +1 -1
- data/samples/qtzip.rb +1 -1
- metadata +14 -16
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,7 +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
|
+
end
|
|
205
|
+
|
|
206
|
+
# Is this entry encrypted with AES encryption?
|
|
207
|
+
def aes?
|
|
208
|
+
!@extra[:aes].nil?
|
|
204
209
|
end
|
|
205
210
|
|
|
206
211
|
def file_type_is?(type) # :nodoc:
|
|
@@ -382,11 +387,12 @@ module Zip
|
|
|
382
387
|
|
|
383
388
|
read_extra_field(extra, local: true)
|
|
384
389
|
parse_zip64_extra(true)
|
|
390
|
+
parse_aes_extra
|
|
385
391
|
@local_header_size = calculate_local_header_size
|
|
386
392
|
end
|
|
387
393
|
|
|
388
394
|
def pack_local_entry # :nodoc:
|
|
389
|
-
zip64 = @extra[
|
|
395
|
+
zip64 = @extra[:zip64]
|
|
390
396
|
[::Zip::LOCAL_ENTRY_SIGNATURE,
|
|
391
397
|
@version_needed_to_extract, # version needed to extract
|
|
392
398
|
@gp_flags, # @gp_flags
|
|
@@ -400,9 +406,17 @@ module Zip
|
|
|
400
406
|
@extra ? @extra.local_size : 0].pack('VvvvvvVVVvv')
|
|
401
407
|
end
|
|
402
408
|
|
|
403
|
-
def write_local_entry(io, rewrite: false) # :nodoc:
|
|
409
|
+
def write_local_entry(io, suppress_extra_fields: false, rewrite: false) # :nodoc:
|
|
404
410
|
prep_local_zip64_extra
|
|
405
|
-
|
|
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
|
+
|
|
406
420
|
@local_header_offset = io.tell
|
|
407
421
|
|
|
408
422
|
io << pack_local_entry
|
|
@@ -430,10 +444,7 @@ module Zip
|
|
|
430
444
|
_, # diskNumberStart
|
|
431
445
|
@internal_file_attributes,
|
|
432
446
|
@external_file_attributes,
|
|
433
|
-
@local_header_offset
|
|
434
|
-
@name,
|
|
435
|
-
@extra,
|
|
436
|
-
@comment = buf.unpack('VCCvvvvvVVVvvvvvVV')
|
|
447
|
+
@local_header_offset = buf.unpack('VCCvvvvvVVVvvvvvVV')
|
|
437
448
|
end
|
|
438
449
|
|
|
439
450
|
def set_ftype_from_c_dir_entry # :nodoc:
|
|
@@ -511,6 +522,7 @@ module Zip
|
|
|
511
522
|
check_c_dir_entry_comment_size
|
|
512
523
|
set_ftype_from_c_dir_entry
|
|
513
524
|
parse_zip64_extra(false)
|
|
525
|
+
parse_aes_extra
|
|
514
526
|
end
|
|
515
527
|
|
|
516
528
|
def file_stat(path) # :nodoc:
|
|
@@ -559,7 +571,7 @@ module Zip
|
|
|
559
571
|
end
|
|
560
572
|
|
|
561
573
|
def pack_c_dir_entry # :nodoc:
|
|
562
|
-
zip64 = @extra[
|
|
574
|
+
zip64 = @extra[:zip64]
|
|
563
575
|
[
|
|
564
576
|
@header_signature,
|
|
565
577
|
@version, # version of encoding software
|
|
@@ -578,14 +590,11 @@ module Zip
|
|
|
578
590
|
zip64 && zip64.disk_start_number ? 0xFFFF : 0, # disk number start
|
|
579
591
|
@internal_file_attributes, # file type (binary=0, text=1)
|
|
580
592
|
@external_file_attributes, # native filesystem attributes
|
|
581
|
-
zip64 && zip64.relative_header_offset ? 0xFFFFFFFF : @local_header_offset
|
|
582
|
-
@name,
|
|
583
|
-
@extra,
|
|
584
|
-
@comment
|
|
593
|
+
zip64 && zip64.relative_header_offset ? 0xFFFFFFFF : @local_header_offset
|
|
585
594
|
].pack('VCCvvvvvVVVvvvvvVV')
|
|
586
595
|
end
|
|
587
596
|
|
|
588
|
-
def write_c_dir_entry(io) # :nodoc:
|
|
597
|
+
def write_c_dir_entry(io, suppress_extra_fields: false) # :nodoc:
|
|
589
598
|
prep_cdir_zip64_extra
|
|
590
599
|
|
|
591
600
|
case @fstype
|
|
@@ -607,6 +616,7 @@ module Zip
|
|
|
607
616
|
end
|
|
608
617
|
end
|
|
609
618
|
|
|
619
|
+
@extra.suppress_fields!(suppress_extra_fields) if suppress_extra_fields
|
|
610
620
|
io << pack_c_dir_entry
|
|
611
621
|
|
|
612
622
|
io << @name
|
|
@@ -667,7 +677,7 @@ module Zip
|
|
|
667
677
|
when 'file'
|
|
668
678
|
if name_is_directory?
|
|
669
679
|
raise ArgumentError,
|
|
670
|
-
"entry name '#{
|
|
680
|
+
"entry name '#{@name}' indicates a directory entry, but " \
|
|
671
681
|
"'#{src_path}' is not a directory"
|
|
672
682
|
end
|
|
673
683
|
:file
|
|
@@ -677,7 +687,7 @@ module Zip
|
|
|
677
687
|
when 'link'
|
|
678
688
|
if name_is_directory?
|
|
679
689
|
raise ArgumentError,
|
|
680
|
-
"entry name '#{
|
|
690
|
+
"entry name '#{@name}' indicates a directory entry, but " \
|
|
681
691
|
"'#{src_path}' is not a directory"
|
|
682
692
|
end
|
|
683
693
|
:symlink
|
|
@@ -792,14 +802,28 @@ module Zip
|
|
|
792
802
|
return unless zip64?
|
|
793
803
|
|
|
794
804
|
if for_local_header
|
|
795
|
-
@size, @compressed_size = @extra[
|
|
805
|
+
@size, @compressed_size = @extra[:zip64].parse(@size, @compressed_size)
|
|
796
806
|
else
|
|
797
|
-
@size, @compressed_size, @local_header_offset = @extra[
|
|
807
|
+
@size, @compressed_size, @local_header_offset = @extra[:zip64].parse(
|
|
798
808
|
@size, @compressed_size, @local_header_offset
|
|
799
809
|
)
|
|
800
810
|
end
|
|
801
811
|
end
|
|
802
812
|
|
|
813
|
+
def parse_aes_extra # :nodoc:
|
|
814
|
+
return unless aes?
|
|
815
|
+
|
|
816
|
+
if @extra[:aes].vendor_id != 'AE'
|
|
817
|
+
raise Error, "Unsupported encryption method #{@extra[:aes].vendor_id}"
|
|
818
|
+
end
|
|
819
|
+
|
|
820
|
+
unless ::Zip::AESEncryption::VERSIONS.include? @extra[:aes].vendor_version
|
|
821
|
+
raise Error, "Unsupported encryption style #{@extra[:aes].vendor_version}"
|
|
822
|
+
end
|
|
823
|
+
|
|
824
|
+
@compression_method = @extra[:aes].compression_method if ftype != :directory
|
|
825
|
+
end
|
|
826
|
+
|
|
803
827
|
# For DEFLATED compression *only*: set the general purpose flags 1 and 2 to
|
|
804
828
|
# indicate compression level. This seems to be mainly cosmetic but they are
|
|
805
829
|
# generally set by other tools - including in docx files. It is these flags
|
|
@@ -830,7 +854,7 @@ module Zip
|
|
|
830
854
|
# If we already have a ZIP64 extra (placeholder) then we must fill it in.
|
|
831
855
|
if zip64? || @size.nil? || @size >= 0xFFFFFFFF || @compressed_size >= 0xFFFFFFFF
|
|
832
856
|
@version_needed_to_extract = VERSION_NEEDED_TO_EXTRACT_ZIP64
|
|
833
|
-
zip64 = @extra[
|
|
857
|
+
zip64 = @extra[:zip64] || @extra.create(:zip64)
|
|
834
858
|
|
|
835
859
|
# Local header always includes size and compressed size.
|
|
836
860
|
zip64.original_size = @size || 0
|
|
@@ -844,7 +868,7 @@ module Zip
|
|
|
844
868
|
if (@size && @size >= 0xFFFFFFFF) || @compressed_size >= 0xFFFFFFFF ||
|
|
845
869
|
@local_header_offset >= 0xFFFFFFFF
|
|
846
870
|
@version_needed_to_extract = VERSION_NEEDED_TO_EXTRACT_ZIP64
|
|
847
|
-
zip64 = @extra[
|
|
871
|
+
zip64 = @extra[:zip64] || @extra.create(:zip64)
|
|
848
872
|
|
|
849
873
|
# Central directory entry entries include whichever fields are necessary.
|
|
850
874
|
zip64.original_size = @size if @size && @size >= 0xFFFFFFFF
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Zip
|
|
4
|
+
# Info-ZIP Extra for AES encryption
|
|
5
|
+
class ExtraField::AES < ExtraField::Generic # :nodoc:
|
|
6
|
+
attr_reader :vendor_version, :vendor_id, :encryption_strength, :compression_method
|
|
7
|
+
|
|
8
|
+
HEADER_ID = [0x9901].pack('v')
|
|
9
|
+
register_map
|
|
10
|
+
|
|
11
|
+
def initialize(binstr = nil)
|
|
12
|
+
@vendor_version = nil
|
|
13
|
+
@vendor_id = nil
|
|
14
|
+
@encryption_strength = nil
|
|
15
|
+
@compression_method = nil
|
|
16
|
+
binstr && merge(binstr)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def ==(other)
|
|
20
|
+
@vendor_version == other.vendor_version &&
|
|
21
|
+
@vendor_id == other.vendor_id &&
|
|
22
|
+
@encryption_strength == other.encryption_strength &&
|
|
23
|
+
@compression_method == other.compression_method
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def merge(binstr)
|
|
27
|
+
return if binstr.empty?
|
|
28
|
+
|
|
29
|
+
size, content = initial_parse(binstr)
|
|
30
|
+
(size && content) || return
|
|
31
|
+
|
|
32
|
+
@vendor_version, @vendor_id,
|
|
33
|
+
@encryption_strength, @compression_method = content.unpack('va2Cv')
|
|
34
|
+
end
|
|
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
|
+
|
|
41
|
+
def pack_for_local
|
|
42
|
+
[@vendor_version, @vendor_id,
|
|
43
|
+
@encryption_strength, @compression_method].pack('va2Cv')
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def pack_for_c_dir
|
|
47
|
+
pack_for_local
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -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
|
|
@@ -91,6 +98,7 @@ require 'zip/extra_field/old_unix'
|
|
|
91
98
|
require 'zip/extra_field/unix'
|
|
92
99
|
require 'zip/extra_field/zip64'
|
|
93
100
|
require 'zip/extra_field/ntfs'
|
|
101
|
+
require 'zip/extra_field/aes'
|
|
94
102
|
|
|
95
103
|
# Copyright (C) 2002, 2003 Thomas Sondergaard
|
|
96
104
|
# rubyzip is free software; you can redistribute it and/or
|
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,29 +135,48 @@ 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)
|
|
156
162
|
|
|
157
|
-
|
|
163
|
+
compressed_size =
|
|
164
|
+
if @current_entry.incomplete? && @current_entry.crc == 0 &&
|
|
165
|
+
@current_entry.compressed_size == 0 && @complete_entry
|
|
166
|
+
@complete_entry.compressed_size
|
|
167
|
+
else
|
|
168
|
+
@current_entry.compressed_size
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
if @decrypter.kind_of?(::Zip::AESDecrypter)
|
|
172
|
+
compressed_size -= @decrypter.header_bytesize
|
|
173
|
+
compressed_size -= ::Zip::AESEncryption::AUTHENTICATION_CODE_LENGTH
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
::Zip::DecryptedIo.new(@archive_io, @decrypter, compressed_size)
|
|
158
177
|
end
|
|
159
178
|
|
|
160
|
-
def get_decompressor # :nodoc:
|
|
179
|
+
def get_decompressor(io) # :nodoc:
|
|
161
180
|
return ::Zip::NullDecompressor if @current_entry.nil?
|
|
162
181
|
|
|
163
182
|
decompressed_size =
|
|
@@ -175,7 +194,7 @@ module Zip
|
|
|
175
194
|
raise ::Zip::CompressionMethodError, @current_entry.compression_method
|
|
176
195
|
end
|
|
177
196
|
|
|
178
|
-
decompressor_class.new(
|
|
197
|
+
decompressor_class.new(io, decompressed_size)
|
|
179
198
|
end
|
|
180
199
|
|
|
181
200
|
def produce_input # :nodoc:
|
|
@@ -183,7 +202,7 @@ module Zip
|
|
|
183
202
|
end
|
|
184
203
|
|
|
185
204
|
def input_finished? # :nodoc:
|
|
186
|
-
@decompressor.eof
|
|
205
|
+
@decompressor.eof?
|
|
187
206
|
end
|
|
188
207
|
end
|
|
189
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
|