archive-zip 0.1.1 → 0.2.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.
@@ -0,0 +1,176 @@
1
+ require 'archive/support/io-like'
2
+ require 'archive/zip/codec'
3
+
4
+ module Archive; class Zip; module Codec
5
+ # Archive::Zip::Codec::NullEncryption is a handle for an encryption codec
6
+ # which passes data through itself unchanged.
7
+ class NullEncryption
8
+ # Archive::Zip::Codec::NullEncryption::Encrypt is a writable, IO-like object
9
+ # which writes all data written to it directly to a delegate IO object. A
10
+ # _close_ method is also provided which can optionally closed the delegate
11
+ # object.
12
+ class Encrypt
13
+ include IO::Like
14
+
15
+ # Creates a new instance of this class with the given argument using #new
16
+ # and then passes the instance to the given block. The #close method is
17
+ # guaranteed to be called after the block completes.
18
+ #
19
+ # Equivalent to #new if no block is given.
20
+ def self.open(io)
21
+ encrypt_io = new(io)
22
+ return encrypt_io unless block_given?
23
+
24
+ begin
25
+ yield(encrypt_io)
26
+ ensure
27
+ encrypt_io.close unless encrypt_io.closed?
28
+ end
29
+ end
30
+
31
+ # Creates a new instance of this class using _io_ as a data sink. _io_
32
+ # must be writable and must provide a write method as IO does or errors
33
+ # will be raised when performing write operations.
34
+ #
35
+ # The _flush_size_ attribute is set to <tt>0</tt> by default under the
36
+ # assumption that _io_ is already buffered.
37
+ def initialize(io)
38
+ @io = io
39
+ # Assume that the delegate IO object is already buffered.
40
+ self.flush_size = 0
41
+ end
42
+
43
+ # Closes this object so that further write operations will fail. If
44
+ # _close_delegate_ is +true+, the delegate object used as a data sink will
45
+ # also be closed using its close method.
46
+ def close(close_delegate = true)
47
+ super()
48
+ @io.close if close_delegate
49
+ end
50
+
51
+ private
52
+
53
+ # Writes _string_ to the delegate IO object and returns the result.
54
+ def unbuffered_write(string)
55
+ @io.write(string)
56
+ end
57
+ end
58
+
59
+ # Archive::Zip::Codec::NullEncryption::Decrypt is a readable, IO-like object
60
+ # which reads data directly from a delegate IO object, making no changes. A
61
+ # _close_ method is also provided which can optionally closed the delegate
62
+ # object.
63
+ class Decrypt
64
+ include IO::Like
65
+
66
+ # Creates a new instance of this class with the given argument using #new
67
+ # and then passes the instance to the given block. The #close method is
68
+ # guaranteed to be called after the block completes.
69
+ #
70
+ # Equivalent to #new if no block is given.
71
+ def self.open(io)
72
+ decrypt_io = new(io)
73
+ return decrypt_io unless block_given?
74
+
75
+ begin
76
+ yield(decrypt_io)
77
+ ensure
78
+ decrypt_io.close unless decrypt_io.closed?
79
+ end
80
+ end
81
+
82
+ # Creates a new instance of this class using _io_ as a data source. _io_
83
+ # must be readable and provide a read method as IO does or errors will be
84
+ # raised when performing read operations. If _io_ provides a rewind
85
+ # method, this class' rewind method will be enabled.
86
+ #
87
+ # The _fill_size_ attribute is set to <tt>0</tt> by default under the
88
+ # assumption that _io_ is already buffered.
89
+ def initialize(io)
90
+ @io = io
91
+ # Assume that the delegate IO object is already buffered.
92
+ self.fill_size = 0
93
+ end
94
+
95
+ # Closes this object so that further write operations will fail. If
96
+ # _close_delegate_ is +true+, the delegate object used as a data sink will
97
+ # also be closed using its close method.
98
+ def close(close_delegate = true)
99
+ super()
100
+ @io.close if close_delegate
101
+ end
102
+
103
+ private
104
+
105
+ # Reads and returns at most _length_ bytes from the delegate IO object.
106
+ #
107
+ # Raises EOFError if there is no data to read.
108
+ def unbuffered_read(length)
109
+ buffer = @io.read(length)
110
+ raise EOFError, 'end of file reached' if buffer.nil?
111
+
112
+ buffer
113
+ end
114
+
115
+ # Allows resetting this object and the delegate object back to the
116
+ # beginning of the stream. _offset_ must be <tt>0</tt> and _whence_ must
117
+ # be IO::SEEK_SET or an error will be raised. The delegate object must
118
+ # respond to the _rewind_ method or an error will be raised.
119
+ def unbuffered_seek(offset, whence = IO::SEEK_SET)
120
+ unless offset == 0 && whence == IO::SEEK_SET then
121
+ raise Errno::EINVAL, 'Invalid argument'
122
+ end
123
+ unless @io.respond_to?(:rewind) then
124
+ raise Errno::ESPIPE, 'Illegal seek'
125
+ end
126
+ @io.rewind
127
+ end
128
+ end
129
+
130
+ # This method signature is part of the interface contract expected by
131
+ # Archive::Zip::Entry for encryption codec objects.
132
+ #
133
+ # A convenience method for creating an
134
+ # Archive::Zip::Codec::NullEncryption::Encrypt object using that class' open
135
+ # method.
136
+ def encryptor(io, password, &b)
137
+ Encrypt.open(io, &b)
138
+ end
139
+
140
+ # This method signature is part of the interface contract expected by
141
+ # Archive::Zip::Entry for encryption codec objects.
142
+ #
143
+ # A convenience method for creating an
144
+ # Archive::Zip::Codec::NullEncryption::Decrypt object using that class' open
145
+ # method.
146
+ def decryptor(io, password, &b)
147
+ Decrypt.open(io, &b)
148
+ end
149
+
150
+ # This method signature is part of the interface contract expected by
151
+ # Archive::Zip::Entry for encryption codec objects.
152
+ #
153
+ # Returns an integer which indicates the version of the official ZIP
154
+ # specification which introduced support for this encryption codec.
155
+ def version_needed_to_extract
156
+ 0x0014
157
+ end
158
+
159
+ # This method signature is part of the interface contract expected by
160
+ # Archive::Zip::Entry for encryption codec objects.
161
+ #
162
+ # Returns an integer representing the general purpose flags of a ZIP archive
163
+ # entry using this encryption codec.
164
+ def general_purpose_flags
165
+ 0b0000000000000000
166
+ end
167
+
168
+ # This method signature is part of the interface contract expected by
169
+ # Archive::Zip::Entry for encryption codec objects.
170
+ #
171
+ # Returns the size of the encryption header in bytes.
172
+ def header_size
173
+ 0
174
+ end
175
+ end
176
+ end; end; end
@@ -1,19 +1,22 @@
1
1
  require 'archive/support/io-like'
