archive-zip 0.3.0 → 0.4.0
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.
- data/HACKING +25 -42
- data/NEWS +25 -0
- data/README +2 -2
- data/Rakefile +202 -0
- data/TODO +5 -0
- data/default.mspec +8 -0
- data/lib/archive/support/binary_stringio.rb +23 -0
- data/lib/archive/support/integer.rb +13 -0
- data/lib/archive/support/io-like.rb +3 -1
- data/lib/archive/support/ioextensions.rb +16 -0
- data/lib/archive/support/iowindow.rb +10 -18
- data/lib/archive/support/time.rb +2 -0
- data/lib/archive/support/zlib.rb +298 -71
- data/lib/archive/zip.rb +161 -139
- data/lib/archive/zip/codec.rb +2 -0
- data/lib/archive/zip/codec/deflate.rb +59 -11
- data/lib/archive/zip/codec/null_encryption.rb +75 -14
- data/lib/archive/zip/codec/store.rb +75 -26
- data/lib/archive/zip/codec/traditional_encryption.rb +146 -35
- data/lib/archive/zip/data_descriptor.rb +6 -4
- data/lib/archive/zip/entry.rb +184 -132
- data/lib/archive/zip/error.rb +2 -0
- data/lib/archive/zip/extra_field.rb +20 -6
- data/lib/archive/zip/extra_field/extended_timestamp.rb +141 -60
- data/lib/archive/zip/extra_field/raw.rb +70 -12
- data/lib/archive/zip/extra_field/unix.rb +58 -16
- data/lib/archive/zip/version.rb +6 -0
- data/spec/archive/zip/codec/deflate/compress/checksum_spec.rb +42 -0
- data/spec/archive/zip/codec/deflate/compress/close_spec.rb +44 -0
- data/spec/archive/zip/codec/deflate/compress/crc32_spec.rb +21 -0
- data/spec/archive/zip/codec/deflate/compress/data_descriptor_spec.rb +67 -0
- data/spec/archive/zip/codec/deflate/compress/new_spec.rb +37 -0
- data/spec/archive/zip/codec/deflate/compress/open_spec.rb +46 -0
- data/spec/archive/zip/codec/deflate/compress/write_spec.rb +109 -0
- data/spec/archive/zip/codec/deflate/decompress/checksum_spec.rb +18 -0
- data/spec/archive/zip/codec/deflate/decompress/close_spec.rb +33 -0
- data/spec/archive/zip/codec/deflate/decompress/crc32_spec.rb +18 -0
- data/spec/archive/zip/codec/deflate/decompress/data_descriptor_spec.rb +67 -0
- data/spec/archive/zip/codec/deflate/decompress/new_spec.rb +14 -0
- data/spec/archive/zip/codec/deflate/decompress/open_spec.rb +27 -0
- data/spec/archive/zip/codec/deflate/fixtures/classes.rb +25 -0
- data/spec/archive/zip/codec/deflate/fixtures/compressed_file.bin +1 -0
- data/spec/archive/zip/codec/deflate/fixtures/compressed_file_nocomp.bin +0 -0
- data/spec/archive/zip/codec/deflate/fixtures/raw_file.txt +10 -0
- data/spec/archive/zip/codec/null_encryption/decrypt/close_spec.rb +33 -0
- data/spec/archive/zip/codec/null_encryption/decrypt/new_spec.rb +14 -0
- data/spec/archive/zip/codec/null_encryption/decrypt/open_spec.rb +27 -0
- data/spec/archive/zip/codec/null_encryption/decrypt/read_spec.rb +24 -0
- data/spec/archive/zip/codec/null_encryption/decrypt/rewind_spec.rb +25 -0
- data/spec/archive/zip/codec/null_encryption/decrypt/seek_spec.rb +57 -0
- data/spec/archive/zip/codec/null_encryption/decrypt/tell_spec.rb +21 -0
- data/spec/archive/zip/codec/null_encryption/encrypt/close_spec.rb +33 -0
- data/spec/archive/zip/codec/null_encryption/encrypt/new_spec.rb +14 -0
- data/spec/archive/zip/codec/null_encryption/encrypt/open_spec.rb +27 -0
- data/spec/archive/zip/codec/null_encryption/encrypt/rewind_spec.rb +26 -0
- data/spec/archive/zip/codec/null_encryption/encrypt/seek_spec.rb +50 -0
- data/spec/archive/zip/codec/null_encryption/encrypt/tell_spec.rb +29 -0
- data/spec/archive/zip/codec/null_encryption/encrypt/write_spec.rb +29 -0
- data/spec/archive/zip/codec/null_encryption/fixtures/classes.rb +12 -0
- data/spec/archive/zip/codec/null_encryption/fixtures/raw_file.txt +10 -0
- data/spec/archive/zip/codec/store/compress/close_spec.rb +33 -0
- data/spec/archive/zip/codec/store/compress/data_descriptor_spec.rb +68 -0
- data/spec/archive/zip/codec/store/compress/new_spec.rb +14 -0
- data/spec/archive/zip/codec/store/compress/open_spec.rb +27 -0
- data/spec/archive/zip/codec/store/compress/rewind_spec.rb +26 -0
- data/spec/archive/zip/codec/store/compress/seek_spec.rb +50 -0
- data/spec/archive/zip/codec/store/compress/tell_spec.rb +29 -0
- data/spec/archive/zip/codec/store/compress/write_spec.rb +29 -0
- data/spec/archive/zip/codec/store/decompress/close_spec.rb +33 -0
- data/spec/archive/zip/codec/store/decompress/data_descriptor_spec.rb +68 -0
- data/spec/archive/zip/codec/store/decompress/new_spec.rb +14 -0
- data/spec/archive/zip/codec/store/decompress/open_spec.rb +27 -0
- data/spec/archive/zip/codec/store/decompress/read_spec.rb +24 -0
- data/spec/archive/zip/codec/store/decompress/rewind_spec.rb +25 -0
- data/spec/archive/zip/codec/store/decompress/seek_spec.rb +57 -0
- data/spec/archive/zip/codec/store/decompress/tell_spec.rb +21 -0
- data/spec/archive/zip/codec/store/fixtures/classes.rb +12 -0
- data/spec/archive/zip/codec/store/fixtures/raw_file.txt +10 -0
- data/spec/archive/zip/codec/traditional_encryption/decrypt/close_spec.rb +64 -0
- data/spec/archive/zip/codec/traditional_encryption/decrypt/new_spec.rb +18 -0
- data/spec/archive/zip/codec/traditional_encryption/decrypt/open_spec.rb +39 -0
- data/spec/archive/zip/codec/traditional_encryption/decrypt/read_spec.rb +126 -0
- data/spec/archive/zip/codec/traditional_encryption/decrypt/rewind_spec.rb +38 -0
- data/spec/archive/zip/codec/traditional_encryption/decrypt/seek_spec.rb +82 -0
- data/spec/archive/zip/codec/traditional_encryption/decrypt/tell_spec.rb +25 -0
- data/spec/archive/zip/codec/traditional_encryption/encrypt/close_spec.rb +64 -0
- data/spec/archive/zip/codec/traditional_encryption/encrypt/new_spec.rb +18 -0
- data/spec/archive/zip/codec/traditional_encryption/encrypt/open_spec.rb +39 -0
- data/spec/archive/zip/codec/traditional_encryption/encrypt/rewind_spec.rb +41 -0
- data/spec/archive/zip/codec/traditional_encryption/encrypt/seek_spec.rb +75 -0
- data/spec/archive/zip/codec/traditional_encryption/encrypt/tell_spec.rb +42 -0
- data/spec/archive/zip/codec/traditional_encryption/encrypt/write_spec.rb +127 -0
- data/spec/archive/zip/codec/traditional_encryption/fixtures/classes.rb +27 -0
- data/spec/archive/zip/codec/traditional_encryption/fixtures/encrypted_file.bin +0 -0
- data/spec/archive/zip/codec/traditional_encryption/fixtures/raw_file.txt +10 -0
- data/spec/binary_stringio/new_spec.rb +34 -0
- data/spec/binary_stringio/set_encoding_spec.rb +14 -0
- data/spec/ioextensions/read_exactly_spec.rb +50 -0
- data/spec/zlib/fixtures/classes.rb +65 -0
- data/spec/zlib/fixtures/compressed_file.bin +1 -0
- data/spec/zlib/fixtures/compressed_file_gzip.bin +0 -0
- data/spec/zlib/fixtures/compressed_file_huffman.bin +2 -0
- data/spec/zlib/fixtures/compressed_file_minmem.bin +0 -0
- data/spec/zlib/fixtures/compressed_file_minwin.bin +1 -0
- data/spec/zlib/fixtures/compressed_file_nocomp.bin +0 -0
- data/spec/zlib/fixtures/compressed_file_raw.bin +1 -0
- data/spec/zlib/fixtures/raw_file.txt +10 -0
- data/spec/zlib/zreader/checksum_spec.rb +40 -0
- data/spec/zlib/zreader/close_spec.rb +14 -0
- data/spec/zlib/zreader/compressed_size_spec.rb +18 -0
- data/spec/zlib/zreader/new_spec.rb +41 -0
- data/spec/zlib/zreader/open_spec.rb +49 -0
- data/spec/zlib/zreader/read_spec.rb +47 -0
- data/spec/zlib/zreader/rewind_spec.rb +23 -0
- data/spec/zlib/zreader/seek_spec.rb +55 -0
- data/spec/zlib/zreader/tell_spec.rb +21 -0
- data/spec/zlib/zreader/uncompressed_size_spec.rb +18 -0
- data/spec/zlib/zwriter/checksum_spec.rb +41 -0
- data/spec/zlib/zwriter/close_spec.rb +14 -0
- data/spec/zlib/zwriter/compressed_size_spec.rb +19 -0
- data/spec/zlib/zwriter/new_spec.rb +64 -0
- data/spec/zlib/zwriter/open_spec.rb +68 -0
- data/spec/zlib/zwriter/rewind_spec.rb +26 -0
- data/spec/zlib/zwriter/seek_spec.rb +54 -0
- data/spec/zlib/zwriter/tell_spec.rb +29 -0
- data/spec/zlib/zwriter/uncompressed_size_spec.rb +19 -0
- data/spec/zlib/zwriter/write_spec.rb +28 -0
- data/spec_helper.rb +49 -0
- metadata +296 -74
- data/MANIFEST +0 -27
- data/lib/archive/support/io.rb +0 -14
- data/lib/archive/support/stringio.rb +0 -22
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
1
3
|
require 'archive/support/io-like'
|
|
2
4
|
require 'archive/zip/codec'
|
|
3
5
|
|
|
@@ -36,6 +38,10 @@ module Archive; class Zip; module Codec
|
|
|
36
38
|
# assumption that _io_ is already buffered.
|
|
37
39
|
def initialize(io)
|
|
38
40
|
@io = io
|
|
41
|
+
|
|
42
|
+
# Keep track of the total number of bytes written.
|
|
43
|
+
@total_bytes_in = 0
|
|
44
|
+
|
|
39
45
|
# Assume that the delegate IO object is already buffered.
|
|
40
46
|
self.flush_size = 0
|
|
41
47
|
end
|
|
@@ -50,9 +56,34 @@ module Archive; class Zip; module Codec
|
|
|
50
56
|
|
|
51
57
|
private
|
|
52
58
|
|
|
59
|
+
# Allows resetting this object and the delegate object back to the
|
|
60
|
+
# beginning of the stream or reporting the current position in the stream.
|
|
61
|
+
#
|
|
62
|
+
# Raises Errno::EINVAL unless _offset_ is <tt>0</tt> and _whence_ is
|
|
63
|
+
# either IO::SEEK_SET or IO::SEEK_CUR. Raises Errno::EINVAL if _whence_
|
|
64
|
+
# is IO::SEEK_SEK and the delegate object does not respond to the _rewind_
|
|
65
|
+
# method.
|
|
66
|
+
def unbuffered_seek(offset, whence = IO::SEEK_SET)
|
|
67
|
+
unless offset == 0 &&
|
|
68
|
+
((whence == IO::SEEK_SET && @io.respond_to?(:rewind)) ||
|
|
69
|
+
whence == IO::SEEK_CUR) then
|
|
70
|
+
raise Errno::EINVAL
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
case whence
|
|
74
|
+
when IO::SEEK_SET
|
|
75
|
+
@io.rewind
|
|
76
|
+
@total_bytes_in = 0
|
|
77
|
+
when IO::SEEK_CUR
|
|
78
|
+
@total_bytes_in
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
53
82
|
# Writes _string_ to the delegate IO object and returns the result.
|
|
54
83
|
def unbuffered_write(string)
|
|
55
|
-
@io.write(string)
|
|
84
|
+
bytes_written = @io.write(string)
|
|
85
|
+
@total_bytes_in += bytes_written
|
|
86
|
+
bytes_written
|
|
56
87
|
end
|
|
57
88
|
end
|
|
58
89
|
|
|
@@ -80,21 +111,41 @@ module Archive; class Zip; module Codec
|
|
|
80
111
|
end
|
|
81
112
|
|
|
82
113
|
# Creates a new instance of this class using _io_ as a data source. _io_
|
|
83
|
-
# must be readable and provide a
|
|
84
|
-
# raised when performing read operations.
|
|
85
|
-
#
|
|
114
|
+
# must be readable and provide a _read_ method as an IO instance would or
|
|
115
|
+
# errors will be raised when performing read operations.
|
|
116
|
+
#
|
|
117
|
+
# This class has extremely limited seek capabilities. It is possible to
|
|
118
|
+
# seek with an offset of <tt>0</tt> and a whence of <tt>IO::SEEK_CUR</tt>.
|
|
119
|
+
# As a result, the _pos_ and _tell_ methods also work as expected.
|
|
120
|
+
#
|
|
121
|
+
# Due to certain optimizations within IO::Like#seek and if there is data
|
|
122
|
+
# in the read buffer, the _seek_ method can be used to seek forward from
|
|
123
|
+
# the current stream position up to the end of the buffer. Unless it is
|
|
124
|
+
# known definitively how much data is in the buffer, it is best to avoid
|
|
125
|
+
# relying on this behavior.
|
|
126
|
+
#
|
|
127
|
+
# If _io_ also responds to _rewind_, then the _rewind_ method of this
|
|
128
|
+
# class can be used to reset the whole stream back to the beginning. Using
|
|
129
|
+
# _seek_ of this class to seek directly to offset <tt>0</tt> using
|
|
130
|
+
# <tt>IO::SEEK_SET</tt> for whence will also work in this case.
|
|
131
|
+
#
|
|
132
|
+
# Any other seeking attempts, will raise Errno::EINVAL exceptions.
|
|
86
133
|
#
|
|
87
134
|
# The _fill_size_ attribute is set to <tt>0</tt> by default under the
|
|
88
135
|
# assumption that _io_ is already buffered.
|
|
89
136
|
def initialize(io)
|
|
90
137
|
@io = io
|
|
138
|
+
|
|
139
|
+
# Keep track of the total number of bytes read.
|
|
140
|
+
@total_bytes_out = 0
|
|
141
|
+
|
|
91
142
|
# Assume that the delegate IO object is already buffered.
|
|
92
143
|
self.fill_size = 0
|
|
93
144
|
end
|
|
94
145
|
|
|
95
146
|
# Closes this object so that further write operations will fail. If
|
|
96
|
-
# _close_delegate_ is +true+, the delegate object used as a data
|
|
97
|
-
# also be closed using its close method.
|
|
147
|
+
# _close_delegate_ is +true+, the delegate object used as a data source
|
|
148
|
+
# will also be closed using its close method.
|
|
98
149
|
def close(close_delegate = true)
|
|
99
150
|
super()
|
|
100
151
|
@io.close if close_delegate
|
|
@@ -108,22 +159,32 @@ module Archive; class Zip; module Codec
|
|
|
108
159
|
def unbuffered_read(length)
|
|
109
160
|
buffer = @io.read(length)
|
|
110
161
|
raise EOFError, 'end of file reached' if buffer.nil?
|
|
162
|
+
@total_bytes_out += buffer.length
|
|
111
163
|
|
|
112
164
|
buffer
|
|
113
165
|
end
|
|
114
166
|
|
|
115
167
|
# Allows resetting this object and the delegate object back to the
|
|
116
|
-
# beginning of the stream
|
|
117
|
-
#
|
|
118
|
-
#
|
|
168
|
+
# beginning of the stream or reporting the current position in the stream.
|
|
169
|
+
#
|
|
170
|
+
# Raises Errno::EINVAL unless _offset_ is <tt>0</tt> and _whence_ is
|
|
171
|
+
# either IO::SEEK_SET or IO::SEEK_CUR. Raises Errno::EINVAL if _whence_
|
|
172
|
+
# is IO::SEEK_SEK and the delegate object does not respond to the _rewind_
|
|
173
|
+
# method.
|
|
119
174
|
def unbuffered_seek(offset, whence = IO::SEEK_SET)
|
|
120
|
-
unless offset == 0 &&
|
|
121
|
-
|
|
175
|
+
unless offset == 0 &&
|
|
176
|
+
((whence == IO::SEEK_SET && @io.respond_to?(:rewind)) ||
|
|
177
|
+
whence == IO::SEEK_CUR) then
|
|
178
|
+
raise Errno::EINVAL
|
|
122
179
|
end
|
|
123
|
-
|
|
124
|
-
|
|
180
|
+
|
|
181
|
+
case whence
|
|
182
|
+
when IO::SEEK_SET
|
|
183
|
+
@io.rewind
|
|
184
|
+
@total_bytes_out = 0
|
|
185
|
+
when IO::SEEK_CUR
|
|
186
|
+
@total_bytes_out
|
|
125
187
|
end
|
|
126
|
-
@io.rewind
|
|
127
188
|
end
|
|
128
189
|
end
|
|
129
190
|
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
1
3
|
require 'archive/support/io-like'
|
|
4
|
+
require 'archive/support/zlib'
|
|
2
5
|
require 'archive/zip/codec'
|
|
3
6
|
require 'archive/zip/data_descriptor'
|
|
4
7
|
|
|
@@ -58,12 +61,12 @@ module Archive; class Zip; module Codec
|
|
|
58
61
|
nil
|
|
59
62
|
end
|
|
60
63
|
|
|
61
|
-
# Returns an instance of Archive::Zip::
|
|
62
|
-
#
|
|
63
|
-
#
|
|
64
|
-
#
|
|
65
|
-
#
|
|
66
|
-
#
|
|
64
|
+
# Returns an instance of Archive::Zip::DataDescriptor with information
|
|
65
|
+
# regarding the data which has passed through this object to the delegate
|
|
66
|
+
# object. The close or flush methods should be called before using this
|
|
67
|
+
# method in order to ensure that any possibly buffered data is flushed to
|
|
68
|
+
# the delegate object; otherwise, the contents of the data descriptor may
|
|
69
|
+
# be inaccurate.
|
|
67
70
|
def data_descriptor
|
|
68
71
|
DataDescriptor.new(
|
|
69
72
|
@crc32,
|
|
@@ -74,6 +77,30 @@ module Archive; class Zip; module Codec
|
|
|
74
77
|
|
|
75
78
|
private
|
|
76
79
|
|
|
80
|
+
# Allows resetting this object and the delegate object back to the
|
|
81
|
+
# beginning of the stream or reporting the current position in the stream.
|
|
82
|
+
#
|
|
83
|
+
# Raises Errno::EINVAL unless _offset_ is <tt>0</tt> and _whence_ is
|
|
84
|
+
# either IO::SEEK_SET or IO::SEEK_CUR. Raises Errno::EINVAL if _whence_
|
|
85
|
+
# is IO::SEEK_SEK and the delegate object does not respond to the _rewind_
|
|
86
|
+
# method.
|
|
87
|
+
def unbuffered_seek(offset, whence = IO::SEEK_SET)
|
|
88
|
+
unless offset == 0 &&
|
|
89
|
+
((whence == IO::SEEK_SET && @io.respond_to?(:rewind)) ||
|
|
90
|
+
whence == IO::SEEK_CUR) then
|
|
91
|
+
raise Errno::EINVAL
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
case whence
|
|
95
|
+
when IO::SEEK_SET
|
|
96
|
+
@io.rewind
|
|
97
|
+
@crc32 = 0
|
|
98
|
+
@uncompressed_size = 0
|
|
99
|
+
when IO::SEEK_CUR
|
|
100
|
+
@uncompressed_size
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
77
104
|
# Writes _string_ to the delegate object and returns the number of bytes
|
|
78
105
|
# actually written. Updates the uncompressed_size and crc32 attributes as
|
|
79
106
|
# a side effect.
|
|
@@ -115,9 +142,25 @@ module Archive; class Zip; module Codec
|
|
|
115
142
|
end
|
|
116
143
|
|
|
117
144
|
# Creates a new instance of this class using _io_ as a data source. _io_
|
|
118
|
-
# must be readable and provide a
|
|
119
|
-
# raised when performing read operations.
|
|
120
|
-
#
|
|
145
|
+
# must be readable and provide a _read_ method as an IO instance would or
|
|
146
|
+
# errors will be raised when performing read operations.
|
|
147
|
+
#
|
|
148
|
+
# This class has extremely limited seek capabilities. It is possible to
|
|
149
|
+
# seek with an offset of <tt>0</tt> and a whence of <tt>IO::SEEK_CUR</tt>.
|
|
150
|
+
# As a result, the _pos_ and _tell_ methods also work as expected.
|
|
151
|
+
#
|
|
152
|
+
# Due to certain optimizations within IO::Like#seek and if there is data
|
|
153
|
+
# in the read buffer, the _seek_ method can be used to seek forward from
|
|
154
|
+
# the current stream position up to the end of the buffer. Unless it is
|
|
155
|
+
# known definitively how much data is in the buffer, it is best to avoid
|
|
156
|
+
# relying on this behavior.
|
|
157
|
+
#
|
|
158
|
+
# If _io_ also responds to _rewind_, then the _rewind_ method of this
|
|
159
|
+
# class can be used to reset the whole stream back to the beginning. Using
|
|
160
|
+
# _seek_ of this class to seek directly to offset <tt>0</tt> using
|
|
161
|
+
# <tt>IO::SEEK_SET</tt> for whence will also work in this case.
|
|
162
|
+
#
|
|
163
|
+
# Any other seeking attempts, will raise Errno::EINVAL exceptions.
|
|
121
164
|
#
|
|
122
165
|
# The _fill_size_ attribute is set to <tt>0</tt> by default under the
|
|
123
166
|
# assumption that _io_ is already buffered.
|
|
@@ -138,11 +181,11 @@ module Archive; class Zip; module Codec
|
|
|
138
181
|
nil
|
|
139
182
|
end
|
|
140
183
|
|
|
141
|
-
# Returns an instance of Archive::Zip::
|
|
142
|
-
#
|
|
143
|
-
#
|
|
144
|
-
#
|
|
145
|
-
#
|
|
184
|
+
# Returns an instance of Archive::Zip::DataDescriptor with information
|
|
185
|
+
# regarding the data which has passed through this object from the
|
|
186
|
+
# delegate object. It is recommended to call the close method before
|
|
187
|
+
# calling this in order to ensure that no further read operations change
|
|
188
|
+
# the state of this object.
|
|
146
189
|
def data_descriptor
|
|
147
190
|
DataDescriptor.new(
|
|
148
191
|
@crc32,
|
|
@@ -165,21 +208,27 @@ module Archive; class Zip; module Codec
|
|
|
165
208
|
end
|
|
166
209
|
|
|
167
210
|
# Allows resetting this object and the delegate object back to the
|
|
168
|
-
# beginning of the stream
|
|
169
|
-
#
|
|
170
|
-
#
|
|
171
|
-
#
|
|
172
|
-
#
|
|
211
|
+
# beginning of the stream or reporting the current position in the stream.
|
|
212
|
+
#
|
|
213
|
+
# Raises Errno::EINVAL unless _offset_ is <tt>0</tt> and _whence_ is
|
|
214
|
+
# either IO::SEEK_SET or IO::SEEK_CUR. Raises Errno::EINVAL if _whence_
|
|
215
|
+
# is IO::SEEK_SEK and the delegate object does not respond to the _rewind_
|
|
216
|
+
# method.
|
|
173
217
|
def unbuffered_seek(offset, whence = IO::SEEK_SET)
|
|
174
|
-
unless offset == 0 &&
|
|
175
|
-
|
|
218
|
+
unless offset == 0 &&
|
|
219
|
+
((whence == IO::SEEK_SET && @io.respond_to?(:rewind)) ||
|
|
220
|
+
whence == IO::SEEK_CUR) then
|
|
221
|
+
raise Errno::EINVAL
|
|
176
222
|
end
|
|
177
|
-
|
|
178
|
-
|
|
223
|
+
|
|
224
|
+
case whence
|
|
225
|
+
when IO::SEEK_SET
|
|
226
|
+
@io.rewind
|
|
227
|
+
@crc32 = 0
|
|
228
|
+
@uncompressed_size = 0
|
|
229
|
+
when IO::SEEK_CUR
|
|
230
|
+
@uncompressed_size
|
|
179
231
|
end
|
|
180
|
-
@io.rewind
|
|
181
|
-
@crc32 = 0
|
|
182
|
-
@uncompressed_size = 0
|
|
183
232
|
end
|
|
184
233
|
end
|
|
185
234
|
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
require 'archive/support/integer'
|
|
1
4
|
require 'archive/support/io-like'
|
|
2
5
|
require 'archive/support/time'
|
|
3
6
|
require 'archive/support/zlib'
|
|
@@ -108,18 +111,40 @@ module Archive; class Zip; module Codec
|
|
|
108
111
|
# The _flush_size_ attribute is set to <tt>0</tt> by default under the
|
|
109
112
|
# assumption that _io_ is already buffered.
|
|
110
113
|
def initialize(io, password, mtime)
|
|
114
|
+
# Keep track of the total number of bytes written.
|
|
115
|
+
# Set this here so that the call to #initialize_keys caused by the call
|
|
116
|
+
# to super below does not cause errors in #unbuffered_write due to this
|
|
117
|
+
# attribute being uninitialized.
|
|
118
|
+
@total_bytes_in = 0
|
|
119
|
+
|
|
120
|
+
# This buffer is used to hold the encrypted version of the string most
|
|
121
|
+
# recently sent to #unbuffered_write.
|
|
122
|
+
@encrypt_buffer = ''
|
|
123
|
+
|
|
111
124
|
super(io, password, mtime)
|
|
112
125
|
|
|
113
126
|
# Assume that the delegate IO object is already buffered.
|
|
114
127
|
self.flush_size = 0
|
|
115
128
|
end
|
|
116
129
|
|
|
117
|
-
# Closes
|
|
118
|
-
# _close_delegate_ is +true+, the delegate object used as a data sink
|
|
119
|
-
# also be closed using its close method.
|
|
130
|
+
# Closes the stream after flushing the encryption buffer to the delegate.
|
|
131
|
+
# If _close_delegate_ is +true+, the delegate object used as a data sink
|
|
132
|
+
# will also be closed using its close method.
|
|
133
|
+
#
|
|
134
|
+
# Raises IOError if called more than once.
|
|
120
135
|
def close(close_delegate = true)
|
|
136
|
+
flush()
|
|
137
|
+
begin
|
|
138
|
+
until @encrypt_buffer.empty? do
|
|
139
|
+
@encrypt_buffer.slice!(0, io.write(@encrypt_buffer))
|
|
140
|
+
end
|
|
141
|
+
rescue Errno::EAGAIN, Errno::EINTR
|
|
142
|
+
retry if write_ready?
|
|
143
|
+
end
|
|
144
|
+
|
|
121
145
|
super()
|
|
122
146
|
io.close if close_delegate
|
|
147
|
+
nil
|
|
123
148
|
end
|
|
124
149
|
|
|
125
150
|
private
|
|
@@ -131,31 +156,74 @@ module Archive; class Zip; module Codec
|
|
|
131
156
|
super
|
|
132
157
|
|
|
133
158
|
# Create and encrypt a 12 byte header to protect the encrypted file data
|
|
134
|
-
# from attack. The first 10 bytes are random, and the
|
|
159
|
+
# from attack. The first 10 bytes are random, and the last 2 bytes are
|
|
135
160
|
# the low order word of the last modified time of the entry in DOS
|
|
136
161
|
# format.
|
|
162
|
+
header = ''
|
|
137
163
|
10.times do
|
|
138
|
-
|
|
164
|
+
header << rand(256).chr
|
|
139
165
|
end
|
|
140
166
|
time = mtime.to_dos_time.to_i
|
|
141
|
-
|
|
142
|
-
|
|
167
|
+
header << (time & 0xff).chr << ((time >> 8) & 0xff).chr
|
|
168
|
+
|
|
169
|
+
# Take care to ensure that all bytes in the header are written.
|
|
170
|
+
while header.size > 0 do
|
|
171
|
+
begin
|
|
172
|
+
header.slice!(0, unbuffered_write(header))
|
|
173
|
+
rescue Errno::EAGAIN, Errno::EINTR
|
|
174
|
+
sleep(1)
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Reset the total bytes written in order to disregard the header.
|
|
179
|
+
@total_bytes_in = 0
|
|
180
|
+
|
|
143
181
|
nil
|
|
144
182
|
end
|
|
145
183
|
|
|
184
|
+
# Allows resetting this object and the delegate object back to the
|
|
185
|
+
# beginning of the stream or reporting the current position in the stream.
|
|
186
|
+
#
|
|
187
|
+
# Raises Errno::EINVAL unless _offset_ is <tt>0</tt> and _whence_ is
|
|
188
|
+
# either IO::SEEK_SET or IO::SEEK_CUR. Raises Errno::EINVAL if _whence_
|
|
189
|
+
# is IO::SEEK_SEK and the delegate object does not respond to the _rewind_
|
|
190
|
+
# method.
|
|
191
|
+
def unbuffered_seek(offset, whence = IO::SEEK_SET)
|
|
192
|
+
unless offset == 0 &&
|
|
193
|
+
((whence == IO::SEEK_SET && @io.respond_to?(:rewind)) ||
|
|
194
|
+
whence == IO::SEEK_CUR) then
|
|
195
|
+
raise Errno::EINVAL
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
case whence
|
|
199
|
+
when IO::SEEK_SET
|
|
200
|
+
io.rewind
|
|
201
|
+
@encrypt_buffer = ''
|
|
202
|
+
initialize_keys
|
|
203
|
+
@total_bytes_in = 0
|
|
204
|
+
when IO::SEEK_CUR
|
|
205
|
+
@total_bytes_in
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
146
209
|
# Encrypts and writes _string_ to the delegate IO object. Returns the
|
|
147
|
-
# number of bytes of _string_ written.
|
|
148
|
-
# converted into one using its _to_s_ method.
|
|
210
|
+
# number of bytes of _string_ written.
|
|
149
211
|
def unbuffered_write(string)
|
|
150
|
-
|
|
151
|
-
|
|
212
|
+
# First try to write out the contents of the encrypt buffer because if
|
|
213
|
+
# that raises a failure we can let that pass up the call stack without
|
|
214
|
+
# having polluted the encryption state.
|
|
215
|
+
until @encrypt_buffer.empty? do
|
|
216
|
+
@encrypt_buffer.slice!(0, io.write(@encrypt_buffer))
|
|
217
|
+
end
|
|
218
|
+
# At this point we can encrypt the given string into a new buffer and
|
|
219
|
+
# behave as if it was written.
|
|
152
220
|
string.each_byte do |byte|
|
|
153
221
|
temp = decrypt_byte
|
|
154
|
-
|
|
155
|
-
bytes_written += 1
|
|
222
|
+
@encrypt_buffer << (byte ^ temp).chr
|
|
156
223
|
update_keys(byte.chr)
|
|
157
224
|
end
|
|
158
|
-
|
|
225
|
+
@total_bytes_in += string.length
|
|
226
|
+
string.length
|
|
159
227
|
end
|
|
160
228
|
end
|
|
161
229
|
|
|
@@ -187,15 +255,37 @@ module Archive; class Zip; module Codec
|
|
|
187
255
|
end
|
|
188
256
|
|
|
189
257
|
# Creates a new instance of this class using _io_ as a data source. _io_
|
|
190
|
-
# must be readable and provide a
|
|
191
|
-
# raised when performing read operations.
|
|
192
|
-
#
|
|
193
|
-
# the
|
|
194
|
-
#
|
|
258
|
+
# must be readable and provide a _read_ method as an IO instance would or
|
|
259
|
+
# errors will be raised when performing read operations. _password_
|
|
260
|
+
# should be the encryption key. _mtime_ must be the last modified time of
|
|
261
|
+
# the entry to be encrypted/decrypted.
|
|
262
|
+
#
|
|
263
|
+
# This class has extremely limited seek capabilities. It is possible to
|
|
264
|
+
# seek with an offset of <tt>0</tt> and a whence of <tt>IO::SEEK_CUR</tt>.
|
|
265
|
+
# As a result, the _pos_ and _tell_ methods also work as expected.
|
|
266
|
+
#
|
|
267
|
+
# Due to certain optimizations within IO::Like#seek and if there is data
|
|
268
|
+
# in the read buffer, the _seek_ method can be used to seek forward from
|
|
269
|
+
# the current stream position up to the end of the buffer. Unless it is
|
|
270
|
+
# known definitively how much data is in the buffer, it is best to avoid
|
|
271
|
+
# relying on this behavior.
|
|
272
|
+
#
|
|
273
|
+
# If _io_ also responds to _rewind_, then the _rewind_ method of this
|
|
274
|
+
# class can be used to reset the whole stream back to the beginning. Using
|
|
275
|
+
# _seek_ of this class to seek directly to offset <tt>0</tt> using
|
|
276
|
+
# <tt>IO::SEEK_SET</tt> for whence will also work in this case.
|
|
277
|
+
#
|
|
278
|
+
# Any other seeking attempts, will raise Errno::EINVAL exceptions.
|
|
195
279
|
#
|
|
196
280
|
# The _fill_size_ attribute is set to <tt>0</tt> by default under the
|
|
197
281
|
# assumption that _io_ is already buffered.
|
|
198
282
|
def initialize(io, password, mtime)
|
|
283
|
+
# Keep track of the total number of bytes read.
|
|
284
|
+
# Set this here so that the call to #initialize_keys caused by the call
|
|
285
|
+
# to super below does not cause errors in #unbuffered_read due to this
|
|
286
|
+
# attribute being uninitialized.
|
|
287
|
+
@total_bytes_out = 0
|
|
288
|
+
|
|
199
289
|
super(io, password, mtime)
|
|
200
290
|
|
|
201
291
|
# Assume that the delegate IO object is already buffered.
|
|
@@ -203,8 +293,8 @@ module Archive; class Zip; module Codec
|
|
|
203
293
|
end
|
|
204
294
|
|
|
205
295
|
# Closes this object so that further write operations will fail. If
|
|
206
|
-
# _close_delegate_ is +true+, the delegate object used as a data
|
|
207
|
-
# also be closed using its close method.
|
|
296
|
+
# _close_delegate_ is +true+, the delegate object used as a data source
|
|
297
|
+
# will also be closed using its close method.
|
|
208
298
|
def close(close_delegate = true)
|
|
209
299
|
super()
|
|
210
300
|
io.close if close_delegate
|
|
@@ -218,8 +308,20 @@ module Archive; class Zip; module Codec
|
|
|
218
308
|
def initialize_keys
|
|
219
309
|
super
|
|
220
310
|
|
|
221
|
-
#
|
|
222
|
-
|
|
311
|
+
# Load the 12 byte header taking care to ensure that all bytes are read.
|
|
312
|
+
bytes_needed = 12
|
|
313
|
+
while bytes_needed > 0 do
|
|
314
|
+
begin
|
|
315
|
+
bytes_read = unbuffered_read(bytes_needed)
|
|
316
|
+
bytes_needed -= bytes_read.size
|
|
317
|
+
rescue Errno::EAGAIN, Errno::EINTR
|
|
318
|
+
sleep(1)
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
# Reset the total bytes read in order to disregard the header.
|
|
323
|
+
@total_bytes_out = 0
|
|
324
|
+
|
|
223
325
|
nil
|
|
224
326
|
end
|
|
225
327
|
|
|
@@ -230,28 +332,37 @@ module Archive; class Zip; module Codec
|
|
|
230
332
|
def unbuffered_read(length)
|
|
231
333
|
buffer = io.read(length)
|
|
232
334
|
raise EOFError, 'end of file reached' if buffer.nil?
|
|
335
|
+
@total_bytes_out += buffer.length
|
|
233
336
|
|
|
234
|
-
(
|
|
235
|
-
buffer[i] = (buffer[i] ^ decrypt_byte)
|
|
337
|
+
0.upto(buffer.length - 1) do |i|
|
|
338
|
+
buffer[i] = (buffer[i].ord ^ decrypt_byte).chr
|
|
236
339
|
update_keys(buffer[i].chr)
|
|
237
340
|
end
|
|
238
341
|
buffer
|
|
239
342
|
end
|
|
240
343
|
|
|
241
344
|
# Allows resetting this object and the delegate object back to the
|
|
242
|
-
# beginning of the stream
|
|
243
|
-
#
|
|
244
|
-
#
|
|
345
|
+
# beginning of the stream or reporting the current position in the stream.
|
|
346
|
+
#
|
|
347
|
+
# Raises Errno::EINVAL unless _offset_ is <tt>0</tt> and _whence_ is
|
|
348
|
+
# either IO::SEEK_SET or IO::SEEK_CUR. Raises Errno::EINVAL if _whence_
|
|
349
|
+
# is IO::SEEK_SEK and the delegate object does not respond to the _rewind_
|
|
350
|
+
# method.
|
|
245
351
|
def unbuffered_seek(offset, whence = IO::SEEK_SET)
|
|
246
|
-
unless offset == 0 &&
|
|
247
|
-
|
|
352
|
+
unless offset == 0 &&
|
|
353
|
+
((whence == IO::SEEK_SET && @io.respond_to?(:rewind)) ||
|
|
354
|
+
whence == IO::SEEK_CUR) then
|
|
355
|
+
raise Errno::EINVAL
|
|
248
356
|
end
|
|
249
|
-
|
|
250
|
-
|
|
357
|
+
|
|
358
|
+
case whence
|
|
359
|
+
when IO::SEEK_SET
|
|
360
|
+
io.rewind
|
|
361
|
+
initialize_keys
|
|
362
|
+
@total_bytes_out = 0
|
|
363
|
+
when IO::SEEK_CUR
|
|
364
|
+
@total_bytes_out
|
|
251
365
|
end
|
|
252
|
-
io.rewind
|
|
253
|
-
initialize_keys
|
|
254
|
-
0
|
|
255
366
|
end
|
|
256
367
|
end
|
|
257
368
|
|