zip_tricks 4.1.0 → 4.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|