2
2
  require 'archive/zip/codec'
3
- require 'archive/zip/datadescriptor'
3
+ require 'archive/zip/data_descriptor'
4
4
 
5
5
  module Archive; class Zip; module Codec
6
6
  # Archive::Zip::Codec::Store is a handle for the store-unstore (no
7
7
  # compression) codec.
8
8
  class Store
9
- # Archive::Zip::Codec::Store::Store is simply a writable, IO-like wrapper
9
+ # Archive::Zip::Codec::Store::Compress is simply a writable, IO-like wrapper
10
10
  # around a writable, IO-like object which provides a CRC32 checksum of the
11
11
  # data written through it as well as the count of the total amount of data.
12
- # A #close method is also provided which can optionally close the delegate
12
+ # A _close_ method is also provided which can optionally close the delegate
13
13
  # object. In addition a convenience method is provided for generating
14
14
  # DataDescriptor objects based on the data which is passed through this
15
15
  # object.
16
- class Store
16
+ #
17
+ # Instances of this class should only be accessed via the
18
+ # Archive::Zip::Codec::Store#compressor method.
19
+ class Compress
17
20
  include IO::Like
18
21
 
19
22
  # Creates a new instance of this class with the given argument using #new
@@ -36,8 +39,8 @@ module Archive; class Zip; module Codec
36
39
  # must be writable and must provide a write method as IO does or errors
