archive-zip 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,7 +1,9 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
1
3
|
module Archive; class Zip
|
2
|
-
# Archive::Zip::
|
3
|
-
#
|
4
|
-
#
|
4
|
+
# Archive::Zip::DataDescriptor is a convenience class which bundles important
|
5
|
+
# information concerning the compressed data in a ZIP archive entry and allows
|
6
|
+
# easy comparisons between instances of itself.
|
5
7
|
class DataDescriptor
|
6
8
|
# Create a new instance of this class where <em>crc32</em>,
|
7
9
|
# _compressed_size_, and _uncompressed_size_ are all integers representing a
|
@@ -41,7 +43,7 @@ module Archive; class Zip
|
|
41
43
|
end
|
42
44
|
|
43
45
|
# Writes the data wrapped in this object to _io_ which must be a writable,
|
44
|
-
# IO-like object
|
46
|
+
# IO-like object providing a _write_ method. Returns the number of bytes
|
45
47
|
# written.
|
46
48
|
def dump(io)
|
47
49
|
io.write(
|
data/lib/archive/zip/entry.rb
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'archive/support/ioextensions'
|
4
|
+
require 'archive/support/binary_stringio'
|
1
5
|
require 'archive/zip/codec/deflate'
|
2
6
|
require 'archive/zip/codec/null_encryption'
|
3
7
|
require 'archive/zip/codec/store'
|
@@ -197,33 +201,26 @@ module Archive; class Zip
|
|
197
201
|
end
|
198
202
|
|
199
203
|
# Creates and returns a new entry object by parsing from the current
|
200
|
-
# position of _io_. _io_ must be a readable, IO-like object which
|
201
|
-
#
|
202
|
-
#
|
204
|
+
# position of _io_. _io_ must be a readable, IO-like object which is
|
205
|
+
# positioned at the start of a central file record following the signature
|
206
|
+
# for that record.
|
203
207
|
#
|
204
|
-
# <b>NOTE:</b> For now _io_ MUST be seekable
|
205
|
-
# +true+ from its <i>seekable?</i> method. See IO#seekable?.
|
208
|
+
# <b>NOTE:</b> For now _io_ MUST be seekable.
|
206
209
|
#
|
207
210
|
# Currently, the only entry objects returned are instances of
|
208
211
|
# Archive::Zip::Entry::File, Archive::Zip::Entry::Directory, and
|
209
212
|
# Archive::Zip::Entry::Symlink. Any other kind of entry will be mapped into
|
210
213
|
# an instance of Archive::Zip::Entry::File.
|
211
214
|
#
|
212
|
-
# Raises Archive::Zip::
|
213
|
-
#
|
214
|
-
# entry.
|
215
|
+
# Raises Archive::Zip::EntryError for any other errors related to processing
|
216
|
+
# the entry.
|
215
217
|
def self.parse(io)
|
216
|
-
# Error out if the IO object is not confirmed seekable.
|
217
|
-
unless io.respond_to?(:seekable?) and io.seekable? then
|
218
|
-
raise Zip::IOError, 'non-seekable IO object given'
|
219
|
-
end
|
220
|
-
|
221
218
|
# Parse the central file record and then use the information found there
|
222
219
|
# to locate and parse the corresponding local file record.
|
223
220
|
cfr = parse_central_file_record(io)
|
224
221
|
next_record_position = io.pos
|
225
222
|
io.seek(cfr.local_header_position)
|
226
|
-
unless
|
223
|
+
unless IOExtensions.read_exactly(io, 4) == LFH_SIGNATURE then
|
227
224
|
raise Zip::EntryError, 'bad local file header signature'
|
228
225
|
end
|
229
226
|
lfr = parse_local_file_record(io, cfr.compressed_size)
|
@@ -275,8 +272,6 @@ module Archive; class Zip
|
|
275
272
|
|
276
273
|
# Set the expected data descriptor so that extraction can be verified.
|
277
274
|
entry.expected_data_descriptor = expected_data_descriptor
|
278
|
-
# Record the raw file data for the entry.
|
279
|
-
entry.raw_data = IOWindow.new(io, io.pos, cfr.compressed_size)
|
280
275
|
# Record the compression codec.
|
281
276
|
entry.compression_codec = compression_codec
|
282
277
|
# Record the encryption codec.
|
@@ -301,9 +296,9 @@ module Archive; class Zip
|
|
301
296
|
private
|
302
297
|
|
303
298
|
# Parses a central file record and returns a CFHRecord instance containing
|
304
|
-
# the parsed data. _io_ must be a readable, IO-like object which
|
305
|
-
#
|
306
|
-
#
|
299
|
+
# the parsed data. _io_ must be a readable, IO-like object which is
|
300
|
+
# positioned at the start of a central file record following the signature
|
301
|
+
# for that record.
|
307
302
|
def self.parse_central_file_record(io)
|
308
303
|
cfr = CFHRecord.new
|
309
304
|
|
@@ -321,24 +316,27 @@ module Archive; class Zip
|
|
321
316
|
cfr.disk_number_start,
|
322
317
|
cfr.internal_file_attributes,
|
323
318
|
cfr.external_file_attributes,
|
324
|
-
cfr.local_header_position =
|
319
|
+
cfr.local_header_position =
|
320
|
+
IOExtensions.read_exactly(io, 42).unpack('vvvvVVVVvvvvvVV')
|
325
321
|
|
326
|
-
cfr.zip_path =
|
327
|
-
cfr.extra_fields =
|
328
|
-
|
322
|
+
cfr.zip_path = IOExtensions.read_exactly(io, file_name_length)
|
323
|
+
cfr.extra_fields = parse_central_extra_fields(
|
324
|
+
IOExtensions.read_exactly(io, extra_fields_length)
|
325
|
+
)
|
326
|
+
cfr.comment = IOExtensions.read_exactly(io, comment_length)
|
329
327
|
|
330
328
|
# Convert from MSDOS time to Unix time.
|
331
329
|
cfr.mtime = DOSTime.new(dos_mtime).to_time
|
332
330
|
|
333
331
|
cfr
|
334
|
-
rescue EOFError
|
332
|
+
rescue EOFError
|
335
333
|
raise Zip::EntryError, 'unexpected end of file'
|
336
334
|
end
|
337
335
|
|
338
336
|
# Parses a local file record and returns a LFHRecord instance containing the
|
339
|
-
# parsed data. _io_ must be a readable, IO-like object which
|
340
|
-
#
|
341
|
-
# record
|
337
|
+
# parsed data. _io_ must be a readable, IO-like object which is positioned
|
338
|
+
# at the start of a local file record following the signature for that
|
339
|
+
# record.
|
342
340
|
#
|
343
341
|
# If the record to be parsed is flagged to have a trailing data descriptor
|
344
342
|
# record, _expected_compressed_size_ must be set to an integer counting the
|
@@ -356,10 +354,13 @@ module Archive; class Zip
|
|
356
354
|
lfr.compressed_size,
|
357
355
|
lfr.uncompressed_size,
|
358
356
|
file_name_length,
|
359
|
-
extra_fields_length =
|
357
|
+
extra_fields_length =
|
358
|
+
IOExtensions.read_exactly(io, 26).unpack('vvvVVVVvv')
|
360
359
|
|
361
|
-
lfr.zip_path =
|
362
|
-
lfr.extra_fields =
|
360
|
+
lfr.zip_path = IOExtensions.read_exactly(io, file_name_length)
|
361
|
+
lfr.extra_fields = parse_local_extra_fields(
|
362
|
+
IOExtensions.read_exactly(io, extra_fields_length)
|
363
|
+
)
|
363
364
|
|
364
365
|
# Convert from MSDOS time to Unix time.
|
365
366
|
lfr.mtime = DOSTime.new(dos_mtime).to_time
|
@@ -371,35 +372,60 @@ module Archive; class Zip
|
|
371
372
|
# libraries create trailing data descriptor records with a preceding
|
372
373
|
# signature while others do not.
|
373
374
|
# This handles both cases.
|
374
|
-
possible_signature =
|
375
|
+
possible_signature = IOExtensions.read_exactly(io, 4)
|
375
376
|
if possible_signature == DD_SIGNATURE then
|
376
377
|
lfr.crc32,
|
377
378
|
lfr.compressed_size,
|
378
|
-
lfr.uncompressed_size =
|
379
|
+
lfr.uncompressed_size =
|
380
|
+
IOExtensions.read_exactly(io, 12).unpack('VVV')
|
379
381
|
else
|
380
382
|
lfr.crc32 = possible_signature.unpack('V')[0]
|
381
383
|
lfr.compressed_size,
|
382
|
-
lfr.uncompressed_size =
|
384
|
+
lfr.uncompressed_size = IOExtensions.read_exactly(io, 8).unpack('VV')
|
383
385
|
end
|
384
386
|
io.pos = saved_pos
|
385
387
|
end
|
386
388
|
|
387
389
|
lfr
|
388
|
-
rescue EOFError
|
390
|
+
rescue EOFError
|
389
391
|
raise Zip::EntryError, 'unexpected end of file'
|
390
392
|
end
|
391
393
|
|
392
|
-
# Parses the extra fields for
|
393
|
-
#
|
394
|
-
#
|
395
|
-
def self.
|
396
|
-
|
394
|
+
# Parses the extra fields for central file records and returns an array of
|
395
|
+
# extra field objects. _bytes_ must be a String containing all of the extra
|
396
|
+
# field data to be parsed.
|
397
|
+
def self.parse_central_extra_fields(bytes)
|
398
|
+
BinaryStringIO.open(bytes) do |io|
|
397
399
|
extra_fields = []
|
398
400
|
while ! io.eof? do
|
399
|
-
|
400
|
-
|
401
|
+
begin
|
402
|
+
header_id, data_size = IOExtensions.read_exactly(io, 4).unpack('vv')
|
403
|
+
data = IOExtensions.read_exactly(io, data_size)
|
404
|
+
rescue ::EOFError
|
405
|
+
raise EntryError, 'insufficient data available'
|
406
|
+
end
|
407
|
+
|
408
|
+
extra_fields << ExtraField.parse_central(header_id, data)
|
409
|
+
end
|
410
|
+
extra_fields
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
# Parses the extra fields for local file records and returns an array of
|
415
|
+
# extra field objects. _bytes_ must be a String containing all of the extra
|
416
|
+
# field data to be parsed.
|
417
|
+
def self.parse_local_extra_fields(bytes)
|
418
|
+
BinaryStringIO.open(bytes) do |io|
|
419
|
+
extra_fields = []
|
420
|
+
while ! io.eof? do
|
421
|
+
begin
|
422
|
+
header_id, data_size = IOExtensions.read_exactly(io, 4).unpack('vv')
|
423
|
+
data = IOExtensions.read_exactly(io, data_size)
|
424
|
+
rescue ::EOFError
|
425
|
+
raise EntryError, 'insufficient data available'
|
426
|
+
end
|
401
427
|
|
402
|
-
extra_fields << ExtraField.
|
428
|
+
extra_fields << ExtraField.parse_local(header_id, data)
|
403
429
|
end
|
404
430
|
extra_fields
|
405
431
|
end
|
@@ -474,10 +500,10 @@ module Archive; class Zip
|
|
474
500
|
attr_accessor :mode
|
475
501
|
# The comment associated with this entry.
|
476
502
|
attr_accessor :comment
|
477
|
-
# An Archive::Zip::
|
478
|
-
#
|
479
|
-
#
|
480
|
-
#
|
503
|
+
# An Archive::Zip::DataDescriptor instance which should contain the expected
|
504
|
+
# CRC32 checksum, compressed size, and uncompressed size for the file data.
|
505
|
+
# When not +nil+, this is used by #extract to confirm that the data
|
506
|
+
# extraction was successful.
|
481
507
|
attr_accessor :expected_data_descriptor
|
482
508
|
# The selected compression codec.
|
483
509
|
attr_accessor :compression_codec
|
@@ -521,24 +547,37 @@ module Archive; class Zip
|
|
521
547
|
false
|
522
548
|
end
|
523
549
|
|
524
|
-
# Adds _extra_field_ as an extra field specification to
|
525
|
-
#
|
550
|
+
# Adds _extra_field_ as an extra field specification to *both* the central
|
551
|
+
# file record and the local file record of this entry.
|
552
|
+
#
|
553
|
+
# If _extra_field_ is an instance of
|
526
554
|
# Archive::Zip::Entry::ExtraField::ExtendedTimestamp, the values of that
|
527
555
|
# field are used to set mtime and atime for this entry. If _extra_field_ is
|
528
556
|
# an instance of Archive::Zip::Entry::ExtraField::Unix, the values of that
|
529
557
|
# field are used to set mtime, atime, uid, and gid for this entry.
|
530
558
|
def add_extra_field(extra_field)
|
531
|
-
|
532
|
-
|
559
|
+
# Try to find an extra field with the same header ID already in the list
|
560
|
+
# and merge the new one with that if one exists; otherwise, add the new
|
561
|
+
# one to the list.
|
562
|
+
existing_extra_field = @extra_fields.find do |ef|
|
563
|
+
ef.header_id == extra_field.header_id
|
564
|
+
end
|
565
|
+
if existing_extra_field.nil? then
|
566
|
+
@extra_fields << extra_field
|
567
|
+
else
|
568
|
+
extra_field = existing_extra_field.merge(extra_field)
|
569
|
+
end
|
533
570
|
|
571
|
+
# Set some attributes of this entry based on the settings in select types
|
572
|
+
# of extra fields.
|
534
573
|
if extra_field.kind_of?(ExtraField::ExtendedTimestamp) then
|
535
|
-
self.mtime = extra_field.mtime
|
536
|
-
self.atime = extra_field.atime
|
574
|
+
self.mtime = extra_field.mtime unless extra_field.mtime.nil?
|
575
|
+
self.atime = extra_field.atime unless extra_field.atime.nil?
|
537
576
|
elsif extra_field.kind_of?(ExtraField::Unix) then
|
538
|
-
self.mtime = extra_field.mtime
|
539
|
-
self.atime = extra_field.atime
|
540
|
-
self.uid = extra_field.uid
|
541
|
-
self.gid = extra_field.gid
|
577
|
+
self.mtime = extra_field.mtime unless extra_field.mtime.nil?
|
578
|
+
self.atime = extra_field.atime unless extra_field.atime.nil?
|
579
|
+
self.uid = extra_field.uid unless extra_field.uid.nil?
|
580
|
+
self.gid = extra_field.gid unless extra_field.uid.nil?
|
542
581
|
end
|
543
582
|
self
|
544
583
|
end
|
@@ -554,21 +593,34 @@ module Archive; class Zip
|
|
554
593
|
@local_file_record_position = local_file_record_position
|
555
594
|
bytes_written = 0
|
556
595
|
|
596
|
+
# Assume that no trailing data descriptor will be necessary.
|
597
|
+
need_trailing_data_descriptor = false
|
598
|
+
begin
|
599
|
+
io.pos
|
600
|
+
rescue Errno::ESPIPE
|
601
|
+
# A trailing data descriptor is required for non-seekable IO.
|
602
|
+
need_trailing_data_descriptor = true
|
603
|
+
end
|
604
|
+
if encryption_codec.class == Codec::TraditionalEncryption then
|
605
|
+
# HACK:
|
606
|
+
# According to the ZIP specification, a trailing data descriptor should
|
607
|
+
# only be required when writing to non-seekable IO , but InfoZIP
|
608
|
+
# *always* does this when using traditional encryption even though it
|
609
|
+
# will also write the data descriptor in the usual place if possible.
|
610
|
+
# Failure to emulate InfoZIP in this behavior will prevent InfoZIP
|
611
|
+
# compatibility with traditionally encrypted entries.
|
612
|
+
need_trailing_data_descriptor = true
|
613
|
+
# HACK:
|
614
|
+
# The InfoZIP implementation of traditional encryption requires that the
|
615
|
+
# the last modified file time be used as part of the encryption header.
|
616
|
+
# This is a deviation from the ZIP specification.
|
617
|
+
encryption_codec.mtime = mtime
|
618
|
+
end
|
619
|
+
|
620
|
+
# Set the general purpose flags.
|
557
621
|
general_purpose_flags = compression_codec.general_purpose_flags
|
558
622
|
general_purpose_flags |= encryption_codec.general_purpose_flags
|
559
|
-
|
560
|
-
if ! io.seekable? ||
|
561
|
-
encryption_codec.class == Codec::TraditionalEncryption then
|
562
|
-
# Flag that the data descriptor record will follow the compressed file
|
563
|
-
# data of this entry.
|
564
|
-
#
|
565
|
-
# HACK:
|
566
|
-
# According to the ZIP specification, this should only be done if the IO
|
567
|
-
# object cannot be accessed randomly, but InfoZIP *always* sets this
|
568
|
-
# flag when using traditional encryption even though it will also write
|
569
|
-
# the data descriptor in the usual place if possible. Failure to
|
570
|
-
# emulate InfoZIP in this behavior will prevent InfoZIP compatibility
|
571
|
-
# with traditionally encrypted entries.
|
623
|
+
if need_trailing_data_descriptor then
|
572
624
|
general_purpose_flags |= FLAG_DATA_DESCRIPTOR_FOLLOWS
|
573
625
|
end
|
574
626
|
|
@@ -579,7 +631,9 @@ module Archive; class Zip
|
|
579
631
|
version_needed_to_extract = encryption_codec.version_needed_to_extract
|
580
632
|
end
|
581
633
|
|
634
|
+
# Write the data.
|
582
635
|
bytes_written += io.write(LFH_SIGNATURE)
|
636
|
+
extra_field_data = local_extra_field_data
|
583
637
|
bytes_written += io.write(
|
584
638
|
[
|
585
639
|
version_needed_to_extract,
|
@@ -596,15 +650,6 @@ module Archive; class Zip
|
|
596
650
|
bytes_written += io.write(zip_path)
|
597
651
|
bytes_written += io.write(extra_field_data)
|
598
652
|
|
599
|
-
if encryption_codec.class == Codec::TraditionalEncryption then
|
600
|
-
# HACK:
|
601
|
-
# Traditional encryption requires that the last 2 bytes of the header it
|
602
|
-
# uses are the 2 low order bytes of the last modified file time in DOS
|
603
|
-
# format. This is different than stated in the ZIP specification and
|
604
|
-
# rather comes from the InfoZIP implementation.
|
605
|
-
encryption_codec.mtime = mtime
|
606
|
-
end
|
607
|
-
|
608
653
|
# Pipeline a compressor into an encryptor, write all the file data to the
|
609
654
|
# compressor, and get a data descriptor from it.
|
610
655
|
encryption_codec.encryptor(io, password) do |e|
|
@@ -619,31 +664,23 @@ module Archive; class Zip
|
|
619
664
|
end
|
620
665
|
e.close(false)
|
621
666
|
end
|
622
|
-
|
623
667
|
bytes_written += @data_descriptor.compressed_size
|
624
|
-
|
668
|
+
|
669
|
+
# Write the trailing data descriptor if necessary.
|
670
|
+
if need_trailing_data_descriptor then
|
671
|
+
bytes_written += io.write(DD_SIGNATURE)
|
672
|
+
bytes_written += @data_descriptor.dump(io)
|
673
|
+
end
|
674
|
+
|
675
|
+
begin
|
625
676
|
# Update the data descriptor located before the compressed data for the
|
626
677
|
# entry.
|
627
678
|
saved_position = io.pos
|
628
679
|
io.pos = @local_file_record_position + 14
|
629
680
|
@data_descriptor.dump(io)
|
630
681
|
io.pos = saved_position
|
631
|
-
|
632
|
-
|
633
|
-
if ! io.seekable? ||
|
634
|
-
encryption_codec.class == Codec::TraditionalEncryption then
|
635
|
-
# Write the data descriptor after the compressed file data for the
|
636
|
-
# entry.
|
637
|
-
#
|
638
|
-
# HACK:
|
639
|
-
# According to the ZIP specification, this should only be done if the IO
|
640
|
-
# object cannot be accessed randomly, but InfoZIP *always* does this
|
641
|
-
# when using traditional encryption even though it will also write the
|
642
|
-
# data descriptor in the usual place if possible. Failure to emulate
|
643
|
-
# InfoZIP in this behavior will prevent InfoZIP compatibility with
|
644
|
-
# traditionally encrypted entries.
|
645
|
-
bytes_written += io.write(DD_SIGNATURE)
|
646
|
-
bytes_written += @data_descriptor.dump(io)
|
682
|
+
rescue Errno::ESPIPE
|
683
|
+
# Ignore a failed attempt to update the data descriptor.
|
647
684
|
end
|
648
685
|
|
649
686
|
bytes_written
|
@@ -657,21 +694,29 @@ module Archive; class Zip
|
|
657
694
|
def dump_central_file_record(io)
|
658
695
|
bytes_written = 0
|
659
696
|
|
697
|
+
# Assume that no trailing data descriptor will be necessary.
|
698
|
+
need_trailing_data_descriptor = false
|
699
|
+
begin
|
700
|
+
io.pos
|
701
|
+
rescue Errno::ESPIPE
|
702
|
+
# A trailing data descriptor is required for non-seekable IO.
|
703
|
+
need_trailing_data_descriptor = true
|
704
|
+
end
|
705
|
+
if encryption_codec.class == Codec::TraditionalEncryption then
|
706
|
+
# HACK:
|
707
|
+
# According to the ZIP specification, a trailing data descriptor should
|
708
|
+
# only be required when writing to non-seekable IO , but InfoZIP
|
709
|
+
# *always* does this when using traditional encryption even though it
|
710
|
+
# will also write the data descriptor in the usual place if possible.
|
711
|
+
# Failure to emulate InfoZIP in this behavior will prevent InfoZIP
|
712
|
+
# compatibility with traditionally encrypted entries.
|
713
|
+
need_trailing_data_descriptor = true
|
714
|
+
end
|
715
|
+
|
716
|
+
# Set the general purpose flags.
|
660
717
|
general_purpose_flags = compression_codec.general_purpose_flags
|
661
718
|
general_purpose_flags |= encryption_codec.general_purpose_flags
|
662
|
-
|
663
|
-
if ! io.seekable? ||
|
664
|
-
encryption_codec.class == Codec::TraditionalEncryption then
|
665
|
-
# Flag that the data descriptor record will follow the compressed file
|
666
|
-
# data of this entry.
|
667
|
-
#
|
668
|
-
# HACK:
|
669
|
-
# According to the ZIP specification, this should only be done if the IO
|
670
|
-
# object cannot be accessed randomly, but InfoZIP *always* sets this
|
671
|
-
# flag when using traditional encryption even though it will also write
|
672
|
-
# the data descriptor in the usual place if possible. Failure to
|
673
|
-
# emulate InfoZIP in this behavior will prevent InfoZIP compatibility
|
674
|
-
# with encrypted entries.
|
719
|
+
if need_trailing_data_descriptor then
|
675
720
|
general_purpose_flags |= FLAG_DATA_DESCRIPTOR_FOLLOWS
|
676
721
|
end
|
677
722
|
|
@@ -682,6 +727,7 @@ module Archive; class Zip
|
|
682
727
|
version_needed_to_extract = encryption_codec.version_needed_to_extract
|
683
728
|
end
|
684
729
|
|
730
|
+
# Write the data.
|
685
731
|
bytes_written += io.write(CFH_SIGNATURE)
|
686
732
|
bytes_written += io.write(
|
687
733
|
[
|
@@ -693,6 +739,7 @@ module Archive; class Zip
|
|
693
739
|
].pack('vvvvV')
|
694
740
|
)
|
695
741
|
bytes_written += @data_descriptor.dump(io)
|
742
|
+
extra_field_data = central_extra_field_data
|
696
743
|
bytes_written += io.write(
|
697
744
|
[
|
698
745
|
zip_path.length,
|
@@ -717,19 +764,31 @@ module Archive; class Zip
|
|
717
764
|
0x0314
|
718
765
|
end
|
719
766
|
|
720
|
-
def
|
721
|
-
|
767
|
+
def central_extra_field_data
|
768
|
+
@central_extra_field_data = @extra_fields.collect do |extra_field|
|
769
|
+
extra_field.dump_central
|
770
|
+
end.join
|
771
|
+
end
|
722
772
|
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
ExtraField::Unix.new(
|
773
|
+
def dummy
|
774
|
+
# Add fields for time data if available.
|
775
|
+
unless mtime.nil? && atime.nil? then
|
776
|
+
@central_extra_field_data +=
|
777
|
+
ExtraField::ExtendedTimestamp.new(mtime, atime, nil).dump_central
|
778
|
+
end
|
779
|
+
|
780
|
+
# Add fields for user and group ownerships if available.
|
781
|
+
unless uid.nil? || gid.nil? || mtime.nil? || atime.nil? then
|
782
|
+
@central_extra_field_data += ExtraField::Unix.new(
|
783
|
+
mtime, atime, uid, gid
|
784
|
+
).dump_central
|
785
|
+
end
|
786
|
+
end
|
787
|
+
|
788
|
+
def local_extra_field_data
|
789
|
+
@local_extra_field_data = @extra_fields.collect do |extra_field|
|
790
|
+
extra_field.dump_local
|
791
|
+
end.join
|
733
792
|
end
|
734
793
|
|
735
794
|
def internal_file_attributes
|
@@ -1012,7 +1071,7 @@ module Archive; class Zip; module Entry
|
|
1012
1071
|
)
|
1013
1072
|
else
|
1014
1073
|
if @file_path.nil? then
|
1015
|
-
simulated_raw_data =
|
1074
|
+
simulated_raw_data = BinaryStringIO.new
|
1016
1075
|
else
|
1017
1076
|
simulated_raw_data = ::File.new(@file_path, 'rb')
|
1018
1077
|
end
|
@@ -1023,22 +1082,15 @@ module Archive; class Zip; module Entry
|
|
1023
1082
|
@file_data
|
1024
1083
|
end
|
1025
1084
|
|
1026
|
-
# Sets the +file_data+ attribute of this object to _file_data_.
|
1027
|
-
#
|
1028
|
-
# otherwise, _file_data_ must be a readable, IO-like object. _file_data_ is
|
1029
|
-
# then wrapped inside an Archive::Zip::Codec::Store::Unstore instance before
|
1030
|
-
# finally setting the +file_data+ attribute.
|
1085
|
+
# Sets the +file_data+ attribute of this object to _file_data_. _file_data_
|
1086
|
+
# must be a readable, IO-like object.
|
1031
1087
|
#
|
1032
1088
|
# <b>NOTE:</b> As a side effect, the +file_path+ and +raw_data+ attributes
|
1033
1089
|
# for this object will be set to +nil+.
|
1034
1090
|
def file_data=(file_data)
|
1035
1091
|
@file_path = nil
|
1036
1092
|
self.raw_data = nil
|
1037
|
-
|
1038
|
-
@file_data = StringIO.new(file_data)
|
1039
|
-
else
|
1040
|
-
@file_data = file_data
|
1041
|
-
end
|
1093
|
+
@file_data = file_data
|
1042
1094
|
# Ensure that the IO-like object can return CRC32 and data size
|
1043
1095
|
# information so that it's possible to verify extraction later if desired.
|
1044
1096
|
unless @file_data.respond_to?(:data_descriptor) then
|