rubyzip 2.4.1 → 3.0.0.alpha
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 +368 -0
- data/README.md +112 -37
- data/Rakefile +11 -7
- data/lib/zip/central_directory.rb +164 -118
- data/lib/zip/compressor.rb +3 -1
- data/lib/zip/constants.rb +25 -21
- data/lib/zip/crypto/decrypted_io.rb +3 -1
- data/lib/zip/crypto/encryption.rb +4 -2
- data/lib/zip/crypto/null_encryption.rb +5 -3
- data/lib/zip/crypto/traditional_encryption.rb +5 -3
- data/lib/zip/decompressor.rb +4 -3
- data/lib/zip/deflater.rb +10 -8
- data/lib/zip/dirtyable.rb +32 -0
- data/lib/zip/dos_time.rb +32 -3
- data/lib/zip/entry.rb +262 -198
- data/lib/zip/entry_set.rb +9 -7
- data/lib/zip/errors.rb +115 -16
- data/lib/zip/extra_field/generic.rb +3 -10
- data/lib/zip/extra_field/ntfs.rb +4 -2
- data/lib/zip/extra_field/old_unix.rb +3 -1
- data/lib/zip/extra_field/universal_time.rb +3 -1
- data/lib/zip/extra_field/unix.rb +5 -3
- data/lib/zip/extra_field/unknown.rb +33 -0
- data/lib/zip/extra_field/zip64.rb +12 -5
- data/lib/zip/extra_field.rb +15 -21
- data/lib/zip/file.rb +144 -265
- data/lib/zip/file_split.rb +97 -0
- data/lib/zip/filesystem/dir.rb +86 -0
- data/lib/zip/filesystem/directory_iterator.rb +48 -0
- data/lib/zip/filesystem/file.rb +262 -0
- data/lib/zip/filesystem/file_stat.rb +110 -0
- data/lib/zip/filesystem/zip_file_name_mapper.rb +81 -0
- data/lib/zip/filesystem.rb +26 -595
- data/lib/zip/inflater.rb +7 -5
- data/lib/zip/input_stream.rb +44 -39
- data/lib/zip/ioextras/abstract_input_stream.rb +14 -9
- data/lib/zip/ioextras/abstract_output_stream.rb +5 -3
- data/lib/zip/ioextras.rb +6 -6
- data/lib/zip/null_compressor.rb +3 -1
- data/lib/zip/null_decompressor.rb +3 -1
- data/lib/zip/null_input_stream.rb +3 -1
- data/lib/zip/output_stream.rb +47 -48
- data/lib/zip/pass_thru_compressor.rb +3 -1
- data/lib/zip/pass_thru_decompressor.rb +4 -2
- data/lib/zip/streamable_directory.rb +3 -1
- data/lib/zip/streamable_stream.rb +3 -0
- data/lib/zip/version.rb +3 -1
- data/lib/zip.rb +15 -20
- data/rubyzip.gemspec +38 -0
- data/samples/example.rb +8 -3
- data/samples/example_filesystem.rb +2 -1
- data/samples/example_recursive.rb +3 -1
- data/samples/gtk_ruby_zip.rb +4 -2
- data/samples/qtzip.rb +6 -5
- data/samples/write_simple.rb +1 -0
- data/samples/zipfind.rb +1 -0
- metadata +84 -50
- data/TODO +0 -15
- data/lib/zip/extra_field/zip64_placeholder.rb +0 -15
data/lib/zip/entry_set.rb
CHANGED
@@ -1,7 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Zip
|
2
|
-
class EntrySet
|
4
|
+
class EntrySet # :nodoc:all
|
3
5
|
include Enumerable
|
4
|
-
|
6
|
+
|
7
|
+
attr_reader :entry_set
|
8
|
+
protected :entry_set
|
5
9
|
|
6
10
|
def initialize(an_enumerable = [])
|
7
11
|
super()
|
@@ -33,10 +37,8 @@ module Zip
|
|
33
37
|
entry if @entry_set.delete(to_key(entry))
|
34
38
|
end
|
35
39
|
|
36
|
-
def each
|
37
|
-
|
38
|
-
yield(value)
|
39
|
-
end
|
40
|
+
def each(&block)
|
41
|
+
entries.each(&block)
|
40
42
|
end
|
41
43
|
|
42
44
|
def entries
|
@@ -70,7 +72,7 @@ module Zip
|
|
70
72
|
protected
|
71
73
|
|
72
74
|
def sorted_entries
|
73
|
-
::Zip.sort_entries ?
|
75
|
+
::Zip.sort_entries ? @entry_set.sort.to_h : @entry_set
|
74
76
|
end
|
75
77
|
|
76
78
|
private
|
data/lib/zip/errors.rb
CHANGED
@@ -1,19 +1,118 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Zip
|
4
|
+
# The superclass for all rubyzip error types. Simply rescue this one if
|
5
|
+
# you don't need to know what sort of error has been raised.
|
2
6
|
class Error < StandardError; end
|
3
|
-
|
4
|
-
|
5
|
-
class CompressionMethodError < Error
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
7
|
+
|
8
|
+
# Error raised if an unsupported compression method is used.
|
9
|
+
class CompressionMethodError < Error
|
10
|
+
attr_reader :compression_method
|
11
|
+
|
12
|
+
def initialize(method)
|
13
|
+
super()
|
14
|
+
@compression_method = method
|
15
|
+
end
|
16
|
+
|
17
|
+
def message
|
18
|
+
"Unsupported compression method: #{COMPRESSION_METHODS[@compression_method]}."
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Error raised if there is a problem while decompressing an archive entry.
|
23
|
+
class DecompressionError < Error
|
24
|
+
attr_reader :zlib_error
|
25
|
+
|
26
|
+
def initialize(zlib_error)
|
27
|
+
super()
|
28
|
+
@zlib_error = zlib_error
|
29
|
+
end
|
30
|
+
|
31
|
+
def message
|
32
|
+
"Zlib error ('#{@zlib_error.message}') while inflating."
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Error raised when trying to extract an archive entry over an
|
37
|
+
# existing file.
|
38
|
+
class DestinationExistsError < Error
|
39
|
+
def initialize(destination)
|
40
|
+
super()
|
41
|
+
@destination = destination
|
42
|
+
end
|
43
|
+
|
44
|
+
def message
|
45
|
+
"Cannot create file or directory '#{@destination}'. " \
|
46
|
+
'A file already exists with that name.'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Error raised when trying to add an entry to an archive where the
|
51
|
+
# entry name already exists.
|
52
|
+
class EntryExistsError < Error
|
53
|
+
def initialize(source, name)
|
54
|
+
super()
|
55
|
+
@source = source
|
56
|
+
@name = name
|
57
|
+
end
|
58
|
+
|
59
|
+
def message
|
60
|
+
"'#{@source}' failed. Entry #{@name} already exists."
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Error raised when an entry name is invalid.
|
65
|
+
class EntryNameError < Error
|
66
|
+
def initialize(name = nil)
|
67
|
+
super()
|
68
|
+
@name = name
|
69
|
+
end
|
70
|
+
|
71
|
+
def message
|
72
|
+
if @name.nil?
|
73
|
+
'Illegal entry name. Names must have fewer than 65,536 characters.'
|
74
|
+
else
|
75
|
+
"Illegal entry name '#{@name}'. Names must not start with '/'."
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Error raised if an entry is larger on extraction than it is advertised
|
81
|
+
# to be.
|
82
|
+
class EntrySizeError < Error
|
83
|
+
attr_reader :entry
|
84
|
+
|
85
|
+
def initialize(entry)
|
86
|
+
super()
|
87
|
+
@entry = entry
|
88
|
+
end
|
89
|
+
|
90
|
+
def message
|
91
|
+
"Entry '#{@entry.name}' should be #{@entry.size}B, but is larger when inflated."
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Error raised if a split archive is read. Rubyzip does not support reading
|
96
|
+
# split archives.
|
97
|
+
class SplitArchiveError < Error
|
98
|
+
def message
|
99
|
+
'Rubyzip cannot extract from split archives at this time.'
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Error raised if there is not enough metadata for the entry to be streamed.
|
104
|
+
class StreamingError < Error
|
105
|
+
attr_reader :entry
|
106
|
+
|
107
|
+
def initialize(entry)
|
108
|
+
super()
|
109
|
+
@entry = entry
|
110
|
+
end
|
111
|
+
|
112
|
+
def message
|
113
|
+
"The local header of this entry ('#{@entry.name}') does not contain " \
|
114
|
+
'the correct metadata for `Zip::InputStream` to be able to ' \
|
115
|
+
'uncompress it. Please use `Zip::File` instead of `Zip::InputStream`.'
|
116
|
+
end
|
117
|
+
end
|
19
118
|
end
|
@@ -1,5 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Zip
|
2
|
-
class ExtraField::Generic
|
4
|
+
class ExtraField::Generic # :nodoc:
|
3
5
|
def self.register_map
|
4
6
|
return unless const_defined?(:HEADER_ID)
|
5
7
|
|
@@ -22,15 +24,6 @@ module Zip
|
|
22
24
|
[binstr[2, 2].unpack1('v'), binstr[4..-1]]
|
23
25
|
end
|
24
26
|
|
25
|
-
def ==(other)
|
26
|
-
return false if self.class != other.class
|
27
|
-
|
28
|
-
each do |k, v|
|
29
|
-
return false if v != other[k]
|
30
|
-
end
|
31
|
-
true
|
32
|
-
end
|
33
|
-
|
34
27
|
def to_local_bin
|
35
28
|
s = pack_for_local
|
36
29
|
self.class.const_get(:HEADER_ID) + [s.bytesize].pack('v') << s
|
data/lib/zip/extra_field/ntfs.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Zip
|
2
4
|
# PKWARE NTFS Extra Field (0x000a)
|
3
5
|
# Only Tag 0x0001 is supported
|
4
|
-
class ExtraField::NTFS < ExtraField::Generic
|
6
|
+
class ExtraField::NTFS < ExtraField::Generic # :nodoc:
|
5
7
|
HEADER_ID = [0x000A].pack('v')
|
6
8
|
register_map
|
7
9
|
|
@@ -51,7 +53,7 @@ module Zip
|
|
51
53
|
# reserved 0 and tag 1
|
52
54
|
s = [0, 1].pack('Vv')
|
53
55
|
|
54
|
-
tag1 = ''.
|
56
|
+
tag1 = (+'').force_encoding(Encoding::BINARY)
|
55
57
|
if @mtime
|
56
58
|
tag1 << [to_ntfs_time(@mtime)].pack('Q<')
|
57
59
|
if @atime
|
data/lib/zip/extra_field/unix.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Zip
|
2
4
|
# Info-ZIP Extra for UNIX uid/gid
|
3
|
-
class ExtraField::IUnix < ExtraField::Generic
|
5
|
+
class ExtraField::IUnix < ExtraField::Generic # :nodoc:
|
4
6
|
HEADER_ID = 'Ux'
|
5
7
|
register_map
|
6
8
|
|
@@ -20,8 +22,8 @@ module Zip
|
|
20
22
|
return if !size || size == 0
|
21
23
|
|
22
24
|
uid, gid = content.unpack('vv')
|
23
|
-
@uid
|
24
|
-
@gid
|
25
|
+
@uid = uid
|
26
|
+
@gid = gid
|
25
27
|
end
|
26
28
|
|
27
29
|
def ==(other)
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Zip
|
4
|
+
# A class to hold unknown extra fields so that they are preserved.
|
5
|
+
class ExtraField::Unknown # :nodoc:
|
6
|
+
def initialize
|
7
|
+
@local_bin = +''
|
8
|
+
@cdir_bin = +''
|
9
|
+
end
|
10
|
+
|
11
|
+
def merge(binstr, local: false)
|
12
|
+
return if binstr.empty?
|
13
|
+
|
14
|
+
if local
|
15
|
+
@local_bin << binstr
|
16
|
+
else
|
17
|
+
@cdir_bin << binstr
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_local_bin
|
22
|
+
@local_bin
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_c_dir_bin
|
26
|
+
@cdir_bin
|
27
|
+
end
|
28
|
+
|
29
|
+
def ==(other)
|
30
|
+
@local_bin == other.to_local_bin && @cdir_bin == other.to_c_dir_bin
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -1,7 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Zip
|
2
4
|
# Info-ZIP Extra for Zip64 size
|
3
|
-
class ExtraField::Zip64 < ExtraField::Generic
|
4
|
-
attr_accessor :
|
5
|
+
class ExtraField::Zip64 < ExtraField::Generic # :nodoc:
|
6
|
+
attr_accessor :compressed_size, :disk_start_number,
|
7
|
+
:original_size, :relative_header_offset
|
8
|
+
|
5
9
|
HEADER_ID = ['0100'].pack('H*')
|
6
10
|
register_map
|
7
11
|
|
@@ -36,7 +40,9 @@ module Zip
|
|
36
40
|
def parse(original_size, compressed_size, relative_header_offset = nil, disk_start_number = nil)
|
37
41
|
@original_size = extract(8, 'Q<') if original_size == 0xFFFFFFFF
|
38
42
|
@compressed_size = extract(8, 'Q<') if compressed_size == 0xFFFFFFFF
|
39
|
-
|
43
|
+
if relative_header_offset && relative_header_offset == 0xFFFFFFFF
|
44
|
+
@relative_header_offset = extract(8, 'Q<')
|
45
|
+
end
|
40
46
|
@disk_start_number = extract(4, 'V') if disk_start_number && disk_start_number == 0xFFFF
|
41
47
|
@content = nil
|
42
48
|
[@original_size || original_size,
|
@@ -51,7 +57,8 @@ module Zip
|
|
51
57
|
private :extract
|
52
58
|
|
53
59
|
def pack_for_local
|
54
|
-
#
|
60
|
+
# Local header entries must contain original size and compressed size;
|
61
|
+
# other fields do not apply.
|
55
62
|
return '' unless @original_size && @compressed_size
|
56
63
|
|
57
64
|
[@original_size, @compressed_size].pack('Q<Q<')
|
@@ -59,7 +66,7 @@ module Zip
|
|
59
66
|
|
60
67
|
def pack_for_c_dir
|
61
68
|
# central directory entries contain only fields that didn't fit in the main entry part
|
62
|
-
packed = ''.
|
69
|
+
packed = (+'').force_encoding('BINARY')
|
63
70
|
packed << [@original_size].pack('Q<') if @original_size
|
64
71
|
packed << [@compressed_size].pack('Q<') if @compressed_size
|
65
72
|
packed << [@relative_header_offset].pack('Q<') if @relative_header_offset
|
data/lib/zip/extra_field.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Zip
|
2
|
-
class ExtraField < Hash
|
4
|
+
class ExtraField < Hash # :nodoc:all
|
3
5
|
ID_MAP = {}
|
4
6
|
|
5
|
-
def initialize(binstr = nil)
|
6
|
-
merge(binstr) if binstr
|
7
|
+
def initialize(binstr = nil, local: false)
|
8
|
+
merge(binstr, local: local) if binstr
|
7
9
|
end
|
8
10
|
|
9
11
|
def extra_field_type_exist(binstr, id, len, index)
|
@@ -16,25 +18,18 @@ module Zip
|
|
16
18
|
end
|
17
19
|
end
|
18
20
|
|
19
|
-
def extra_field_type_unknown(binstr, len, index)
|
20
|
-
|
21
|
+
def extra_field_type_unknown(binstr, len, index, local)
|
22
|
+
self['Unknown'] ||= Unknown.new
|
23
|
+
|
21
24
|
if !len || len + 4 > binstr[index..-1].bytesize
|
22
|
-
self['Unknown']
|
25
|
+
self['Unknown'].merge(binstr[index..-1], local: local)
|
23
26
|
return
|
24
27
|
end
|
25
|
-
self['Unknown'] << binstr[index, len + 4]
|
26
|
-
end
|
27
28
|
|
28
|
-
|
29
|
-
s = +''
|
30
|
-
class << s
|
31
|
-
alias_method :to_c_dir_bin, :to_s
|
32
|
-
alias_method :to_local_bin, :to_s
|
33
|
-
end
|
34
|
-
self['Unknown'] = s
|
29
|
+
self['Unknown'].merge(binstr[index, len + 4], local: local)
|
35
30
|
end
|
36
31
|
|
37
|
-
def merge(binstr)
|
32
|
+
def merge(binstr, local: false)
|
38
33
|
return if binstr.empty?
|
39
34
|
|
40
35
|
i = 0
|
@@ -44,8 +39,7 @@ module Zip
|
|
44
39
|
if id && ID_MAP.member?(id)
|
45
40
|
extra_field_type_exist(binstr, id, len, i)
|
46
41
|
elsif id
|
47
|
-
|
48
|
-
break unless extra_field_type_unknown(binstr, len, i)
|
42
|
+
break unless extra_field_type_unknown(binstr, len, i, local)
|
49
43
|
end
|
50
44
|
i += len + 4
|
51
45
|
end
|
@@ -59,8 +53,8 @@ module Zip
|
|
59
53
|
self[name] = field_class.new
|
60
54
|
end
|
61
55
|
|
62
|
-
#
|
63
|
-
# does not prevent known fields from being read back in
|
56
|
+
# Place Unknown last, so "extra" data that is missing the proper
|
57
|
+
# signature/size does not prevent known fields from being read back in.
|
64
58
|
def ordered_values
|
65
59
|
result = []
|
66
60
|
each { |k, v| k == 'Unknown' ? result.push(v) : result.unshift(v) }
|
@@ -90,12 +84,12 @@ module Zip
|
|
90
84
|
end
|
91
85
|
end
|
92
86
|
|
87
|
+
require 'zip/extra_field/unknown'
|
93
88
|
require 'zip/extra_field/generic'
|
94
89
|
require 'zip/extra_field/universal_time'
|
95
90
|
require 'zip/extra_field/old_unix'
|
96
91
|
require 'zip/extra_field/unix'
|
97
92
|
require 'zip/extra_field/zip64'
|
98
|
-
require 'zip/extra_field/zip64_placeholder'
|
99
93
|
require 'zip/extra_field/ntfs'
|
100
94
|
|
101
95
|
# Copyright (C) 2002, 2003 Thomas Sondergaard
|