zip_tricks 4.2.0 → 4.2.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: 7a529e0fc4dae54675a7ba880475ec6860ae6c4e
4
- data.tar.gz: e5ef83a503a377bfa504a9daeeb415367c9c680e
3
+ metadata.gz: 99c656baa62bbe09d48e090b2971d63ab3bb875a
4
+ data.tar.gz: d6b84eb8c621b7039ae205f9e3d9ff5d29887032
5
5
  SHA512:
6
- metadata.gz: 46d2a64d61873cf3b32889ef99408fab97aeb25eecc7cd76690f40c328c7bdcec9aa6f9d981726a775b79d1746709a35817156221aaf339dc7cda885e58796bc
7
- data.tar.gz: 458b3a982c1e533d321b2fffebd086125aeb29e0b5e687cfc187e340847200e946ce7944ee237298dae1c1702ef8e3a971ceb53b439e7156f12ae9c7ff995842
6
+ metadata.gz: fa0dfe2f578443c695be43c3e170b81bc24fdc836401e3b8e50f41379a7583bd425fffbb1ed1d618624988c63e45fd0732f234d57abb249ccea3ef4d20c863d9
7
+ data.tar.gz: 30a98f251dd26cde99a62f1cf93ff889aceb30f3ae3eebbba3516b01da3fd56d920e8816d5f742b4c0b17d731b6d8be1f235b4d5bc595b0dc3ecd3960e6b1618
@@ -1,6 +1,7 @@
1
1
  rvm:
2
2
  - 2.1
3
3
  - 2.2
4
+ - 2.3.0
4
5
  - jruby-9.0
5
6
  sudo: false
6
7
  cache: bundler
data/Gemfile CHANGED
@@ -1,7 +1,7 @@
1
1
  source "http://rubygems.org"
2
2
 
3
3
  group :development do
4
- gem 'rubyzip', '~> 1.1', '>= 1.1.7'
4
+ gem 'rubyzip', '~> 1.1'
5
5
  gem 'terminal-table'
6
6
  gem 'range_utils'
7
7
  gem 'rack', '~> 1.6' # For Jeweler
data/Rakefile CHANGED
@@ -12,6 +12,9 @@ end
12
12
  require 'rake'
13
13
  require_relative 'lib/zip_tricks'
14
14
  require 'jeweler'
15
+
16
+
17
+
15
18
  Jeweler::Tasks.new do |gem|
16
19
  # gem is a Gem::Specification... see http://guides.rubygems.org/specification-reference/ for more options
17
20
  gem.name = "zip_tricks"
@@ -1,5 +1,5 @@
1
1
  module ZipTricks
2
- VERSION = '4.2.0'
2
+ VERSION = '4.2.1'
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__ }
@@ -37,15 +37,21 @@
37
37
  #
38
38
  # ZipTricks::Streamer.open(socket) do | zip |
39
39
  # zip.add_stored_entry(filename: "myfile1.bin", size: 9090821, crc32: 12485)
40
- # zip.simulate_write(tempfile1.size)
41
40
  # socket.sendfile(tempfile1)
41
+ # zip.simulate_write(tempfile1.size)
42
+ #
42
43
  # zip.add_stored_entry(filename: "myfile2.bin", size: 458678, crc32: 89568)
43
- # zip.simulate_write(tempfile2.size)
44
44
  # socket.sendfile(tempfile2)
45
+ # zip.simulate_write(tempfile2.size)
45
46
  # end
46
47
  #
47
- # Note that you need to use `simulate_write` to let the
48
- # The central directory will be written automatically at the end of the block.
48
+ # Note that you need to use `simulate_write` in this case. This needs to happen since Streamer
49
+ # writes absolute offsets into the ZIP (local file header offsets and the like),
50
+ # and it relies on the output object to tell it how many bytes have been written
51
+ # so far. When using `sendfile` the Ruby write methods get bypassed entirely, and the
52
+ # offsets in the IO will not be updated - which will result in an invalid ZIP.
53
+ #
54
+ # The central directory will be written automatically at the end of the `open` block.
49
55
  class ZipTricks::Streamer
50
56
  require_relative 'streamer/deflated_writer'
51
57
  require_relative 'streamer/writable'
@@ -58,8 +64,6 @@ class ZipTricks::Streamer
58
64
  EntryBodySizeMismatch = Class.new(StandardError)
59
65
  InvalidOutput = Class.new(ArgumentError)
60
66
  Overflow = Class.new(StandardError)
