rubyzip 2.4.rc1 → 3.0.0.alpha
Sign up to get free protection for your applications and to get access to all the features.
- 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 +263 -199
- 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 +143 -264
- 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 -16
- 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 +81 -46
- 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 = ''.force_encoding(Encoding::BINARY)
|
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
|