37
40
  # will be raised when performing write operations.
38
41
  #
39
- # The _flush_size_ attribute is set to +0+ by default under the assumption
40
- # that _io_ is already buffered.
42
+ # The _flush_size_ attribute is set to <tt>0</tt> by default under the
43
+ # assumption that _io_ is already buffered.
41
44
  def initialize(io)
42
45
  @io = io
43
46
  @crc32 = 0
@@ -82,14 +85,17 @@ module Archive; class Zip; module Codec
82
85
  end
83
86
  end
84
87
 
85
- # Archive::Zip::Codec::Store::Unstore is a readable, IO-like wrapper around
86
- # a readable, IO-like object which provides a CRC32 checksum of the data
87
- # read through it as well as the count of the total amount of data. A
88
- # #close method is also provided which can optionally close the delegate
88
+ # Archive::Zip::Codec::Store::Decompress is a readable, IO-like wrapper
89
+ # around a readable, IO-like object which provides a CRC32 checksum of the
90
+ # data read through it as well as the count of the total amount of data. A
91
+ # _close_ method is also provided which can optionally close the delegate
89
92
  # object. In addition a convenience method is provided for generating
90
93
  # DataDescriptor objects based on the data which is passed through this
91
94
  # object.
92
- class Unstore
95
+ #
96
+ # Instances of this class should only be accessed via the
97
+ # Archive::Zip::Codec::Store#decompressor method.
98
+ class Decompress
93
99
  include IO::Like
94
100
 
95
101
  # Creates a new instance of this class with the given arguments using #new
@@ -113,8 +119,8 @@ module Archive; class Zip; module Codec
113
119
  # raised when performing read operations. If _io_ provides a rewind
114
120
  # method, this class' rewind method will be enabled.
115
121
  #
116
- # The _fill_size_ attribute is set to +0+ by default under the assumption
117
- # that _io_ is already buffered.
122
+ # The _fill_size_ attribute is set to <tt>0</tt> by default under the
123
+ # assumption that _io_ is already buffered.
118
124
  def initialize(io)
119
125
  @io = io
120
126
  @crc32 = 0
@@ -151,18 +157,16 @@ module Archive; class Zip; module Codec
151
157
  # uncompressed_size and crc32 attributes as a side effect.
152
158
  def unbuffered_read(length)
153
159
  buffer = @io.read(length)
154
- if buffer.nil? then
155
- raise EOFError, 'end of file reached'
156
- else
157
- @uncompressed_size += buffer.length
158
- @crc32 = Zlib.crc32(buffer, @crc32)
159
- end
160
+ raise EOFError, 'end of file reached' if buffer.nil?
161
+
162
+ @uncompressed_size += buffer.length
163
+ @crc32 = Zlib.crc32(buffer, @crc32)
160
164
  buffer
161
165
  end
162
166
 
163
167
  # Allows resetting this object and the delegate object back to the
164
- # beginning of the stream. _offset_ must be +0+ and _whence_ must be
165
- # IO::SEEK_SET or an error will be raised. The delegate object must
168
+ # beginning of the stream. _offset_ must be <tt>0</tt> and _whence_ must
169
+ # be IO::SEEK_SET or an error will be raised. The delegate object must
166
170
  # respond to the _rewind_ method or an error will be raised. The
167
171
  # uncompressed_size and crc32 attributes are reinitialized as a side
168
172
  # effect.
@@ -179,14 +183,15 @@ module Archive; class Zip; module Codec
179
183
  end
180
184
  end
181
185
 
182
- # The numeric identifier assigned to this codec by the ZIP specification.
186
+ # The numeric identifier assigned to this compresion codec by the ZIP
187
+ # specification.
183
188
  ID = 0