61
- PathError = Class.new(StandardError)
62
- DuplicateFilenames = Class.new(StandardError)
63
67
  UnknownMode = Class.new(StandardError)
64
68
 
65
69
  private_constant :DeflatedWriter, :StoredWriter, :STORED, :DEFLATED
@@ -86,7 +90,7 @@ class ZipTricks::Streamer
86
90
  unless stream.respond_to?(:tell) && stream.respond_to?(:advance_position_by)
87
91
  stream = ZipTricks::WriteAndTell.new(stream)
88
92
  end
89
-
93
+
90
94
  @out = stream
91
95
  @files = []
92
96
  @local_header_offsets = []
@@ -208,7 +212,7 @@ class ZipTricks::Streamer
208
212
  def close
209
213
  # Record the central directory offset, so that it can be written into the EOCD record
210
214
  cdir_starts_at = @out.tell
211
-
215
+
212
216
  # Write out the central directory entries, one for each file
213
217
  @files.each_with_index do |entry, i|
214
218
  header_loc = @local_header_offsets.fetch(i)
@@ -217,7 +221,7 @@ class ZipTricks::Streamer
217
221
  compressed_size: entry.compressed_size, uncompressed_size: entry.uncompressed_size,
218
222
  mtime: entry.mtime, crc32: entry.crc32, filename: entry.filename) #, external_attrs: DEFAULT_EXTERNAL_ATTRS)
219
223
  end
220
-
224
+
221
225
  # Record the central directory size, for the EOCDR
222
226
  cdir_size = @out.tell - cdir_starts_at
223
227
 
@@ -226,7 +230,7 @@ class ZipTricks::Streamer
226
230
  central_directory_size: cdir_size, num_files_in_archive: @files.length)
227
231
  @out.tell
228
232
  end
229
-
233
+
230
234
  # Sets up the ZipWriter with wrappers if necessary. The method is called once, when the Streamer
231
235
  # gets instantiated - the Writer then gets reused. This method is primarily there so that you
232
236
  # can override it.
@@ -235,22 +239,18 @@ class ZipTricks::Streamer
235
239
  def create_writer
236
240
  ZipTricks::ZipWriter.new
237
241
  end
238
-
242
+
239
243
  private
240
-
244
+
241
245
  def add_file_and_write_local_header(filename:, crc32:, storage_mode:, compressed_size:,
242
246
  uncompressed_size:, use_data_descriptor: false)
243
- if @files.any?{|e| e.filename == filename }
244
- raise DuplicateFilenames, "Filename #{filename.inspect} already used in the archive"
245
- end
246
-
247
+
248
+ # Clean backslashes and uniqify filenames if there are duplicates
249
+ filename = remove_backslash(filename)
250
+ filename = uniquify_name(filename) if @files.any? { |e| e.filename == filename }
251
+
247
252
  raise UnknownMode, "Unknown compression mode #{storage_mode}" unless [STORED, DEFLATED].include?(storage_mode)
248
253
  raise Overflow, "Filename is too long" if filename.bytesize > 0xFFFF
249
- raise PathError, "Paths in ZIP may only contain forward slashes (UNIX separators)" if filename.include?('\\')
250
-
251
- @check_compressed_size_after_leaving_body = !use_data_descriptor
252
- @bytes_written_for_entry = 0
253
- @expected_bytes_for_entry = compressed_size
254
254
 
255
255
  e = Entry.new(filename, crc32, compressed_size, uncompressed_size, storage_mode, mtime=Time.now.utc, use_data_descriptor)
256
256
  @files << e
@@ -258,9 +258,37 @@ class ZipTricks::Streamer
258
258
  @writer.write_local_file_header(io: @out, gp_flags: e.gp_flags, crc32: e.crc32, compressed_size: e.compressed_size,
259
259
  uncompressed_size: e.uncompressed_size, mtime: e.mtime, filename: e.filename, storage_mode: e.storage_mode)
260
260
  end
261
-
261
+
262
262
  def write_data_descriptor_for_last_entry
263
263
  e = @files.fetch(-1)
264
264
  @writer.write_data_descriptor(io: @out, crc32: 0, compressed_size: e.compressed_size, uncompressed_size: e.uncompressed_size)
265
265
  end
