zip_tricks 3.1.0 → 3.1.1
Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3434ce881bfe7fdf3494ed494f8e251082d94510
|
4
|
+
data.tar.gz: 30062193918dacf4a018d7bde923303f0a14c47f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 88be7f1bebd8e0faa8906203b594e022b173390039732a3eaafa53ec903770b0a619f7a0895a8203b75c6c09f5908ca400d86611f443e34fdead53554edd8e14
|
7
|
+
data.tar.gz: 5c4362394b3666ce528e16c657b019a8185875555af2202a8c976df523c309017ac060ec61082617b3573722cb1d2e4eb002502ef9c8b770227859756e0afe5f
|
@@ -188,26 +188,36 @@ class ZipTricks::FileReader
|
|
188
188
|
# Parse an IO handle to a ZIP archive into an array of Entry objects.
|
189
189
|
#
|
190
190
|
# @param io[#tell, #seek, #read, #size] an IO-ish object
|
191
|
+
# @param read_local_headers[Boolean] whether to proceed to read the local headers in addition to the central directory
|
191
192
|
# @return [Array<Entry>] an array of entries within the ZIP being parsed
|
192
|
-
def read_zip_structure(io)
|
193
|
+
def read_zip_structure(io, read_local_headers: true)
|
193
194
|
zip_file_size = io.size
|
194
195
|
eocd_offset = get_eocd_offset(io, zip_file_size)
|
195
196
|
|
196
|
-
zip64_end_of_cdir_location =
|
197
|
+
zip64_end_of_cdir_location = get_zip64_eocd_location(io, eocd_offset)
|
197
198
|
num_files, cdir_location, cdir_size = if zip64_end_of_cdir_location
|
198
199
|
num_files_and_central_directory_offset_zip64(io, zip64_end_of_cdir_location)
|
199
200
|
else
|
200
201
|
num_files_and_central_directory_offset(io, eocd_offset)
|
201
202
|
end
|
203
|
+
log { 'Located the central directory start at %d' % cdir_location }
|
202
204
|
seek(io, cdir_location)
|
203
205
|
|
204
206
|
# Read the entire central directory in one fell swoop
|
205
207
|
central_directory_str = read_n(io, cdir_size)
|
206
208
|
central_directory_io = StringIO.new(central_directory_str)
|
209
|
+
log { 'Read %d bytes with central directory entries' % cdir_size }
|
207
210
|
|
208
|
-
entries = (
|
209
|
-
|
210
|
-
|
211
|
+
entries = (0...num_files).map do |entry_n|
|
212
|
+
log { 'Reading the central directory entry %d starting at offset %d' % [entry_n, cdir_location + central_directory_io.tell] }
|
213
|
+
read_cdir_entry(central_directory_io)
|
214
|
+
end
|
215
|
+
|
216
|
+
entries.each_with_index do |entry, i|
|
217
|
+
if read_local_headers
|
218
|
+
log { 'Reading the local header for entry %d at offset %d' % [i, entry.local_file_header_offset] }
|
219
|
+
entry.compressed_data_offset = find_compressed_data_start_offset(io, entry.local_file_header_offset)
|
220
|
+
end
|
211
221
|
end
|
212
222
|
end
|
213
223
|
|
@@ -349,9 +359,20 @@ class ZipTricks::FileReader
|
|
349
359
|
# ...of which we really only need the Zip64 extra
|
350
360
|
if zip64_extra_contents = extra_table[1] # Zip64 extra
|
351
361
|
zip64_extra = StringIO.new(zip64_extra_contents)
|
352
|
-
e.
|
353
|
-
|
354
|
-
|
362
|
+
log { 'Will read Zip64 extra data for %s, %d bytes' % [e.filename, zip64_extra.size] }
|
363
|
+
# Now here be dragons. The APPNOTE specifies that
|
364
|
+
#
|
365
|
+
# > The order of the fields in the ZIP64 extended
|
366
|
+
# > information record is fixed, but the fields will
|
367
|
+
# > only appear if the corresponding Local or Central
|
368
|
+
# > directory record field is set to 0xFFFF or 0xFFFFFFFF.
|
369
|
+
#
|
370
|
+
# It means that before we read this stuff we need to check if the previously-read
|
371
|
+
# values are at overflow, and only _then_ proceed to read them. Bah.
|
372
|
+
e.uncompressed_size = read_8b(zip64_extra) if e.uncompressed_size == 0xFFFFFFFF
|
373
|
+
e.compressed_size = read_8b(zip64_extra) if e.compressed_size == 0xFFFFFFFF
|
374
|
+
e.local_file_header_offset = read_8b(zip64_extra) if e.local_file_header_offset == 0xFFFFFFFF
|
375
|
+
# Disk number comes last and we can skip it anyway, since we do not support multi-disk archives
|
355
376
|
end
|
356
377
|
end
|
357
378
|
end
|
@@ -369,8 +390,11 @@ class ZipTricks::FileReader
|
|
369
390
|
eocd_idx_in_buf = locate_eocd_signature(str_containing_eocd_record)
|
370
391
|
|
371
392
|
raise "Could not find the EOCD signature in the buffer - maybe a malformed ZIP file" unless eocd_idx_in_buf
|
372
|
-
|
373
|
-
implied_position_of_eocd_record + eocd_idx_in_buf
|
393
|
+
|
394
|
+
eocd_offset = implied_position_of_eocd_record + eocd_idx_in_buf
|
395
|
+
log { 'Found EOCD signature at offset %d' % eocd_offset }
|
396
|
+
|
397
|
+
eocd_offset
|
374
398
|
end
|
375
399
|
|
376
400
|
# This is tricky. Essentially, we have to scan the maximum possible number of bytes (that the EOCD can
|
@@ -394,7 +418,6 @@ class ZipTricks::FileReader
|
|
394
418
|
|
395
419
|
window_location = in_str.bytesize + end_location
|
396
420
|
unpacked = window.unpack(unpack_pattern)
|
397
|
-
|
398
421
|
# If we found the signarue, pick up the comment size, and check if the size of the window
|
399
422
|
# plus that comment size is where we are in the string. If we are - bingo.
|
400
423
|
if unpacked[0] == 0x06054b50 && comment_size = unpacked[-1]
|
@@ -409,18 +432,23 @@ class ZipTricks::FileReader
|
|
409
432
|
|
410
433
|
# Find the Zip64 EOCD locator segment offset. Do this by seeking backwards from the
|
411
434
|
# EOCD record in the archive by fixed offsets
|
412
|
-
def
|
435
|
+
def get_zip64_eocd_location(file_io, eocd_offset)
|
413
436
|
zip64_eocd_loc_offset = eocd_offset
|
414
437
|
zip64_eocd_loc_offset -= 4 # The signature
|
415
438
|
zip64_eocd_loc_offset -= 4 # Which disk has the Zip64 end of central directory record
|
416
439
|
zip64_eocd_loc_offset -= 8 # Offset of the zip64 central directory record
|
417
440
|
zip64_eocd_loc_offset -= 4 # Total number of disks
|
418
441
|
|
442
|
+
log { 'Will look for the Zip64 EOCD locator signature at offset %d' % zip64_eocd_loc_offset }
|
443
|
+
|
419
444
|
# If the offset is negative there is certainly no Zip64 EOCD locator here
|
420
445
|
return unless zip64_eocd_loc_offset >= 0
|
421
446
|
|
422
447
|
file_io.seek(zip64_eocd_loc_offset, IO::SEEK_SET)
|
423
448
|
assert_signature(file_io, 0x07064b50)
|
449
|
+
|
450
|
+
log { 'Found Zip64 EOCD locator at offset %d' % zip64_eocd_loc_offset }
|
451
|
+
|
424
452
|
disk_num = read_4b(file_io) # number of the disk
|
425
453
|
raise UnsupportedFeature, "The archive spans multiple disks" if disk_num != 0
|
426
454
|
read_8b(file_io)
|
@@ -445,9 +473,10 @@ class ZipTricks::FileReader
|
|
445
473
|
|
446
474
|
num_files_this_disk = read_8b(zip64_eocdr) # number of files on this disk
|
447
475
|
num_files_total = read_8b(zip64_eocdr) # files total in the central directory
|
448
|
-
|
449
476
|
raise UnsupportedFeature, "The archive spans multiple disks" if num_files_this_disk != num_files_total
|
450
477
|
|
478
|
+
log { 'Zip64 EOCD record states there are %d files in the archive' % num_files_total }
|
479
|
+
|
451
480
|
central_dir_size = read_8b(zip64_eocdr) # Size of the central directory
|
452
481
|
central_dir_offset = read_8b(zip64_eocdr) # Where the central directory starts
|
453
482
|
|
@@ -519,4 +548,12 @@ class ZipTricks::FileReader
|
|
519
548
|
|
520
549
|
private_constant :C_V, :C_v, :C_Qe, :MAX_END_OF_CENTRAL_DIRECTORY_RECORD_SIZE,
|
521
550
|
:MAX_LOCAL_HEADER_SIZE, :SIZE_OF_USABLE_EOCD_RECORD
|
551
|
+
|
552
|
+
# Is provided as a stub to be overridden in a subclass if you need it. Will report
|
553
|
+
# during various stages of reading. The log message is contained in the return value
|
554
|
+
# of `yield` in the method (the log messages are lazy-evaluated).
|
555
|
+
def log
|
556
|
+
# The most minimal implementation for the method is just this:
|
557
|
+
# $stderr.puts(yield)
|
558
|
+
end
|
522
559
|
end
|
data/lib/zip_tricks.rb
CHANGED
@@ -66,4 +66,17 @@ describe ZipTricks::FileReader do
|
|
66
66
|
entries = described_class.read_zip_structure(z)
|
67
67
|
expect(entries.length).to eq(1)
|
68
68
|
end
|
69
|
+
|
70
|
+
it 'can handle a Zip64 central directory fields that only contains the required fields (substitutes for standard fields)' do
|
71
|
+
# In this example central directory, 2 entries contain Zip64 extra where only the local header offset is set (8 bytes each)
|
72
|
+
# This is the exceptional case where we have to poke at a private method directly
|
73
|
+
File.open(__dir__ + '/cdir_entry_with_partial_use_of_zip64_extra_fields.bin', 'rb') do |f|
|
74
|
+
reader = described_class.new
|
75
|
+
entry = reader.send(:read_cdir_entry, f)
|
76
|
+
expect(entry.local_file_header_offset).to eq(4312401349)
|
77
|
+
expect(entry.filename).to eq('Motorhead - Ace Of Spades.srt')
|
78
|
+
expect(entry.compressed_size).to eq(69121)
|
79
|
+
expect(entry.uncompressed_size).to eq(69121)
|
80
|
+
end
|
81
|
+
end
|
69
82
|
end
|
data/zip_tricks.gemspec
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: zip_tricks 3.1.
|
5
|
+
# stub: zip_tricks 3.1.1 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "zip_tricks"
|
9
|
-
s.version = "3.1.
|
9
|
+
s.version = "3.1.1"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib"]
|
13
13
|
s.authors = ["Julik Tarkhanov"]
|
14
|
-
s.date = "2016-08-
|
14
|
+
s.date = "2016-08-17"
|
15
15
|
s.description = "Makes rubyzip stream, for real"
|
16
16
|
s.email = "me@julik.nl"
|
17
17
|
s.extra_rdoc_files = [
|
@@ -52,6 +52,7 @@ Gem::Specification.new do |s|
|
|
52
52
|
"spec/spec_helper.rb",
|
53
53
|
"spec/zip_tricks/block_deflate_spec.rb",
|
54
54
|
"spec/zip_tricks/block_write_spec.rb",
|
55
|
+
"spec/zip_tricks/cdir_entry_with_partial_use_of_zip64_extra_fields.bin",
|
55
56
|
"spec/zip_tricks/file_reader_spec.rb",
|
56
57
|
"spec/zip_tricks/rack_body_spec.rb",
|
57
58
|
"spec/zip_tricks/remote_io_spec.rb",
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zip_tricks
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.1.
|
4
|
+
version: 3.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Julik Tarkhanov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-08-
|
11
|
+
date: 2016-08-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rubyzip
|
@@ -203,6 +203,7 @@ files:
|
|
203
203
|
- spec/spec_helper.rb
|
204
204
|
- spec/zip_tricks/block_deflate_spec.rb
|
205
205
|
- spec/zip_tricks/block_write_spec.rb
|
206
|
+
- spec/zip_tricks/cdir_entry_with_partial_use_of_zip64_extra_fields.bin
|
206
207
|
- spec/zip_tricks/file_reader_spec.rb
|
207
208
|
- spec/zip_tricks/rack_body_spec.rb
|
208
209
|
- spec/zip_tricks/remote_io_spec.rb
|