184
189
 
185
- # Register this codec.
186
- CODECS[ID] = self
190
+ # Register this compression codec.
191
+ COMPRESSION_CODECS[ID] = self
187
192
 
188
193
  # This method signature is part of the interface contract expected by
189
- # Archive::Zip::Entry for codec objects.
194
+ # Archive::Zip::Entry for compression codec objects.
190
195
  #
191
196
  # Creates a new instance of this class. _general_purpose_flags_ is not
192
197
  # used.
@@ -194,46 +199,47 @@ module Archive; class Zip; module Codec
194
199
  end
195
200
 
196
201
  # This method signature is part of the interface contract expected by
197
- # Archive::Zip::Entry for codec objects.
202
+ # Archive::Zip::Entry for compression codec objects.
198
203
  #
199
- # A convenience method for creating an Archive::Zip::Codec::Store::Store
204
+ # A convenience method for creating an Archive::Zip::Codec::Store::Compress
200
205
  # object using that class' open method.
201
206
  def compressor(io, &b)
202
- Store.open(io, &b)
207
+ Compress.open(io, &b)
203
208
  end
204
209
 
205
210
  # This method signature is part of the interface contract expected by
206
- # Archive::Zip::Entry for codec objects.
211
+ # Archive::Zip::Entry for compression codec objects.
207
212
  #
208
- # A convenience method for creating an Archive::Zip::Codec::Store::Unstore
209
- # object using that class' open method.
213
+ # A convenience method for creating an
214
+ # Archive::Zip::Codec::Store::Decompress object using that class' open
215
+ # method.
210
216
  def decompressor(io, &b)
211
- Unstore.open(io, &b)
217
+ Decompress.open(io, &b)
212
218
  end
213
219
 
214
220
  # This method signature is part of the interface contract expected by
215
- # Archive::Zip::Entry for codec objects.
221
+ # Archive::Zip::Entry for compression codec objects.
216
222
  #
217
223
  # Returns an integer which indicates the version of the official ZIP
218
- # specification which introduced support for this codec.
224
+ # specification which introduced support for this compression codec.
219
225
  def version_needed_to_extract
220
226
  0x000a
221
227
  end
222
228
 
223
229
  # This method signature is part of the interface contract expected by
224
- # Archive::Zip::Entry for codec objects.
230
+ # Archive::Zip::Entry for compression codec objects.
225
231
  #
226
- # Returns an integer used to flag that this codec is used for a particular
227
- # ZIP archive entry.
232
+ # Returns an integer used to flag that this compression codec is used for a
233
+ # particular ZIP archive entry.
228
234
  def compression_method
229
235
  ID
230
236
  end
231
237
 
232
238
  # This method signature is part of the interface contract expected by
233
- # Archive::Zip::Entry for codec objects.
239
+ # Archive::Zip::Entry for compression codec objects.
234
240
  #
235
- # Returns +0+ since this codec does not make use of general purpose flags of
236
- # ZIP archive entries.
241
+ # Returns <tt>0</tt> since this compression codec does not make use of
242
+ # general purpose flags of ZIP archive entries.
237
243
  def general_purpose_flags
238
244
  0
239
245
  end
@@ -0,0 +1,308 @@
1
+ require 'archive/support/io-like'
2
+ require 'archive/support/time'
3
+ require 'archive/support/zlib'
4
+ require 'archive/zip/codec'
5
+
6
+ module Archive; class Zip; module Codec
7
+ # Archive::Zip::Codec::TraditionalEncryption is a handle for the traditional
8
+ # encryption codec.
9
+ class TraditionalEncryption
10
+ # Archive::Zip::Codec::TraditionalEncryption::Base provides some basic
11
+ # methods which are shared between
12
+ # Archive::Zip::Codec::TraditionalEncryption::Encrypt and
13
+ # Archive::Zip::Codec::TraditionalEncryption::Decrypt.
14
+ #
15
+ # Do not use this class directly.
16
+ class Base
17
+ # Creates a new instance of this class. _io_ must be an IO-like object to
18
+ # be used as a delegate for IO operations. _password_ should be the
19
+ # encryption key. _mtime_ must be the last modified time of the entry to
20
+ # be encrypted/decrypted.
21
+ def initialize(io, password, mtime)
22
+ @io = io
23
+ @password = password.nil? ? '' : password
24
+ @mtime = mtime
25
+ initialize_keys
26
+ end
27
+
28
+ protected
29
+
30
+ # The delegate IO-like object.
31
+ attr_reader :io
32
+ # The encryption key.
33
+ attr_reader :password
34
+ # The last modified time of the entry being encrypted. This is used in
35
+ # the entryption header as a way to check the password.
36
+ attr_reader :mtime
37
+
38
+ private
39
+
40
+ # Initializes the keys used for encrypting/decrypting data by setting the
41
+ # keys to well known values and then processing them with the password.
42
+ def initialize_keys
43
+ @key0 = 0x12345678
44
+ @key1 = 0x23456789
45
+ @key2 = 0x34567890
46
+ @password.each_byte { |byte| update_keys(byte.chr) }
47
+ nil
48
+ end
49
+
50
+ # Updates the keys following the ZIP specification using _char_, which
51
+ # must be a single byte String.
52
+ def update_keys(char)
53
+ # For some reason not explained in the ZIP specification but discovered
54
+ # in the source for InfoZIP, the old CRC value must first have its bits
55
+ # flipped before processing. The new CRC value must have its bits
56
+ # flipped as well for storage and later use. This applies to the
57
+ # handling of @key0 and @key2.
58
+ #
59
+ # NOTE: XOR'ing with 0xffffffff is used instead of simple bit negation
60
+ # in case this is run on a platform with a native integer size of
61
+ # something other than 32 bits.
62
+ @key0 = Zlib.crc32(char, @key0 ^ 0xffffffff) ^ 0xffffffff
63
+ @key1 = ((@key1 + (@key0 & 0xff)) * 134775813 + 1) & 0xffffffff
64
+ @key2 = Zlib.crc32((@key1 >> 24).chr, @key2 ^ 0xffffffff) ^ 0xffffffff
65
+ nil
66
+ end
67
+
68
+ # Returns the next decryption byte based on the current keys.
69
+ def decrypt_byte
70
+ temp = (@key2 | 2) & 0x0000ffff
71
+ ((temp * (temp ^ 1)) >> 8) & 0x000000ff
72
+ end
73
+ end
74
+
75
+ # Archive::Zip::Codec::TraditionalEncryption::Encrypt is a writable, IO-like
76
+ # object which encrypts data written to it using the traditional encryption
77
+ # algorithm as documented in the ZIP specification and writes the result to
78
+ # a delegate IO object. A _close_ method is also provided which can
79
+ # optionally close the delegate object.
80
+ #
81
+ # Instances of this class should only be accessed via the
82
+ # Archive::Zip::Codec::TraditionalEncryption#compressor method.
83
+ class Encrypt < Base
84
+ include IO::Like
85
+
86
+ # Creates a new instance of this class with the given argument using #new
87
+ # and then passes the instance to the given block. The #close method is
88
+ # guaranteed to be called after the block completes.
89
+ #
90
+ # Equivalent to #new if no block is given.
91
+ def self.open(io, password, mtime)
92
+ encrypt_io = new(io, password, mtime)
93
+ return encrypt_io unless block_given?
94
+
95
+ begin
96
+ yield(encrypt_io)
97
+ ensure
98
+ encrypt_io.close unless encrypt_io.closed?
99
+ end
100
+ end
101
+
102
+ # Creates a new instance of this class using _io_ as a data sink. _io_
103
+ # must be writable and must provide a write method as IO does or errors
104
+ # will be raised when performing write operations. _password_ should be
105
+ # the encryption key. _mtime_ must be the last modified time of the entry
106
+ # to be encrypted/decrypted.
107
+ #
108
+ # The _flush_size_ attribute is set to <tt>0</tt> by default under the
109
+ # assumption that _io_ is already buffered.
110
+ def initialize(io, password, mtime)
111
+ super(io, password, mtime)
112
+
113
+ # Assume that the delegate IO object is already buffered.
114
+ self.flush_size = 0
115
+ end
116
+
117
+ # Closes this object so that further write operations will fail. If
118
+ # _close_delegate_ is +true+, the delegate object used as a data sink will
119
+ # also be closed using its close method.
120
+ def close(close_delegate = true)
121
+ super()
122
+ io.close if close_delegate
123
+ end
124
+
125
+ private
126
+
127
+ # Extend the inherited initialize_keys method to further initialize the
128
+ # keys by encrypting and writing a 12 byte header to the delegate IO
129
+ # object.
130
+ def initialize_keys
131
+ super
132
+
133
+ # Create and encrypt a 12 byte header to protect the encrypted file data
134
+ # from attack. The first 10 bytes are random, and the lat 2 bytes are
135
+ # the low order word of the last modified time of the entry in DOS
136
+ # format.
137
+ 10.times do
138
+ unbuffered_write(rand(256).chr)
139
+ end
140
+ time = mtime.to_dos_time.to_i
141
+ unbuffered_write((time & 0xff).chr)
142
+ unbuffered_write(((time >> 8) & 0xff).chr)
143
+ nil
144
+ end
145
+
146
+ # Encrypts and writes _string_ to the delegate IO object. Returns the
147
+ # number of bytes of _string_ written. If _string_ is not a String, it is
148
+ # converted into one using its _to_s_ method.
149
+ def unbuffered_write(string)
150
+ string = string.to_s
151
+ bytes_written = 0
152
+ string.each_byte do |byte|
153
+ temp = decrypt_byte
154
+ break unless io.write((byte ^ temp).chr) > 0
155
+ bytes_written += 1
156
+ update_keys(byte.chr)
157
+ end
158
+ bytes_written
159
+ end
160
+ end
161
+
162
+ # Archive::Zip::Codec::TraditionalEncryption::Decrypt is a readable, IO-like
163
+ # object which decrypts data data it reads from a delegate IO object using
164
+ # the traditional encryption algorithm as documented in the ZIP
165
+ # specification. A _close_ method is also provided which can optionally
166
+ # close the delegate object.
167
+ #
168
+ # Instances of this class should only be accessed via the
169
+ # Archive::Zip::Codec::TraditionalEncryption#decompressor method.
170
+ class Decrypt < Base
171
+ include IO::Like
172
+
173
+ # Creates a new instance of this class with the given argument using #new
174
+ # and then passes the instance to the given block. The #close method is
175
+ # guaranteed to be called after the block completes.
176
+ #
177
+ # Equivalent to #new if no block is given.
178
+ def self.open(io, password, mtime)
179
+ decrypt_io = new(io, password, mtime)
180
+ return decrypt_io unless block_given?
181
+
182
+ begin
183
+ yield(decrypt_io)
184
+ ensure
185
+ decrypt_io.close unless decrypt_io.closed?
186
+ end
187
+ end
188
+
189
+ # Creates a new instance of this class using _io_ as a data source. _io_
190
+ # must be readable and provide a read method as IO does or errors will be
191
+ # raised when performing read operations. If _io_ provides a rewind
192
+ # method, this class' rewind method will be enabled. _password_ should be
193
+ # the encryption key. _mtime_ must be the last modified time of the entry
194
+ # to be encrypted/decrypted.
195
+ #
196
+ # The _fill_size_ attribute is set to <tt>0</tt> by default under the
197
+ # assumption that _io_ is already buffered.
198
+ def initialize(io, password, mtime)
199
+ super(io, password, mtime)
200
+
201
+ # Assume that the delegate IO object is already buffered.
202
+ self.fill_size = 0
203
+ end
204
+
205
+ # Closes this object so that further write operations will fail. If
206
+ # _close_delegate_ is +true+, the delegate object used as a data sink will
207
+ # also be closed using its close method.
208
+ def close(close_delegate = true)
209
+ super()
210
+ io.close if close_delegate
211
+ end
212
+
213
+ private
214
+
215
+ # Extend the inherited initialize_keys method to further initialize the
216
+ # keys by encrypting and writing a 12 byte header to the delegate IO
217
+ # object.
218
+ def initialize_keys
219
+ super
220
+
221
+ # Decrypt the 12 byte header.
222
+ unbuffered_read(12)
223
+ nil
224
+ end
225
+
226
+ # Reads, decrypts, and returns at most _length_ bytes from the delegate IO
227
+ # object.
228
+ #
229
+ # Raises EOFError if there is no data to read.
230
+ def unbuffered_read(length)
231
+ buffer = io.read(length)
232
+ raise EOFError, 'end of file reached' if buffer.nil?
233
+
234
+ (0 ... buffer.size).each do |i|
235
+ buffer[i] = (buffer[i] ^ decrypt_byte)
236
+ update_keys(buffer[i].chr)
237
+ end
238
+ buffer
239
+ end
240
+
241
+ # Allows resetting this object and the delegate object back to the
242
+ # beginning of the stream. _offset_ must be <tt>0</tt> and _whence_ must
243
+ # be IO::SEEK_SET or an error will be raised. The delegate object must
244
+ # respond to the _rewind_ method or an error will be raised.
245
+ def unbuffered_seek(offset, whence = IO::SEEK_SET)
246
+ unless offset == 0 && whence == IO::SEEK_SET then
247
+ raise Errno::EINVAL, 'Invalid argument'
248
+ end
249
+ unless io.respond_to?(:rewind) then
250
+ raise Errno::ESPIPE, 'Illegal seek'
251
+ end
252
+ io.rewind
253
+ initialize_keys
254
+ 0
255
+ end
256
+ end
257
+
258
+ # The last modified time of the entry to be processed. Set this before
259
+ # calling #encryptor or #decryptor.
260
+ attr_accessor :mtime
261
+
262
+ # This method signature is part of the interface contract expected by
263
+ # Archive::Zip::Entry for encryption codec objects.
264
+ #
265
+ # A convenience method for creating an
266
+ # Archive::Zip::Codec::TraditionalEncryption::Encrypt object using that
267
+ # class' open method.
268
+ def encryptor(io, password, &b)
269
+ Encrypt.open(io, password, mtime, &b)
270
+ end
271
+
272
+ # This method signature is part of the interface contract expected by
273
+ # Archive::Zip::Entry for encryption codec objects.
274
+ #
275
+ # A convenience method for creating an
276
+ # Archive::Zip::Codec::TraditionalEncryption::Decrypt object using that
277
+ # class' open method.
278
+ def decryptor(io, password, &b)
279
+ Decrypt.open(io, password, mtime, &b)
280
+ end
281
+
282
+ # This method signature is part of the interface contract expected by
283
+ # Archive::Zip::Entry for encryption codec objects.
284
+ #
285
+ # Returns an integer which indicates the version of the official ZIP
286
+ # specification which introduced support for this encryption codec.
287
+ def version_needed_to_extract
288
+ 0x0014
289
+ end
290
+
291
+ # This method signature is part of the interface contract expected by
292
+ # Archive::Zip::Entry for encryption codec objects.
293
+ #
294
+ # Returns an integer representing the general purpose flags of a ZIP archive
295
+ # entry using this encryption codec.
296
+ def general_purpose_flags
297
+ 0b0000000000000001
298
+ end
299
+
300
+ # This method signature is part of the interface contract expected by
301
+ # Archive::Zip::Entry for encryption codec objects.
302
+ #
303
+ # Returns the size of the encryption header in bytes.
304
+ def header_size
305
+ 12
306
+ end
307
+ end
308
+ end; end; end