rubyzip 0.9.1 → 2.3.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 +7 -0
- data/README.md +354 -0
- data/Rakefile +15 -104
- data/TODO +0 -1
- data/lib/zip/central_directory.rb +212 -0
- data/lib/zip/compressor.rb +9 -0
- data/lib/zip/constants.rb +115 -0
- 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 +31 -0
- data/lib/zip/deflater.rb +34 -0
- data/lib/zip/dos_time.rb +53 -0
- data/lib/zip/entry.rb +719 -0
- data/lib/zip/entry_set.rb +88 -0
- data/lib/zip/errors.rb +19 -0
- data/lib/zip/extra_field/generic.rb +44 -0
- 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 +77 -0
- data/lib/zip/extra_field/unix.rb +39 -0
- data/lib/zip/extra_field/zip64.rb +70 -0
- data/lib/zip/extra_field/zip64_placeholder.rb +15 -0
- data/lib/zip/extra_field.rb +103 -0
- data/lib/zip/file.rb +468 -0
- data/lib/zip/filesystem.rb +643 -0
- data/lib/zip/inflater.rb +54 -0
- data/lib/zip/input_stream.rb +180 -0
- data/lib/zip/ioextras/abstract_input_stream.rb +122 -0
- data/lib/zip/ioextras/abstract_output_stream.rb +43 -0
- data/lib/zip/ioextras.rb +21 -140
- data/lib/zip/null_compressor.rb +15 -0
- data/lib/zip/null_decompressor.rb +19 -0
- data/lib/zip/null_input_stream.rb +10 -0
- data/lib/zip/output_stream.rb +198 -0
- data/lib/zip/pass_thru_compressor.rb +23 -0
- data/lib/zip/pass_thru_decompressor.rb +31 -0
- data/lib/zip/streamable_directory.rb +15 -0
- data/lib/zip/streamable_stream.rb +52 -0
- data/lib/zip/version.rb +3 -0
- data/lib/zip.rb +72 -0
- data/samples/example.rb +44 -32
- data/samples/example_filesystem.rb +16 -19
- data/samples/example_recursive.rb +54 -0
- 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 +182 -91
- data/ChangeLog +0 -1504
- data/NEWS +0 -144
- data/README +0 -72
- data/install.rb +0 -22
- data/lib/download_quizzes.rb +0 -119
- data/lib/quiz1/t/solutions/Bill Guindon/solitaire.rb +0 -205
- data/lib/quiz1/t/solutions/Carlos/solitaire.rb +0 -111
- data/lib/quiz1/t/solutions/Dennis Ranke/solitaire.rb +0 -111
- data/lib/quiz1/t/solutions/Florian Gross/solitaire.rb +0 -301
- data/lib/quiz1/t/solutions/Glen M. Lewis/solitaire.rb +0 -268
- data/lib/quiz1/t/solutions/James Edward Gray II/solitaire.rb +0 -132
- data/lib/quiz1/t/solutions/Jamis Buck/bin/main.rb +0 -13
- data/lib/quiz1/t/solutions/Jamis Buck/lib/cipher.rb +0 -230
- data/lib/quiz1/t/solutions/Jamis Buck/lib/cli.rb +0 -24
- data/lib/quiz1/t/solutions/Jamis Buck/test/tc_deck.rb +0 -30
- data/lib/quiz1/t/solutions/Jamis Buck/test/tc_key-stream.rb +0 -19
- data/lib/quiz1/t/solutions/Jamis Buck/test/tc_keying-algorithms.rb +0 -31
- data/lib/quiz1/t/solutions/Jamis Buck/test/tc_solitaire-cipher.rb +0 -66
- data/lib/quiz1/t/solutions/Jamis Buck/test/tc_unkeyed-algorithm.rb +0 -17
- data/lib/quiz1/t/solutions/Jamis Buck/test/tests.rb +0 -2
- data/lib/quiz1/t/solutions/Jim Menard/solitaire_cypher.rb +0 -204
- data/lib/quiz1/t/solutions/Jim Menard/test.rb +0 -47
- data/lib/quiz1/t/solutions/Moses Hohman/cipher.rb +0 -97
- data/lib/quiz1/t/solutions/Moses Hohman/deck.rb +0 -140
- data/lib/quiz1/t/solutions/Moses Hohman/solitaire.rb +0 -14
- data/lib/quiz1/t/solutions/Moses Hohman/test_cipher.rb +0 -68
- data/lib/quiz1/t/solutions/Moses Hohman/test_deck.rb +0 -146
- data/lib/quiz1/t/solutions/Moses Hohman/test_util.rb +0 -38
- data/lib/quiz1/t/solutions/Moses Hohman/testsuite.rb +0 -5
- data/lib/quiz1/t/solutions/Moses Hohman/util.rb +0 -27
- data/lib/quiz1/t/solutions/Niklas Frykholm/solitaire.rb +0 -151
- data/lib/quiz1/t/solutions/Thomas Leitner/solitaire.rb +0 -198
- data/lib/zip/stdrubyext.rb +0 -111
- data/lib/zip/tempfile_bugfixed.rb +0 -195
- data/lib/zip/zip.rb +0 -1847
- data/lib/zip/zipfilesystem.rb +0 -609
- data/lib/zip/ziprequire.rb +0 -90
- data/samples/gtkRubyzip.rb +0 -86
- data/test/alltests.rb +0 -9
- data/test/data/file1.txt +0 -46
- data/test/data/file1.txt.deflatedData +0 -0
- data/test/data/file2.txt +0 -1504
- data/test/data/notzippedruby.rb +0 -7
- data/test/data/rubycode.zip +0 -0
- data/test/data/rubycode2.zip +0 -0
- data/test/data/testDirectory.bin +0 -0
- data/test/data/zipWithDirs.zip +0 -0
- data/test/gentestfiles.rb +0 -157
- data/test/ioextrastest.rb +0 -208
- data/test/stdrubyexttest.rb +0 -52
- data/test/zipfilesystemtest.rb +0 -831
- data/test/ziprequiretest.rb +0 -43
- data/test/ziptest.rb +0 -1599
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
module Zip
|
|
2
|
+
class CentralDirectory
|
|
3
|
+
include Enumerable
|
|
4
|
+
|
|
5
|
+
END_OF_CDS = 0x06054b50
|
|
6
|
+
ZIP64_END_OF_CDS = 0x06064b50
|
|
7
|
+
ZIP64_EOCD_LOCATOR = 0x07064b50
|
|
8
|
+
MAX_END_OF_CDS_SIZE = 65_536 + 18
|
|
9
|
+
STATIC_EOCD_SIZE = 22
|
|
10
|
+
|
|
11
|
+
attr_reader :comment
|
|
12
|
+
|
|
13
|
+
# Returns an Enumerable containing the entries.
|
|
14
|
+
def entries
|
|
15
|
+
@entry_set.entries
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def initialize(entries = EntrySet.new, comment = '') #:nodoc:
|
|
19
|
+
super()
|
|
20
|
+
@entry_set = entries.kind_of?(EntrySet) ? entries : EntrySet.new(entries)
|
|
21
|
+
@comment = comment
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def write_to_stream(io) #:nodoc:
|
|
25
|
+
cdir_offset = io.tell
|
|
26
|
+
@entry_set.each { |entry| entry.write_c_dir_entry(io) }
|
|
27
|
+
eocd_offset = io.tell
|
|
28
|
+
cdir_size = eocd_offset - cdir_offset
|
|
29
|
+
if ::Zip.write_zip64_support
|
|
30
|
+
need_zip64_eocd = cdir_offset > 0xFFFFFFFF || cdir_size > 0xFFFFFFFF || @entry_set.size > 0xFFFF
|
|
31
|
+
need_zip64_eocd ||= @entry_set.any? { |entry| entry.extra['Zip64'] }
|
|
32
|
+
if need_zip64_eocd
|
|
33
|
+
write_64_e_o_c_d(io, cdir_offset, cdir_size)
|
|
34
|
+
write_64_eocd_locator(io, eocd_offset)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
write_e_o_c_d(io, cdir_offset, cdir_size)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def write_e_o_c_d(io, offset, cdir_size) #:nodoc:
|
|
41
|
+
tmp = [
|
|
42
|
+
END_OF_CDS,
|
|
43
|
+
0, # @numberOfThisDisk
|
|
44
|
+
0, # @numberOfDiskWithStartOfCDir
|
|
45
|
+
@entry_set ? [@entry_set.size, 0xFFFF].min : 0,
|
|
46
|
+
@entry_set ? [@entry_set.size, 0xFFFF].min : 0,
|
|
47
|
+
[cdir_size, 0xFFFFFFFF].min,
|
|
48
|
+
[offset, 0xFFFFFFFF].min,
|
|
49
|
+
@comment ? @comment.bytesize : 0
|
|
50
|
+
]
|
|
51
|
+
io << tmp.pack('VvvvvVVv')
|
|
52
|
+
io << @comment
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private :write_e_o_c_d
|
|
56
|
+
|
|
57
|
+
def write_64_e_o_c_d(io, offset, cdir_size) #:nodoc:
|
|
58
|
+
tmp = [
|
|
59
|
+
ZIP64_END_OF_CDS,
|
|
60
|
+
44, # size of zip64 end of central directory record (excludes signature and field itself)
|
|
61
|
+
VERSION_MADE_BY,
|
|
62
|
+
VERSION_NEEDED_TO_EXTRACT_ZIP64,
|
|
63
|
+
0, # @numberOfThisDisk
|
|
64
|
+
0, # @numberOfDiskWithStartOfCDir
|
|
65
|
+
@entry_set ? @entry_set.size : 0, # number of entries on this disk
|
|
66
|
+
@entry_set ? @entry_set.size : 0, # number of entries total
|
|
67
|
+
cdir_size, # size of central directory
|
|
68
|
+
offset # offset of start of central directory in its disk
|
|
69
|
+
]
|
|
70
|
+
io << tmp.pack('VQ<vvVVQ<Q<Q<Q<')
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private :write_64_e_o_c_d
|
|
74
|
+
|
|
75
|
+
def write_64_eocd_locator(io, zip64_eocd_offset)
|
|
76
|
+
tmp = [
|
|
77
|
+
ZIP64_EOCD_LOCATOR,
|
|
78
|
+
0, # number of disk containing the start of zip64 eocd record
|
|
79
|
+
zip64_eocd_offset, # offset of the start of zip64 eocd record in its disk
|
|
80
|
+
1 # total number of disks
|
|
81
|
+
]
|
|
82
|
+
io << tmp.pack('VVQ<V')
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
private :write_64_eocd_locator
|
|
86
|
+
|
|
87
|
+
def read_64_e_o_c_d(buf) #:nodoc:
|
|
88
|
+
buf = get_64_e_o_c_d(buf)
|
|
89
|
+
@size_of_zip64_e_o_c_d = Entry.read_zip_64_long(buf)
|
|
90
|
+
@version_made_by = Entry.read_zip_short(buf)
|
|
91
|
+
@version_needed_for_extract = Entry.read_zip_short(buf)
|
|
92
|
+
@number_of_this_disk = Entry.read_zip_long(buf)
|
|
93
|
+
@number_of_disk_with_start_of_cdir = Entry.read_zip_long(buf)
|
|
94
|
+
@total_number_of_entries_in_cdir_on_this_disk = Entry.read_zip_64_long(buf)
|
|
95
|
+
@size = Entry.read_zip_64_long(buf)
|
|
96
|
+
@size_in_bytes = Entry.read_zip_64_long(buf)
|
|
97
|
+
@cdir_offset = Entry.read_zip_64_long(buf)
|
|
98
|
+
@zip_64_extensible = buf.slice!(0, buf.bytesize)
|
|
99
|
+
raise Error, 'Zip consistency problem while reading eocd structure' unless buf.empty?
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def read_e_o_c_d(buf) #:nodoc:
|
|
103
|
+
buf = get_e_o_c_d(buf)
|
|
104
|
+
@number_of_this_disk = Entry.read_zip_short(buf)
|
|
105
|
+
@number_of_disk_with_start_of_cdir = Entry.read_zip_short(buf)
|
|
106
|
+
@total_number_of_entries_in_cdir_on_this_disk = Entry.read_zip_short(buf)
|
|
107
|
+
@size = Entry.read_zip_short(buf)
|
|
108
|
+
@size_in_bytes = Entry.read_zip_long(buf)
|
|
109
|
+
@cdir_offset = Entry.read_zip_long(buf)
|
|
110
|
+
comment_length = Entry.read_zip_short(buf)
|
|
111
|
+
@comment = if comment_length.to_i <= 0
|
|
112
|
+
buf.slice!(0, buf.size)
|
|
113
|
+
else
|
|
114
|
+
buf.read(comment_length)
|
|
115
|
+
end
|
|
116
|
+
raise Error, 'Zip consistency problem while reading eocd structure' unless buf.empty?
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def read_central_directory_entries(io) #:nodoc:
|
|
120
|
+
begin
|
|
121
|
+
io.seek(@cdir_offset, IO::SEEK_SET)
|
|
122
|
+
rescue Errno::EINVAL
|
|
123
|
+
raise Error, 'Zip consistency problem while reading central directory entry'
|
|
124
|
+
end
|
|
125
|
+
@entry_set = EntrySet.new
|
|
126
|
+
@size.times do
|
|
127
|
+
@entry_set << Entry.read_c_dir_entry(io)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def read_from_stream(io) #:nodoc:
|
|
132
|
+
buf = start_buf(io)
|
|
133
|
+
if zip64_file?(buf)
|
|
134
|
+
read_64_e_o_c_d(buf)
|
|
135
|
+
else
|
|
136
|
+
read_e_o_c_d(buf)
|
|
137
|
+
end
|
|
138
|
+
read_central_directory_entries(io)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def get_e_o_c_d(buf) #:nodoc:
|
|
142
|
+
sig_index = buf.rindex([END_OF_CDS].pack('V'))
|
|
143
|
+
raise Error, 'Zip end of central directory signature not found' unless sig_index
|
|
144
|
+
|
|
145
|
+
buf = buf.slice!((sig_index + 4)..(buf.bytesize))
|
|
146
|
+
|
|
147
|
+
def buf.read(count)
|
|
148
|
+
slice!(0, count)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
buf
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def zip64_file?(buf)
|
|
155
|
+
buf.rindex([ZIP64_END_OF_CDS].pack('V')) && buf.rindex([ZIP64_EOCD_LOCATOR].pack('V'))
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def start_buf(io)
|
|
159
|
+
begin
|
|
160
|
+
io.seek(-MAX_END_OF_CDS_SIZE, IO::SEEK_END)
|
|
161
|
+
rescue Errno::EINVAL
|
|
162
|
+
io.seek(0, IO::SEEK_SET)
|
|
163
|
+
end
|
|
164
|
+
io.read
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def get_64_e_o_c_d(buf) #:nodoc:
|
|
168
|
+
zip_64_start = buf.rindex([ZIP64_END_OF_CDS].pack('V'))
|
|
169
|
+
raise Error, 'Zip64 end of central directory signature not found' unless zip_64_start
|
|
170
|
+
|
|
171
|
+
zip_64_locator = buf.rindex([ZIP64_EOCD_LOCATOR].pack('V'))
|
|
172
|
+
raise Error, 'Zip64 end of central directory signature locator not found' unless zip_64_locator
|
|
173
|
+
|
|
174
|
+
buf = buf.slice!((zip_64_start + 4)..zip_64_locator)
|
|
175
|
+
|
|
176
|
+
def buf.read(count)
|
|
177
|
+
slice!(0, count)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
buf
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# For iterating over the entries.
|
|
184
|
+
def each(&a_proc)
|
|
185
|
+
@entry_set.each(&a_proc)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Returns the number of entries in the central directory (and
|
|
189
|
+
# consequently in the zip archive).
|
|
190
|
+
def size
|
|
191
|
+
@entry_set.size
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def self.read_from_stream(io) #:nodoc:
|
|
195
|
+
cdir = new
|
|
196
|
+
cdir.read_from_stream(io)
|
|
197
|
+
cdir
|
|
198
|
+
rescue Error
|
|
199
|
+
nil
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def ==(other) #:nodoc:
|
|
203
|
+
return false unless other.kind_of?(CentralDirectory)
|
|
204
|
+
|
|
205
|
+
@entry_set.entries.sort == other.entries.sort && comment == other.comment
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Copyright (C) 2002, 2003 Thomas Sondergaard
|
|
211
|
+
# rubyzip is free software; you can redistribute it and/or
|
|
212
|
+
# modify it under the terms of the ruby license.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
module Zip
|
|
2
|
+
RUNNING_ON_WINDOWS = RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/i
|
|
3
|
+
|
|
4
|
+
CENTRAL_DIRECTORY_ENTRY_SIGNATURE = 0x02014b50
|
|
5
|
+
CDIR_ENTRY_STATIC_HEADER_LENGTH = 46
|
|
6
|
+
|
|
7
|
+
LOCAL_ENTRY_SIGNATURE = 0x04034b50
|
|
8
|
+
LOCAL_ENTRY_STATIC_HEADER_LENGTH = 30
|
|
9
|
+
LOCAL_ENTRY_TRAILING_DESCRIPTOR_LENGTH = 4 + 4 + 4
|
|
10
|
+
VERSION_MADE_BY = 52 # this library's version
|
|
11
|
+
VERSION_NEEDED_TO_EXTRACT = 20
|
|
12
|
+
VERSION_NEEDED_TO_EXTRACT_ZIP64 = 45
|
|
13
|
+
|
|
14
|
+
FILE_TYPE_FILE = 0o10
|
|
15
|
+
FILE_TYPE_DIR = 0o04
|
|
16
|
+
FILE_TYPE_SYMLINK = 0o12
|
|
17
|
+
|
|
18
|
+
FSTYPE_FAT = 0
|
|
19
|
+
FSTYPE_AMIGA = 1
|
|
20
|
+
FSTYPE_VMS = 2
|
|
21
|
+
FSTYPE_UNIX = 3
|
|
22
|
+
FSTYPE_VM_CMS = 4
|
|
23
|
+
FSTYPE_ATARI = 5
|
|
24
|
+
FSTYPE_HPFS = 6
|
|
25
|
+
FSTYPE_MAC = 7
|
|
26
|
+
FSTYPE_Z_SYSTEM = 8
|
|
27
|
+
FSTYPE_CPM = 9
|
|
28
|
+
FSTYPE_TOPS20 = 10
|
|
29
|
+
FSTYPE_NTFS = 11
|
|
30
|
+
FSTYPE_QDOS = 12
|
|
31
|
+
FSTYPE_ACORN = 13
|
|
32
|
+
FSTYPE_VFAT = 14
|
|
33
|
+
FSTYPE_MVS = 15
|
|
34
|
+
FSTYPE_BEOS = 16
|
|
35
|
+
FSTYPE_TANDEM = 17
|
|
36
|
+
FSTYPE_THEOS = 18
|
|
37
|
+
FSTYPE_MAC_OSX = 19
|
|
38
|
+
FSTYPE_ATHEOS = 30
|
|
39
|
+
|
|
40
|
+
FSTYPES = {
|
|
41
|
+
FSTYPE_FAT => 'FAT'.freeze,
|
|
42
|
+
FSTYPE_AMIGA => 'Amiga'.freeze,
|
|
43
|
+
FSTYPE_VMS => 'VMS (Vax or Alpha AXP)'.freeze,
|
|
44
|
+
FSTYPE_UNIX => 'Unix'.freeze,
|
|
45
|
+
FSTYPE_VM_CMS => 'VM/CMS'.freeze,
|
|
46
|
+
FSTYPE_ATARI => 'Atari ST'.freeze,
|
|
47
|
+
FSTYPE_HPFS => 'OS/2 or NT HPFS'.freeze,
|
|
48
|
+
FSTYPE_MAC => 'Macintosh'.freeze,
|
|
49
|
+
FSTYPE_Z_SYSTEM => 'Z-System'.freeze,
|
|
50
|
+
FSTYPE_CPM => 'CP/M'.freeze,
|
|
51
|
+
FSTYPE_TOPS20 => 'TOPS-20'.freeze,
|
|
52
|
+
FSTYPE_NTFS => 'NTFS'.freeze,
|
|
53
|
+
FSTYPE_QDOS => 'SMS/QDOS'.freeze,
|
|
54
|
+
FSTYPE_ACORN => 'Acorn RISC OS'.freeze,
|
|
55
|
+
FSTYPE_VFAT => 'Win32 VFAT'.freeze,
|
|
56
|
+
FSTYPE_MVS => 'MVS'.freeze,
|
|
57
|
+
FSTYPE_BEOS => 'BeOS'.freeze,
|
|
58
|
+
FSTYPE_TANDEM => 'Tandem NSK'.freeze,
|
|
59
|
+
FSTYPE_THEOS => 'Theos'.freeze,
|
|
60
|
+
FSTYPE_MAC_OSX => 'Mac OS/X (Darwin)'.freeze,
|
|
61
|
+
FSTYPE_ATHEOS => 'AtheOS'.freeze
|
|
62
|
+
}.freeze
|
|
63
|
+
|
|
64
|
+
COMPRESSION_METHOD_STORE = 0
|
|
65
|
+
COMPRESSION_METHOD_SHRINK = 1
|
|
66
|
+
COMPRESSION_METHOD_REDUCE_1 = 2
|
|
67
|
+
COMPRESSION_METHOD_REDUCE_2 = 3
|
|
68
|
+
COMPRESSION_METHOD_REDUCE_3 = 4
|
|
69
|
+
COMPRESSION_METHOD_REDUCE_4 = 5
|
|
70
|
+
COMPRESSION_METHOD_IMPLODE = 6
|
|
71
|
+
# RESERVED = 7
|
|
72
|
+
COMPRESSION_METHOD_DEFLATE = 8
|
|
73
|
+
COMPRESSION_METHOD_DEFLATE_64 = 9
|
|
74
|
+
COMPRESSION_METHOD_PKWARE_DCLI = 10
|
|
75
|
+
# RESERVED = 11
|
|
76
|
+
COMPRESSION_METHOD_BZIP2 = 12
|
|
77
|
+
# RESERVED = 13
|
|
78
|
+
COMPRESSION_METHOD_LZMA = 14
|
|
79
|
+
# RESERVED = 15
|
|
80
|
+
COMPRESSION_METHOD_IBM_CMPSC = 16
|
|
81
|
+
# RESERVED = 17
|
|
82
|
+
COMPRESSION_METHOD_IBM_TERSE = 18
|
|
83
|
+
COMPRESSION_METHOD_IBM_LZ77 = 19
|
|
84
|
+
COMPRESSION_METHOD_JPEG = 96
|
|
85
|
+
COMPRESSION_METHOD_WAVPACK = 97
|
|
86
|
+
COMPRESSION_METHOD_PPMD = 98
|
|
87
|
+
COMPRESSION_METHOD_AES = 99
|
|
88
|
+
|
|
89
|
+
COMPRESSION_METHODS = {
|
|
90
|
+
COMPRESSION_METHOD_STORE => 'Store (no compression)',
|
|
91
|
+
COMPRESSION_METHOD_SHRINK => 'Shrink',
|
|
92
|
+
COMPRESSION_METHOD_REDUCE_1 => 'Reduce with compression factor 1',
|
|
93
|
+
COMPRESSION_METHOD_REDUCE_2 => 'Reduce with compression factor 2',
|
|
94
|
+
COMPRESSION_METHOD_REDUCE_3 => 'Reduce with compression factor 3',
|
|
95
|
+
COMPRESSION_METHOD_REDUCE_4 => 'Reduce with compression factor 4',
|
|
96
|
+
COMPRESSION_METHOD_IMPLODE => 'Implode',
|
|
97
|
+
# RESERVED = 7
|
|
98
|
+
COMPRESSION_METHOD_DEFLATE => 'Deflate',
|
|
99
|
+
COMPRESSION_METHOD_DEFLATE_64 => 'Deflate64(tm)',
|
|
100
|
+
COMPRESSION_METHOD_PKWARE_DCLI => 'PKWARE Data Compression Library Imploding (old IBM TERSE)',
|
|
101
|
+
# RESERVED = 11
|
|
102
|
+
COMPRESSION_METHOD_BZIP2 => 'BZIP2',
|
|
103
|
+
# RESERVED = 13
|
|
104
|
+
COMPRESSION_METHOD_LZMA => 'LZMA',
|
|
105
|
+
# RESERVED = 15
|
|
106
|
+
COMPRESSION_METHOD_IBM_CMPSC => 'IBM z/OS CMPSC Compression',
|
|
107
|
+
# RESERVED = 17
|
|
108
|
+
COMPRESSION_METHOD_IBM_TERSE => 'IBM TERSE (new)',
|
|
109
|
+
COMPRESSION_METHOD_IBM_LZ77 => 'IBM LZ77 z Architecture (PFS)',
|
|
110
|
+
COMPRESSION_METHOD_JPEG => 'JPEG variant',
|
|
111
|
+
COMPRESSION_METHOD_WAVPACK => 'WavPack compressed data',
|
|
112
|
+
COMPRESSION_METHOD_PPMD => 'PPMd version I, Rev 1',
|
|
113
|
+
COMPRESSION_METHOD_AES => 'AES encryption'
|
|
114
|
+
}.freeze
|
|
115
|
+
end
|
|
@@ -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.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Zip
|
|
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)
|
|
21
|
+
super()
|
|
22
|
+
|
|
23
|
+
@input_stream = input_stream
|
|
24
|
+
@decompressed_size = decompressed_size
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Copyright (C) 2002, 2003 Thomas Sondergaard
|
|
30
|
+
# rubyzip is free software; you can redistribute it and/or
|
|
31
|
+
# modify it under the terms of the ruby license.
|
data/lib/zip/deflater.rb
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module Zip
|
|
2
|
+
class Deflater < Compressor #:nodoc:all
|
|
3
|
+
def initialize(output_stream, level = Zip.default_compression, encrypter = NullEncrypter.new)
|
|
4
|
+
super()
|
|
5
|
+
@output_stream = output_stream
|
|
6
|
+
@zlib_deflater = ::Zlib::Deflate.new(level, -::Zlib::MAX_WBITS)
|
|
7
|
+
@size = 0
|
|
8
|
+
@crc = ::Zlib.crc32
|
|
9
|
+
@encrypter = encrypter
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def <<(data)
|
|
13
|
+
val = data.to_s
|
|
14
|
+
@crc = Zlib.crc32(val, @crc)
|
|
15
|
+
@size += val.bytesize
|
|
16
|
+
buffer = @zlib_deflater.deflate(data)
|
|
17
|
+
if buffer.empty?
|
|
18
|
+
@output_stream
|
|
19
|
+
else
|
|
20
|
+
@output_stream << @encrypter.encrypt(buffer)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def finish
|
|
25
|
+
@output_stream << @encrypter.encrypt(@zlib_deflater.finish) until @zlib_deflater.finished?
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
attr_reader :size, :crc
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Copyright (C) 2002, 2003 Thomas Sondergaard
|
|
33
|
+
# rubyzip is free software; you can redistribute it and/or
|
|
34
|
+
# modify it under the terms of the ruby license.
|
data/lib/zip/dos_time.rb
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module Zip
|
|
2
|
+
class DOSTime < Time #:nodoc:all
|
|
3
|
+
# MS-DOS File Date and Time format as used in Interrupt 21H Function 57H:
|
|
4
|
+
|
|
5
|
+
# Register CX, the Time:
|
|
6
|
+
# Bits 0-4 2 second increments (0-29)
|
|
7
|
+
# Bits 5-10 minutes (0-59)
|
|
8
|
+
# bits 11-15 hours (0-24)
|
|
9
|
+
|
|
10
|
+
# Register DX, the Date:
|
|
11
|
+
# Bits 0-4 day (1-31)
|
|
12
|
+
# bits 5-8 month (1-12)
|
|
13
|
+
# bits 9-15 year (four digit year minus 1980)
|
|
14
|
+
|
|
15
|
+
def to_binary_dos_time
|
|
16
|
+
(sec / 2) +
|
|
17
|
+
(min << 5) +
|
|
18
|
+
(hour << 11)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def to_binary_dos_date
|
|
22
|
+
day +
|
|
23
|
+
(month << 5) +
|
|
24
|
+
((year - 1980) << 9)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Dos time is only stored with two seconds accuracy
|
|
28
|
+
def dos_equals(other)
|
|
29
|
+
to_i / 2 == other.to_i / 2
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Create a DOSTime instance from a vanilla Time instance.
|
|
33
|
+
def self.from_time(time)
|
|
34
|
+
local(time.year, time.month, time.day, time.hour, time.min, time.sec)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.parse_binary_dos_format(bin_dos_date, bin_dos_time)
|
|
38
|
+
second = 2 * (0b11111 & bin_dos_time)
|
|
39
|
+
minute = (0b11111100000 & bin_dos_time) >> 5
|
|
40
|
+
hour = (0b1111100000000000 & bin_dos_time) >> 11
|
|
41
|
+
day = (0b11111 & bin_dos_date)
|
|
42
|
+
month = (0b111100000 & bin_dos_date) >> 5
|
|
43
|
+
year = ((0b1111111000000000 & bin_dos_date) >> 9) + 1980
|
|
44
|
+
begin
|
|
45
|
+
local(year, month, day, hour, minute, second)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Copyright (C) 2002, 2003 Thomas Sondergaard
|
|
52
|
+
# rubyzip is free software; you can redistribute it and/or
|
|
53
|
+
# modify it under the terms of the ruby license.
|