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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f6d3a4df27461f1235e526d7d40eb089f4df5c64
4
- data.tar.gz: 179df69ea7d164f9164aabcc0c19d951e8d5748f
3
+ metadata.gz: 7a529e0fc4dae54675a7ba880475ec6860ae6c4e
4
+ data.tar.gz: e5ef83a503a377bfa504a9daeeb415367c9c680e
5
5
  SHA512:
6
- metadata.gz: 68131f0f180074731223f145f06d42f65d7ee327bfcc73c7445a9ae2be198ba8ed2372cffb9deba8729c7b3af46eb4cacdeb3ae408fd37a202c848933ce93ffa
7
- data.tar.gz: 9e38d0ce079b7e367b75db3b6ebd032fef11802f12926cf586f061bdb7f059c1087505b2556cfd76062e288601923d51e4c7a1ca5ca5bb928fcae385ee92807b
6
+ metadata.gz: 46d2a64d61873cf3b32889ef99408fab97aeb25eecc7cd76690f40c328c7bdcec9aa6f9d981726a775b79d1746709a35817156221aaf339dc7cda885e58796bc
7
+ data.tar.gz: 458b3a982c1e533d321b2fffebd086125aeb29e0b5e687cfc187e340847200e946ce7944ee237298dae1c1702ef8e3a971ceb53b439e7156f12ae9c7ff995842
@@ -1,5 +1,5 @@
1
1
  module ZipTricks
2
- VERSION = '4.1.0'
2
+ VERSION = '4.2.0'
3
3
 
4
4
  # Require all the sub-components except myself
5
5
  Dir.glob(__dir__ + '/**/*.rb').sort.each {|p| require p unless p == __FILE__ }
@@ -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
- central_directory_str = read_n(io, cdir_size)
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 entries' % cdir_size }
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] }
@@ -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 [IO] the destination IO for the ZIP (should respond to `<<`)
82
- def initialize(stream)
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 = create_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
- describe 'with a file without EOCD' do
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
- describe 'with an end-to-end ZIP file to read' do
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)
@@ -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.1.0 ruby lib
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.1.0"
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"
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.1.0
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-14 00:00:00.000000000 Z
11
+ date: 2016-09-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubyzip