zip_tricks 4.1.0 → 4.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.
- checksums.yaml +4 -4
- data/lib/zip_tricks.rb +1 -1
- data/lib/zip_tricks/file_reader.rb +15 -3
- data/lib/zip_tricks/streamer.rb +7 -5
- data/spec/zip_tricks/file_reader_spec.rb +42 -3
- data/spec/zip_tricks/streamer_spec.rb +15 -1
- data/zip_tricks.gemspec +3 -3
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7a529e0fc4dae54675a7ba880475ec6860ae6c4e
|
4
|
+
data.tar.gz: e5ef83a503a377bfa504a9daeeb415367c9c680e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 46d2a64d61873cf3b32889ef99408fab97aeb25eecc7cd76690f40c328c7bdcec9aa6f9d981726a775b79d1746709a35817156221aaf339dc7cda885e58796bc
|
7
|
+
data.tar.gz: 458b3a982c1e533d321b2fffebd086125aeb29e0b5e687cfc187e340847200e946ce7944ee237298dae1c1702ef8e3a971ceb53b439e7156f12ae9c7ff995842
|
data/lib/zip_tricks.rb
CHANGED
@@ -199,10 +199,22 @@ class ZipTricks::FileReader
|
|
199
199
|
log { 'Located the central directory start at %d' % cdir_location }
|
200
200
|
seek(io, cdir_location)
|
201
201
|
|
202
|
-
# Read the entire central directory in one fell swoop
|
203
|
-
|
202
|
+
# Read the entire central directory AND anything behind it, in one fell swoop.
|
203
|
+
# Strictly speaking, we should be able to read `cdir_size` bytes and not a byte more.
|
204
|
+
# However, we know for a fact that in some of our files the central directory size
|
205
|
+
# is in fact misreported. `zipinfo` then says:
|
206
|
+
#
|
207
|
+
# warning [ktsglobal-2b03bc.zip]: 1 extra byte at beginning or within zipfile
|
208
|
+
# (attempting to process anyway)
|
209
|
+
# error [ktsglobal-2b03bc.zip]: reported length of central directory is
|
210
|
+
# -1 bytes too long (Atari STZip zipfile? J.H.Holm ZIPSPLIT 1.1
|
211
|
+
# zipfile?). Compensating...
|
212
|
+
#
|
213
|
+
# Since the EOCD is not that big anyway, we just read the entire "tail" of the ZIP ignoring
|
214
|
+
# the central directory size alltogether.
|
215
|
+
central_directory_str = io.read # and not read_n(io, cdir_size), see above
|
204
216
|
central_directory_io = StringIO.new(central_directory_str)
|
205
|
-
log { 'Read %d bytes with central directory
|
217
|
+
log { 'Read %d bytes with central directory + EOCD record and locator' % central_directory_str.bytesize }
|
206
218
|
|
207
219
|
entries = (0...num_files).map do |entry_n|
|
208
220
|
log { 'Reading the central directory entry %d starting at offset %d' % [entry_n, cdir_location + central_directory_io.tell] }
|
data/lib/zip_tricks/streamer.rb
CHANGED
@@ -70,16 +70,18 @@ class ZipTricks::Streamer
|
|
70
70
|
#
|
71
71
|
# @param stream [IO] the destination IO for the ZIP (should respond to `tell` and `<<`)
|
72
72
|
# @yield [Streamer] the streamer that can be written to
|
73
|
-
def self.open(stream)
|
74
|
-
archive = new(stream)
|
73
|
+
def self.open(stream, **kwargs_for_new)
|
74
|
+
archive = new(stream, **kwargs_for_new)
|
75
75
|
yield(archive)
|
76
76
|
archive.close
|
77
77
|
end
|
78
78
|
|
79
79
|
# Creates a new Streamer on top of the given IO-ish object.
|
80
80
|
#
|
81
|
-
# @param stream
|
82
|
-
|
81
|
+
# @param stream[IO] the destination IO for the ZIP (should respond to `<<`)
|
82
|
+
# @param writer[ZipTricks::ZipWriter] the object to be used as the writer.
|
83
|
+
# Defaults to an instance of ZipTricks::ZipWriter, normally you won't need to override it
|
84
|
+
def initialize(stream, writer: create_writer)
|
83
85
|
raise InvalidOutput, "The stream must respond to #<<" unless stream.respond_to?(:<<)
|
84
86
|
unless stream.respond_to?(:tell) && stream.respond_to?(:advance_position_by)
|
85
87
|
stream = ZipTricks::WriteAndTell.new(stream)
|
@@ -88,7 +90,7 @@ class ZipTricks::Streamer
|
|
88
90
|
@out = stream
|
89
91
|
@files = []
|
90
92
|
@local_header_offsets = []
|
91
|
-
@writer =
|
93
|
+
@writer = writer
|
92
94
|
end
|
93
95
|
|
94
96
|
# Writes a part of a zip entry body (actual binary data of the entry) into the output stream.
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
describe ZipTricks::FileReader do
|
3
3
|
|
4
|
-
|
4
|
+
context 'with a file without EOCD' do
|
5
5
|
it 'raises the MissingEOCD exception and refuses to read' do
|
6
6
|
f = StringIO.new
|
7
7
|
10.times { f << ('A' * 1024 ) }
|
@@ -72,8 +72,47 @@ describe ZipTricks::FileReader do
|
|
72
72
|
}.to raise_error(described_class::UnsupportedFeature)
|
73
73
|
end
|
74
74
|
end
|
75
|
-
|
76
|
-
|
75
|
+
|
76
|
+
context 'with a ZIP file where the size of the central directory is recorded incorrectly, and has a wrong value' do
|
77
|
+
it 'is still able to read the entries' do
|
78
|
+
# Purposefully create a ZIP that we will damage after
|
79
|
+
zipfile = StringIO.new
|
80
|
+
tolstoy = File.read(__dir__ + '/war-and-peace.txt')
|
81
|
+
tolstoy.force_encoding(Encoding::BINARY)
|
82
|
+
|
83
|
+
# Make a one-off Writer that corrupts the size of the central directory and says there is more data
|
84
|
+
# in it than there actually is
|
85
|
+
class EvilWriter < ZipTricks::ZipWriter
|
86
|
+
def write_end_of_central_directory(**kwargs)
|
87
|
+
kwargs[:central_directory_size] = kwargs[:central_directory_size] + 64 # Pretend there has to be more data
|
88
|
+
super(**kwargs)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
ZipTricks::Streamer.open(zipfile, writer: EvilWriter.new) do |zip|
|
93
|
+
zip.write_deflated_file('text-1.txt') { |sink| sink << tolstoy }
|
94
|
+
zip.write_deflated_file('text-2.txt') { |sink| sink << tolstoy }
|
95
|
+
end
|
96
|
+
|
97
|
+
# Find the start of the EOCD record (signature, then the information about disk numbers and the information
|
98
|
+
# about the file counts
|
99
|
+
eocd_start_marker = [0x06054b50, 0, 0, 2, 2].pack('Vvvvv')
|
100
|
+
eocd_offset = zipfile.string.index(eocd_start_marker)
|
101
|
+
expect(eocd_offset).not_to be_nil
|
102
|
+
|
103
|
+
# Seek to the offset where the central directory size is going to be (just after the string we looked for)
|
104
|
+
zipfile.seek(eocd_offset + eocd_start_marker.bytesize)
|
105
|
+
# and overwrite it.
|
106
|
+
zipfile.write([118].pack('V'))
|
107
|
+
zipfile.rewind
|
108
|
+
|
109
|
+
entries = ZipTricks::FileReader.read_zip_structure(io: zipfile)
|
110
|
+
|
111
|
+
expect(entries.length).to eq(2)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
context 'with an end-to-end ZIP file to read' do
|
77
116
|
it 'reads and uncompresses the file written deflated with data descriptors' do
|
78
117
|
zipfile = StringIO.new
|
79
118
|
tolstoy = File.read(__dir__ + '/war-and-peace.txt')
|
@@ -24,7 +24,21 @@ describe ZipTricks::Streamer do
|
|
24
24
|
described_class.new(nil)
|
25
25
|
}.to raise_error(ZipTricks::Streamer::InvalidOutput)
|
26
26
|
end
|
27
|
-
|
27
|
+
|
28
|
+
it 'allows the writer to be injectable' do
|
29
|
+
fake_writer = double('ZipWriter')
|
30
|
+
expect(fake_writer).to receive(:write_local_file_header)
|
31
|
+
expect(fake_writer).to receive(:write_data_descriptor)
|
32
|
+
expect(fake_writer).to receive(:write_central_directory_file_header)
|
33
|
+
expect(fake_writer).to receive(:write_end_of_central_directory)
|
34
|
+
|
35
|
+
described_class.open('', writer: fake_writer) do |zip|
|
36
|
+
zip.write_deflated_file('stored.txt') do |sink|
|
37
|
+
sink << File.read(__dir__ + '/war-and-peace.txt')
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
28
42
|
it 'returns the position in the IO at every call' do
|
29
43
|
io = StringIO.new
|
30
44
|
zip = described_class.new(io)
|
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 4.
|
5
|
+
# stub: zip_tricks 4.2.0 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "zip_tricks"
|
9
|
-
s.version = "4.
|
9
|
+
s.version = "4.2.0"
|
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-09-
|
14
|
+
s.date = "2016-09-17"
|
15
15
|
s.description = "Stream out ZIP files from Ruby"
|
16
16
|
s.email = "me@julik.nl"
|
17
17
|
s.extra_rdoc_files = [
|
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: 4.
|
4
|
+
version: 4.2.0
|
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-09-
|
11
|
+
date: 2016-09-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rubyzip
|