266
+
267
+ def remove_backslash(filename)
268
+ filename.tr('\\', '_')
269
+ end
270
+
271
+ def uniquify_name(filename)
272
+ files = Set.new(@files.map(&:filename))
273
+ copy_pattern = /\((\d+)\)$/ # we add (1), (2), (n) at the end of a filename if there is a duplicate
274
+ parts = filename.split(".")
275
+ ext = if parts.last =~ /gz|zip/ && parts.size > 2
276
+ parts.pop(2)
277
+ elsif parts.size > 1
278
+ parts.pop
279
+ end
280
+ fn_last_part = parts.pop
281
+
282
+ duplicate_counter = 1
283
+ loop do
284
+ if fn_last_part =~ copy_pattern
285
+ fn_last_part.sub!(copy_pattern, "(#{duplicate_counter})")
286
+ else
287
+ fn_last_part = "#{fn_last_part} (#{duplicate_counter})"
288
+ end
289
+ new_filename = (parts + [fn_last_part, ext]).compact.join(".")
290
+ return new_filename unless files.include?(new_filename)
291
+ duplicate_counter += 1
292
+ end
293
+ end
266
294
  end
@@ -24,21 +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
28
  it 'allows the writer to be injectable' do
29
29
  fake_writer = double('ZipWriter')
30
30
  expect(fake_writer).to receive(:write_local_file_header)
31
31
  expect(fake_writer).to receive(:write_data_descriptor)
32
32
  expect(fake_writer).to receive(:write_central_directory_file_header)
33
33
  expect(fake_writer).to receive(:write_end_of_central_directory)
34
-
34
+
35
35
  described_class.open('', writer: fake_writer) do |zip|
36
36
  zip.write_deflated_file('stored.txt') do |sink|
37
37
  sink << File.read(__dir__ + '/war-and-peace.txt')
38
38
  end
39
39
  end
40
40
  end
41
-
41
+
42
42
  it 'returns the position in the IO at every call' do
43
43
  io = StringIO.new
44
44
  zip = described_class.new(io)
@@ -135,7 +135,7 @@ describe ZipTricks::Streamer do
135
135
  outbuf.flush
136
136
  File.unlink('test.zip') rescue nil
137
137
  File.rename(outbuf.path, 'osx-archive-test.zip')
138
-
138
+
139
139
  # Mark this test as skipped if the system does not have the binary
140
140
  open_zip_with_archive_utility('osx-archive-test.zip', skip_if_missing: true)
141
141
  end
@@ -226,7 +226,7 @@ describe ZipTricks::Streamer do
226
226
  expect(second_entry.name).to eq("второй-файл.bin".force_encoding(Encoding::BINARY))
227
227
  end
228
228
  end
229
-
229
+
230
230
  it 'creates an archive with data descriptors that can be opened by Rubyzip, with a small number of very tiny text files' do
231
231
  tf = ManagedTempfile.new('zip')
232
232
  z = described_class.open(tf) do |zip|
@@ -238,14 +238,14 @@ describe ZipTricks::Streamer do
238
238
  end
239
239
  end
240
240
  tf.flush
241
-
241
+
242
242
  pending 'https://github.com/rubyzip/rubyzip/issues/295'
243
-
243
+
244
244
  Zip::File.foreach(tf.path) do |entry|
245
245
  # Make sure it is tagged as UNIX
246
246
  expect(entry.fstype).to eq(3)
247
247
 
248
- # The CRC
248
+ # The CRC
249
249
  expect(entry.crc).to eq(Zlib.crc32(File.read(__dir__ + '/war-and-peace.txt')))
250
250
 
251
251
  # Check the name
@@ -253,7 +253,7 @@ describe ZipTricks::Streamer do
253
253
 
254
254
  # Check the right external attributes (non-executable on UNIX)
255
255
  expect(entry.external_file_attributes).to eq(2175008768)
256
-
256
+
257
257
  # Check the file contents
258
258
  readback = entry.get_input_stream.read
259
259
  readback.force_encoding(Encoding::BINARY)
@@ -265,7 +265,7 @@ describe ZipTricks::Streamer do
265
265
 
266
266
  it 'can create a valid ZIP archive without any files' do
267
267
  tf = ManagedTempfile.new('zip')
268
-
268
+
269
269
  described_class.open(tf) do |zip|
270
270
  end
271
271
 
@@ -276,4 +276,21 @@ describe ZipTricks::Streamer do
276
276
  Zip::File.foreach(tf.path, &b)
277
277
  }.not_to yield_control
278
278
  end
