rubyzip 1.0.0 → 2.4.1
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 +6 -14
- data/README.md +231 -46
- data/Rakefile +13 -5
- data/TODO +0 -1
- data/lib/zip/central_directory.rb +64 -29
- data/lib/zip/compressor.rb +1 -2
- data/lib/zip/constants.rb +59 -5
- data/lib/zip/crypto/decrypted_io.rb +40 -0
- data/lib/zip/crypto/encryption.rb +11 -0
- data/lib/zip/crypto/null_encryption.rb +43 -0
- data/lib/zip/crypto/traditional_encryption.rb +99 -0
- data/lib/zip/decompressor.rb +22 -4
- data/lib/zip/deflater.rb +11 -6
- data/lib/zip/dos_time.rb +27 -16
- data/lib/zip/entry.rb +299 -163
- data/lib/zip/entry_set.rb +22 -20
- data/lib/zip/errors.rb +17 -6
- data/lib/zip/extra_field/generic.rb +15 -14
- data/lib/zip/extra_field/ntfs.rb +94 -0
- data/lib/zip/extra_field/old_unix.rb +46 -0
- data/lib/zip/extra_field/universal_time.rb +46 -16
- data/lib/zip/extra_field/unix.rb +10 -9
- data/lib/zip/extra_field/zip64.rb +46 -6
- data/lib/zip/extra_field/zip64_placeholder.rb +15 -0
- data/lib/zip/extra_field.rb +32 -18
- data/lib/zip/file.rb +260 -160
- data/lib/zip/filesystem.rb +297 -276
- data/lib/zip/inflater.rb +23 -34
- data/lib/zip/input_stream.rb +130 -82
- data/lib/zip/ioextras/abstract_input_stream.rb +36 -22
- data/lib/zip/ioextras/abstract_output_stream.rb +4 -6
- data/lib/zip/ioextras.rb +4 -6
- data/lib/zip/null_compressor.rb +2 -2
- data/lib/zip/null_decompressor.rb +4 -12
- data/lib/zip/null_input_stream.rb +2 -1
- data/lib/zip/output_stream.rb +75 -45
- data/lib/zip/pass_thru_compressor.rb +6 -6
- data/lib/zip/pass_thru_decompressor.rb +14 -24
- data/lib/zip/streamable_directory.rb +3 -3
- data/lib/zip/streamable_stream.rb +21 -16
- data/lib/zip/version.rb +1 -1
- data/lib/zip.rb +42 -3
- data/samples/example.rb +30 -40
- data/samples/example_filesystem.rb +16 -18
- data/samples/example_recursive.rb +33 -28
- data/samples/gtk_ruby_zip.rb +84 -0
- data/samples/qtzip.rb +25 -34
- data/samples/write_simple.rb +10 -13
- data/samples/zipfind.rb +38 -45
- metadata +129 -28
- data/NEWS +0 -182
- data/samples/gtkRubyzip.rb +0 -86
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module Zip
|
|
2
|
+
class DecryptedIo #:nodoc:all
|
|
3
|
+
CHUNK_SIZE = 32_768
|
|
4
|
+
|
|
5
|
+
def initialize(io, decrypter)
|
|
6
|
+
@io = io
|
|
7
|
+
@decrypter = decrypter
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def read(length = nil, outbuf = +'')
|
|
11
|
+
return (length.nil? || length.zero? ? '' : nil) if eof
|
|
12
|
+
|
|
13
|
+
while length.nil? || (buffer.bytesize < length)
|
|
14
|
+
break if input_finished?
|
|
15
|
+
|
|
16
|
+
buffer << produce_input
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
outbuf.replace(buffer.slice!(0...(length || output_buffer.bytesize)))
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def eof
|
|
25
|
+
buffer.empty? && input_finished?
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def buffer
|
|
29
|
+
@buffer ||= +''
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def input_finished?
|
|
33
|
+
@io.eof
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def produce_input
|
|
37
|
+
@decrypter.decrypt(@io.read(CHUNK_SIZE))
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module Zip
|
|
2
|
+
module NullEncryption
|
|
3
|
+
def header_bytesize
|
|
4
|
+
0
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def gp_flags
|
|
8
|
+
0
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class NullEncrypter < Encrypter
|
|
13
|
+
include NullEncryption
|
|
14
|
+
|
|
15
|
+
def header(_mtime)
|
|
16
|
+
''
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def encrypt(data)
|
|
20
|
+
data
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def data_descriptor(_crc32, _compressed_size, _uncomprssed_size)
|
|
24
|
+
''
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def reset!; end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
class NullDecrypter < Decrypter
|
|
31
|
+
include NullEncryption
|
|
32
|
+
|
|
33
|
+
def decrypt(data)
|
|
34
|
+
data
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def reset!(_header); end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Copyright (C) 2002, 2003 Thomas Sondergaard
|
|
42
|
+
# rubyzip is free software; you can redistribute it and/or
|
|
43
|
+
# modify it under the terms of the ruby license.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
module Zip
|
|
2
|
+
module TraditionalEncryption
|
|
3
|
+
def initialize(password)
|
|
4
|
+
@password = password
|
|
5
|
+
reset_keys!
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def header_bytesize
|
|
9
|
+
12
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def gp_flags
|
|
13
|
+
0x0001 | 0x0008
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
protected
|
|
17
|
+
|
|
18
|
+
def reset_keys!
|
|
19
|
+
@key0 = 0x12345678
|
|
20
|
+
@key1 = 0x23456789
|
|
21
|
+
@key2 = 0x34567890
|
|
22
|
+
@password.each_byte do |byte|
|
|
23
|
+
update_keys(byte.chr)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def update_keys(num)
|
|
28
|
+
@key0 = ~Zlib.crc32(num, ~@key0)
|
|
29
|
+
@key1 = ((@key1 + (@key0 & 0xff)) * 134_775_813 + 1) & 0xffffffff
|
|
30
|
+
@key2 = ~Zlib.crc32((@key1 >> 24).chr, ~@key2)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def decrypt_byte
|
|
34
|
+
temp = (@key2 & 0xffff) | 2
|
|
35
|
+
((temp * (temp ^ 1)) >> 8) & 0xff
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
class TraditionalEncrypter < Encrypter
|
|
40
|
+
include TraditionalEncryption
|
|
41
|
+
|
|
42
|
+
def header(mtime)
|
|
43
|
+
[].tap do |header|
|
|
44
|
+
(header_bytesize - 2).times do
|
|
45
|
+
header << Random.rand(0..255)
|
|
46
|
+
end
|
|
47
|
+
header << (mtime.to_binary_dos_time & 0xff)
|
|
48
|
+
header << (mtime.to_binary_dos_time >> 8)
|
|
49
|
+
end.map { |x| encode x }.pack('C*')
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def encrypt(data)
|
|
53
|
+
data.unpack('C*').map { |x| encode x }.pack('C*')
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def data_descriptor(crc32, compressed_size, uncomprssed_size)
|
|
57
|
+
[0x08074b50, crc32, compressed_size, uncomprssed_size].pack('VVVV')
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def reset!
|
|
61
|
+
reset_keys!
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
def encode(num)
|
|
67
|
+
t = decrypt_byte
|
|
68
|
+
update_keys(num.chr)
|
|
69
|
+
t ^ num
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
class TraditionalDecrypter < Decrypter
|
|
74
|
+
include TraditionalEncryption
|
|
75
|
+
|
|
76
|
+
def decrypt(data)
|
|
77
|
+
data.unpack('C*').map { |x| decode x }.pack('C*')
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def reset!(header)
|
|
81
|
+
reset_keys!
|
|
82
|
+
header.each_byte do |x|
|
|
83
|
+
decode x
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
private
|
|
88
|
+
|
|
89
|
+
def decode(num)
|
|
90
|
+
num ^= decrypt_byte
|
|
91
|
+
update_keys(num.chr)
|
|
92
|
+
num
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Copyright (C) 2002, 2003 Thomas Sondergaard
|
|
98
|
+
# rubyzip is free software; you can redistribute it and/or
|
|
99
|
+
# modify it under the terms of the ruby license.
|
data/lib/zip/decompressor.rb
CHANGED
|
@@ -1,9 +1,27 @@
|
|
|
1
1
|
module Zip
|
|
2
|
-
class Decompressor
|
|
3
|
-
CHUNK_SIZE =
|
|
4
|
-
|
|
2
|
+
class Decompressor #:nodoc:all
|
|
3
|
+
CHUNK_SIZE = 32_768
|
|
4
|
+
|
|
5
|
+
def self.decompressor_classes
|
|
6
|
+
@decompressor_classes ||= {}
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def self.register(compression_method, decompressor_class)
|
|
10
|
+
decompressor_classes[compression_method] = decompressor_class
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.find_by_compression_method(compression_method)
|
|
14
|
+
decompressor_classes[compression_method]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
attr_reader :input_stream
|
|
18
|
+
attr_reader :decompressed_size
|
|
19
|
+
|
|
20
|
+
def initialize(input_stream, decompressed_size = nil)
|
|
5
21
|
super()
|
|
6
|
-
|
|
22
|
+
|
|
23
|
+
@input_stream = input_stream
|
|
24
|
+
@decompressed_size = decompressed_size
|
|
7
25
|
end
|
|
8
26
|
end
|
|
9
27
|
end
|
data/lib/zip/deflater.rb
CHANGED
|
@@ -1,23 +1,28 @@
|
|
|
1
1
|
module Zip
|
|
2
2
|
class Deflater < Compressor #:nodoc:all
|
|
3
|
-
|
|
4
|
-
def initialize(output_stream, level = ::Zlib::DEFAULT_COMPRESSION)
|
|
3
|
+
def initialize(output_stream, level = Zip.default_compression, encrypter = NullEncrypter.new)
|
|
5
4
|
super()
|
|
6
5
|
@output_stream = output_stream
|
|
7
6
|
@zlib_deflater = ::Zlib::Deflate.new(level, -::Zlib::MAX_WBITS)
|
|
8
7
|
@size = 0
|
|
9
8
|
@crc = ::Zlib.crc32
|
|
9
|
+
@encrypter = encrypter
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
def <<
|
|
12
|
+
def <<(data)
|
|
13
13
|
val = data.to_s
|
|
14
|
-
@crc = Zlib
|
|
14
|
+
@crc = Zlib.crc32(val, @crc)
|
|
15
15
|
@size += val.bytesize
|
|
16
|
-
|
|
16
|
+
buffer = @zlib_deflater.deflate(data)
|
|
17
|
+
if buffer.empty?
|
|
18
|
+
@output_stream
|
|
19
|
+
else
|
|
20
|
+
@output_stream << @encrypter.encrypt(buffer)
|
|
21
|
+
end
|
|
17
22
|
end
|
|
18
23
|
|
|
19
24
|
def finish
|
|
20
|
-
@output_stream << @zlib_deflater.finish until @zlib_deflater.finished?
|
|
25
|
+
@output_stream << @encrypter.encrypt(@zlib_deflater.finish) until @zlib_deflater.finished?
|
|
21
26
|
end
|
|
22
27
|
|
|
23
28
|
attr_reader :size, :crc
|
data/lib/zip/dos_time.rb
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
module Zip
|
|
2
2
|
class DOSTime < Time #:nodoc:all
|
|
3
|
-
|
|
4
|
-
#MS-DOS File Date and Time format as used in Interrupt 21H Function 57H:
|
|
3
|
+
# MS-DOS File Date and Time format as used in Interrupt 21H Function 57H:
|
|
5
4
|
|
|
6
5
|
# Register CX, the Time:
|
|
7
6
|
# Bits 0-4 2 second increments (0-29)
|
|
@@ -14,32 +13,44 @@ module Zip
|
|
|
14
13
|
# bits 9-15 year (four digit year minus 1980)
|
|
15
14
|
|
|
16
15
|
def to_binary_dos_time
|
|
17
|
-
(sec/2) +
|
|
16
|
+
(sec / 2) +
|
|
18
17
|
(min << 5) +
|
|
19
18
|
(hour << 11)
|
|
20
19
|
end
|
|
21
20
|
|
|
22
21
|
def to_binary_dos_date
|
|
23
|
-
|
|
22
|
+
day +
|
|
24
23
|
(month << 5) +
|
|
25
24
|
((year - 1980) << 9)
|
|
26
25
|
end
|
|
27
26
|
|
|
28
|
-
# Dos time is only stored with two seconds accuracy
|
|
29
27
|
def dos_equals(other)
|
|
30
|
-
|
|
28
|
+
Zip.warn_about_v3_api('DOSTime#dos_equals')
|
|
29
|
+
|
|
30
|
+
self == other
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Dos time is only stored with two seconds accuracy.
|
|
34
|
+
def <=>(other)
|
|
35
|
+
return unless other.kind_of?(Time)
|
|
36
|
+
|
|
37
|
+
(to_i / 2) <=> (other.to_i / 2)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Create a DOSTime instance from a vanilla Time instance.
|
|
41
|
+
def self.from_time(time)
|
|
42
|
+
local(time.year, time.month, time.day, time.hour, time.min, time.sec)
|
|
31
43
|
end
|
|
32
44
|
|
|
33
|
-
def self.parse_binary_dos_format(
|
|
34
|
-
second = 2 * (0b11111 &
|
|
35
|
-
minute = (0b11111100000 &
|
|
36
|
-
hour = (0b1111100000000000 &
|
|
37
|
-
day = (0b11111 &
|
|
38
|
-
month = (0b111100000 &
|
|
39
|
-
year = ((0b1111111000000000 &
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
end
|
|
45
|
+
def self.parse_binary_dos_format(bin_dos_date, bin_dos_time)
|
|
46
|
+
second = 2 * (0b11111 & bin_dos_time)
|
|
47
|
+
minute = (0b11111100000 & bin_dos_time) >> 5
|
|
48
|
+
hour = (0b1111100000000000 & bin_dos_time) >> 11
|
|
49
|
+
day = (0b11111 & bin_dos_date)
|
|
50
|
+
month = (0b111100000 & bin_dos_date) >> 5
|
|
51
|
+
year = ((0b1111111000000000 & bin_dos_date) >> 9) + 1980
|
|
52
|
+
|
|
53
|
+
local(year, month, day, hour, minute, second)
|
|
43
54
|
end
|
|
44
55
|
end
|
|
45
56
|
end
|