279
+
280
+ it 'prevents duplicates in the stored files' do
281
+ files = ["README", "README", "file.one\\two.jpg", "file_one.jpg", "file_one (1).jpg",
282
+ "file\\one.jpg", "My.Super.file.txt.zip", "My.Super.file.txt.zip"]
283
+ fake_writer = double('Writer').as_null_object
284
+ seen_filenames = []
285
+ allow(fake_writer).to receive(:write_local_file_header) {|filename:, **others|
286
+ seen_filenames << filename
287
+ }
288
+ zip_streamer = described_class.new(StringIO.new, writer: fake_writer)
289
+ files.each do |fn|
290
+ zip_streamer.add_stored_entry(filename: fn, size: 1024, crc32: 0xCC)
291
+ end
292
+ expect(seen_filenames).to eq(["README", "README (1)", "file.one_two.jpg", "file_one.jpg",
293
+ "file_one (1).jpg", "file_one (2).jpg", "My.Super.file.txt.zip",
294
+ "My.Super.file (1).txt.zip"])
295
+ end
279
296
  end
@@ -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.2.0 ruby lib
5
+ # stub: zip_tricks 4.2.1 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "zip_tricks"
9
- s.version = "4.2.0"
9
+ s.version = "4.2.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-09-17"
14
+ s.date = "2016-10-10"
15
15
  s.description = "Stream out ZIP files from Ruby"
16
16
  s.email = "me@julik.nl"
17
17
  s.extra_rdoc_files = [
@@ -70,14 +70,14 @@ Gem::Specification.new do |s|
70
70
  ]
71
71
  s.homepage = "http://github.com/wetransfer/zip_tricks"
72
72
  s.licenses = ["MIT"]
73
- s.rubygems_version = "2.5.1"
73
+ s.rubygems_version = "2.4.5.1"
74
74
  s.summary = "Stream out ZIP files from Ruby"
75
75
 
76
76
  if s.respond_to? :specification_version then
77
77
  s.specification_version = 4
78
78
 
79
79
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
80
- s.add_development_dependency(%q<rubyzip>, [">= 1.1.7", "~> 1.1"])
80
+ s.add_development_dependency(%q<rubyzip>, ["~> 1.1"])
81
81
  s.add_development_dependency(%q<terminal-table>, [">= 0"])
82
82
  s.add_development_dependency(%q<range_utils>, [">= 0"])
83
83
  s.add_development_dependency(%q<rack>, ["~> 1.6"])
@@ -88,7 +88,7 @@ Gem::Specification.new do |s|
88
88
  s.add_development_dependency(%q<bundler>, ["~> 1.0"])
89
89
  s.add_development_dependency(%q<jeweler>, ["~> 2.0.1"])
90
90
  else
91
- s.add_dependency(%q<rubyzip>, [">= 1.1.7", "~> 1.1"])
91
+ s.add_dependency(%q<rubyzip>, ["~> 1.1"])
92
92
  s.add_dependency(%q<terminal-table>, [">= 0"])
93
93
  s.add_dependency(%q<range_utils>, [">= 0"])
94
94
  s.add_dependency(%q<rack>, ["~> 1.6"])
@@ -100,7 +100,7 @@ Gem::Specification.new do |s|
100
100
  s.add_dependency(%q<jeweler>, ["~> 2.0.1"])
101
101
  end
102
102
  else
103
- s.add_dependency(%q<rubyzip>, [">= 1.1.7", "~> 1.1"])
103
+ s.add_dependency(%q<rubyzip>, ["~> 1.1"])
104
104
  s.add_dependency(%q<terminal-table>, [">= 0"])
105
105
  s.add_dependency(%q<range_utils>, [">= 0"])
106
106
  s.add_dependency(%q<rack>, ["~> 1.6"])
metadata CHANGED
@@ -1,22 +1,19 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zip_tricks
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.2.0
4
+ version: 4.2.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-09-17 00:00:00.000000000 Z
11
+ date: 2016-10-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubyzip
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: 1.1.7
20
17
  - - "~>"
21
18
  - !ruby/object:Gem::Version
22
19
  version: '1.1'
@@ -24,9 +21,6 @@ dependencies:
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
26
23
  requirements:
27
- - - ">="
28
- - !ruby/object:Gem::Version
29
- version: 1.1.7
30
24
  - - "~>"
31
25
  - !ruby/object:Gem::Version
32
26
  version: '1.1'
@@ -238,7 +232,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
238
232
  version: '0'
239
233
  requirements: []
240
234
  rubyforge_project:
241
- rubygems_version: 2.5.1
235
+ rubygems_version: 2.4.5.1
242
236
  signing_key:
243
237
  specification_version: 4
244
238
  summary: Stream out ZIP files from Ruby