rubyzip 1.1.6 → 1.1.7

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rubyzip might be problematic. Click here for more details.

Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +66 -13
  3. data/lib/zip.rb +5 -1
  4. data/lib/zip/central_directory.rb +1 -1
  5. data/lib/zip/crypto/encryption.rb +11 -0
  6. data/lib/zip/crypto/null_encryption.rb +45 -0
  7. data/lib/zip/crypto/traditional_encryption.rb +99 -0
  8. data/lib/zip/deflater.rb +6 -3
  9. data/lib/zip/entry.rb +11 -5
  10. data/lib/zip/entry_set.rb +5 -6
  11. data/lib/zip/extra_field.rb +1 -0
  12. data/lib/zip/extra_field/ntfs.rb +92 -0
  13. data/lib/zip/file.rb +11 -1
  14. data/lib/zip/filesystem.rb +4 -0
  15. data/lib/zip/inflater.rb +4 -3
  16. data/lib/zip/input_stream.rb +10 -4
  17. data/lib/zip/output_stream.rb +12 -7
  18. data/lib/zip/version.rb +1 -1
  19. data/test/basic_zip_file_test.rb +1 -1
  20. data/test/central_directory_entry_test.rb +1 -1
  21. data/test/central_directory_test.rb +5 -1
  22. data/test/crypto/null_encryption_test.rb +53 -0
  23. data/test/crypto/traditional_encryption_test.rb +80 -0
  24. data/test/data/WarnInvalidDate.zip +0 -0
  25. data/test/data/ntfs.zip +0 -0
  26. data/test/data/zipWithEncryption.zip +0 -0
  27. data/test/deflater_test.rb +14 -9
  28. data/test/encryption_test.rb +42 -0
  29. data/test/entry_set_test.rb +14 -1
  30. data/test/entry_test.rb +2 -2
  31. data/test/errors_test.rb +1 -1
  32. data/test/extra_field_test.rb +10 -1
  33. data/test/file_extract_directory_test.rb +3 -2
  34. data/test/file_extract_test.rb +2 -2
  35. data/test/file_split_test.rb +2 -2
  36. data/test/file_test.rb +16 -13
  37. data/test/filesystem/dir_iterator_test.rb +1 -1
  38. data/test/filesystem/directory_test.rb +1 -1
  39. data/test/filesystem/file_mutating_test.rb +1 -1
  40. data/test/filesystem/file_nonmutating_test.rb +10 -1
  41. data/test/filesystem/file_stat_test.rb +1 -1
  42. data/test/inflater_test.rb +1 -1
  43. data/test/input_stream_test.rb +1 -1
  44. data/test/ioextras/abstract_input_stream_test.rb +1 -1
  45. data/test/ioextras/abstract_output_stream_test.rb +1 -1
  46. data/test/ioextras/fake_io_test.rb +1 -1
  47. data/test/local_entry_test.rb +14 -11
  48. data/test/output_stream_test.rb +18 -3
  49. data/test/pass_thru_compressor_test.rb +2 -2
  50. data/test/pass_thru_decompressor_test.rb +1 -1
  51. data/test/settings_test.rb +23 -2
  52. data/test/test_helper.rb +1 -1
  53. data/test/unicode_file_names_and_comments_test.rb +16 -4
  54. data/test/zip64_full_test.rb +5 -1
  55. data/test/zip64_support_test.rb +1 -1
  56. metadata +104 -6
  57. data/test/dummy.txt +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f1b8a6d42ff819b19645f78ce2afb3c9fc7f6e36
4
- data.tar.gz: 7319bb5eb51c46fdd78fffb43ead4452f2e3d947
3
+ metadata.gz: 21b11d72a619a00f577f3ac15a65d309d07ab57b
4
+ data.tar.gz: d2c28c4157e692118e26478c34205094eb2ca055
5
5
  SHA512:
6
- metadata.gz: 5e59f7e789b618c7e7746efac11152c2272ced20cb63977c045bbbf3dd75ce4fe8bfe1b7da75d490bd7a9bbcddfa1ec93f2924c5f6e7ebcdf94c45ddd23e7042
7
- data.tar.gz: 97fbb6781a474c5ba51b586033f0da491044687e32f40fe5dcec253305ff55e0ab49d9c22a5732616eaa8203c1e5b768b9d661a77ec0692fa298b89a6598974c
6
+ metadata.gz: 935e9064b3fc98693e8e0eef68f147b9a62ba4dcf25c9f42cf9828a544665d749fd8ff5866b52183a569171fd3aecddb09613d526da1bc43bf34c0b556bcea02
7
+ data.tar.gz: b9e36842ed0830befe3ca556b954757975291f8c523be419213c5974015742e41c4bb4a4b44c88a746ad22f7656f23162cf5e5d5e7591d917927ae9830ad8dae
data/README.md CHANGED
@@ -1,7 +1,8 @@
1
1
  # rubyzip
2
- [![Build Status](https://secure.travis-ci.org/rubyzip/rubyzip.png)](http://travis-ci.org/rubyzip/rubyzip)
3
- [![Code Climate](https://codeclimate.com/github/rubyzip/rubyzip.png)](https://codeclimate.com/github/rubyzip/rubyzip)
4
- [![Coverage Status](https://coveralls.io/repos/rubyzip/rubyzip/badge.png?branch=master)](https://coveralls.io/r/rubyzip/rubyzip?branch=master)
2
+ [![Gem Version](https://badge.fury.io/rb/rubyzip.svg)](http://badge.fury.io/rb/rubyzip)
3
+ [![Build Status](https://secure.travis-ci.org/rubyzip/rubyzip.svg)](http://travis-ci.org/rubyzip/rubyzip)
4
+ [![Code Climate](https://codeclimate.com/github/rubyzip/rubyzip.svg)](https://codeclimate.com/github/rubyzip/rubyzip)
5
+ [![Coverage Status](https://img.shields.io/coveralls/rubyzip/rubyzip.svg)](https://coveralls.io/r/rubyzip/rubyzip?branch=master)
5
6
 
6
7
  rubyzip is a ruby library for reading and writing zip files.
7
8
 
@@ -9,7 +10,7 @@ rubyzip is a ruby library for reading and writing zip files.
9
10
 
10
11
  Rubyzip interface changed!!! No need to do `require "zip/zip"` and `Zip` prefix in class names removed.
11
12
 
12
- If you have issues with any third-party gems what required old version of rubyzip you can use next workaround:
13
+ If you have issues with any third-party gems that require an old version of rubyzip, you can use this workaround:
13
14
 
14
15
  ```ruby
15
16
  gem 'rubyzip', '>= 1.0.0' # will load new rubyzip version
@@ -58,18 +59,52 @@ end
58
59
  ```
59
60
 
60
61
  ### Zipping a directory recursively
62
+ Copy from [here](https://github.com/rubyzip/rubyzip/blob/05916bf89181e1955118fd3ea059f18acac28cc8/samples/example_recursive.rb )
61
63
 
62
64
  ```ruby
63
65
  require 'rubygems'
64
66
  require 'zip'
65
-
66
- directory = '/Users/me/Desktop/directory_to_zip/'
67
- zipfile_name = '/Users/me/Desktop/recursive_directory.zip'
68
-
69
- Zip::File.open(zipfile_name, Zip::File::CREATE) do |zipfile|
70
- Dir[File.join(directory, '**', '**')].each do |file|
71
- zipfile.add(file.sub(directory, ''), file)
72
- end
67
+ # This is a simple example which uses rubyzip to
68
+ # recursively generate a zip file from the contents of
69
+ # a specified directory. The directory itself is not
70
+ # included in the archive, rather just its contents.
71
+ #
72
+ # Usage:
73
+ # require /path/to/the/ZipFileGenerator/Class
74
+ # directoryToZip = "/tmp/input"
75
+ # outputFile = "/tmp/out.zip"
76
+ # zf = ZipFileGenerator.new(directoryToZip, outputFile)
77
+ # zf.write()
78
+
79
+ class ZipFileGenerator
80
+ # Initialize with the directory to zip and the location of the output archive.
81
+ def initialize(inputDir, outputFile)
82
+ @inputDir = inputDir
83
+ @outputFile = outputFile
84
+ end
85
+ # Zip the input directory.
86
+ def write()
87
+ entries = Dir.entries(@inputDir); entries.delete("."); entries.delete("..")
88
+ io = Zip::File.open(@outputFile, Zip::File::CREATE);
89
+ writeEntries(entries, "", io)
90
+ io.close();
91
+ end
92
+ # A helper method to make the recursion work.
93
+ private
94
+ def writeEntries(entries, path, io)
95
+ entries.each { |e|
96
+ zipFilePath = path == "" ? e : File.join(path, e)
97
+ diskFilePath = File.join(@inputDir, zipFilePath)
98
+ puts "Deflating " + diskFilePath
99
+ if File.directory?(diskFilePath)
100
+ io.mkdir(zipFilePath)
101
+ subdir =Dir.entries(diskFilePath); subdir.delete("."); subdir.delete("..")
102
+ writeEntries(subdir, zipFilePath, io)
103
+ else
104
+ io.get_output_stream(zipFilePath) { |f| f.print(File.open(diskFilePath, "rb").read())}
105
+ end
106
+ }
107
+ end
73
108
  end
74
109
  ```
75
110
 
@@ -111,6 +146,19 @@ Zip::File.open('foo.zip') do |zip_file|
111
146
  end
112
147
  ```
113
148
 
149
+ ### Password Protection (Experimental)
150
+
151
+ RubyZip supports reading/writing zip files with traditional zip encryption (a.k.a. "ZipCrypto"). AES encryption is not yet supported. It can be used with buffer streams, e.g.:
152
+
153
+ ```ruby
154
+ Zip::OutputStream.write_buffer(::StringIO.new(''), Zip::TraditionalEncrypter.new('password')) do |out|
155
+ out.put_next_entry("my_file.txt")
156
+ out.write my_data
157
+ end.string
158
+ ```
159
+
160
+ This is an experimental feature and the interface for encryption may change in future versions.
161
+
114
162
  ## Known issues
115
163
 
116
164
  ### Modify docx file with rubyzip
@@ -158,12 +206,17 @@ If you want to store non english names and want to open properly file on Windows
158
206
  Zip.unicode_names = true
159
207
  ```
160
208
 
209
+ In some zip date of files stored in incorrect format. You can hide warning about it by using:
210
+
211
+ ```ruby
212
+ Zip.warn_invalid_date = false
213
+ ```
214
+
161
215
  You can set the default compression level like so:
162
216
 
163
217
  ```ruby
164
218
  Zip.default_compression = Zlib::DEFAULT_COMPRESSION
165
219
  ```
166
-
167
220
  It defaults to `Zlib::DEFAULT_COMPRESSION`. Possible values are `Zlib::BEST_COMPRESSION`, `Zlib::DEFAULT_COMPRESSION` and `Zlib::NO_COMPRESSION`
168
221
 
169
222
  All settings in same time
data/lib/zip.rb CHANGED
@@ -21,6 +21,9 @@ require 'zip/null_compressor'
21
21
  require 'zip/null_input_stream'
22
22
  require 'zip/pass_thru_compressor'
23
23
  require 'zip/pass_thru_decompressor'
24
+ require 'zip/crypto/encryption'
25
+ require 'zip/crypto/null_encryption'
26
+ require 'zip/crypto/traditional_encryption'
24
27
  require 'zip/inflater'
25
28
  require 'zip/deflater'
26
29
  require 'zip/streamable_stream'
@@ -34,7 +37,7 @@ end
34
37
 
35
38
  module Zip
36
39
  extend self
37
- attr_accessor :unicode_names, :on_exists_proc, :continue_on_exists_proc, :sort_entries, :default_compression, :write_zip64_support
40
+ attr_accessor :unicode_names, :on_exists_proc, :continue_on_exists_proc, :sort_entries, :default_compression, :write_zip64_support, :warn_invalid_date
38
41
 
39
42
  def reset!
40
43
  @_ran_once = false
@@ -44,6 +47,7 @@ module Zip
44
47
  @sort_entries = false
45
48
  @default_compression = ::Zlib::DEFAULT_COMPRESSION
46
49
  @write_zip64_support = false
50
+ @warn_invalid_date = true
47
51
  end
48
52
 
49
53
  def setup
@@ -46,7 +46,7 @@ module Zip
46
46
  @entry_set ? [@entry_set.size, 0xFFFF].min : 0,
47
47
  [cdir_size, 0xFFFFFFFF].min,
48
48
  [offset, 0xFFFFFFFF].min,
49
- @comment ? @comment.length : 0
49
+ @comment ? @comment.bytesize : 0
50
50
  ]
51
51
  io << tmp.pack('VvvvvVVv')
52
52
  io << @comment
@@ -0,0 +1,11 @@
1
+ module Zip
2
+ class Encrypter #:nodoc:all
3
+ end
4
+
5
+ class Decrypter
6
+ end
7
+ end
8
+
9
+ # Copyright (C) 2002, 2003 Thomas Sondergaard
10
+ # rubyzip is free software; you can redistribute it and/or
11
+ # modify it under the terms of the ruby license.
@@ -0,0 +1,45 @@
1
+ module Zip
2
+ module NullEncryption
3
+ def header_bytesize
4
+ 0
5
+ end
6
+
7
+ def gp_flags
8
+ 0
9
+ end
10
+ end
11
+
12
+ class NullEncrypter < Encrypter
13
+ include NullEncryption
14
+
15
+ def header(mtime)
16
+ ''
17
+ end
18
+
19
+ def encrypt(data)
20
+ data
21
+ end
22
+
23
+ def data_descriptor(crc32, compressed_size, uncomprssed_size)
24
+ ''
25
+ end
26
+
27
+ def reset!
28
+ end
29
+ end
30
+
31
+ class NullDecrypter < Decrypter
32
+ include NullEncryption
33
+
34
+ def decrypt(data)
35
+ data
36
+ end
37
+
38
+ def reset!(header)
39
+ end
40
+ end
41
+ end
42
+
43
+ # Copyright (C) 2002, 2003 Thomas Sondergaard
44
+ # rubyzip is free software; you can redistribute it and/or
45
+ # modify it under the terms of the ruby license.
@@ -0,0 +1,99 @@
1
+ module Zip
2
+ module TraditionalEncryption
3
+ def initialize(password)
4
+ @password = password
5
+ reset_keys!
6
+ end
7
+
8
+ def header_bytesize
9
+ 12
10
+ end
11
+
12
+ def gp_flags
13
+ 0x0001 | 0x0008
14
+ end
15
+
16
+ protected
17
+
18
+ def reset_keys!
19
+ @key0 = 0x12345678
20
+ @key1 = 0x23456789
21
+ @key2 = 0x34567890
22
+ @password.each_byte do |byte|
23
+ update_keys(byte.chr)
24
+ end
25
+ end
26
+
27
+ def update_keys(n)
28
+ @key0 = ~Zlib.crc32(n, ~@key0)
29
+ @key1 = ((@key1 + (@key0 & 0xff)) * 134775813 + 1) & 0xffffffff
30
+ @key2 = ~Zlib.crc32((@key1 >> 24).chr, ~@key2)
31
+ end
32
+
33
+ def decrypt_byte
34
+ temp = (@key2 & 0xffff) | 2
35
+ ((temp * (temp ^ 1)) >> 8) & 0xff
36
+ end
37
+ end
38
+
39
+ class TraditionalEncrypter < Encrypter
40
+ include TraditionalEncryption
41
+
42
+ def header(mtime)
43
+ [].tap do |header|
44
+ (header_bytesize - 2).times do
45
+ header << Random.rand(0..255)
46
+ end
47
+ header << (mtime.to_binary_dos_time & 0xff)
48
+ header << (mtime.to_binary_dos_time >> 8)
49
+ end.map{|x| encode x}.pack("C*")
50
+ end
51
+
52
+ def encrypt(data)
53
+ data.unpack("C*").map{|x| encode x}.pack("C*")
54
+ end
55
+
56
+ def data_descriptor(crc32, compressed_size, uncomprssed_size)
57
+ [0x08074b50, crc32, compressed_size, uncomprssed_size].pack("VVVV")
58
+ end
59
+
60
+ def reset!
61
+ reset_keys!
62
+ end
63
+
64
+ private
65
+
66
+ def encode(n)
67
+ t = decrypt_byte
68
+ update_keys(n.chr)
69
+ t ^ n
70
+ end
71
+ end
72
+
73
+ class TraditionalDecrypter < Decrypter
74
+ include TraditionalEncryption
75
+
76
+ def decrypt(data)
77
+ data.unpack("C*").map{|x| decode x}.pack("C*")
78
+ end
79
+
80
+ def reset!(header)
81
+ reset_keys!
82
+ header.each_byte do |x|
83
+ decode x
84
+ end
85
+ end
86
+
87
+ private
88
+
89
+ def decode(n)
90
+ n ^= decrypt_byte
91
+ update_keys(n.chr)
92
+ n
93
+ end
94
+ end
95
+ end
96
+
97
+ # Copyright (C) 2002, 2003 Thomas Sondergaard
98
+ # rubyzip is free software; you can redistribute it and/or
99
+ # modify it under the terms of the ruby license.
@@ -1,23 +1,26 @@
1
1
  module Zip
2
2
  class Deflater < Compressor #:nodoc:all
3
3
 
4
- def initialize(output_stream, level = Zip.default_compression)
4
+ def initialize(output_stream, level = Zip.default_compression, encrypter = NullEncrypter.new)
5
5
  super()
6
6
  @output_stream = output_stream
7
7
  @zlib_deflater = ::Zlib::Deflate.new(level, -::Zlib::MAX_WBITS)
8
8
  @size = 0
9
9
  @crc = ::Zlib.crc32
10
+ @encrypter = encrypter
11
+ @buffer_stream = ::StringIO.new('')
10
12
  end
11
13
 
12
14
  def << (data)
13
15
  val = data.to_s
14
16
  @crc = Zlib::crc32(val, @crc)
15
17
  @size += val.bytesize
16
- @output_stream << @zlib_deflater.deflate(data)
18
+ @buffer_stream << @zlib_deflater.deflate(data)
17
19
  end
18
20
 
19
21
  def finish
20
- @output_stream << @zlib_deflater.finish until @zlib_deflater.finished?
22
+ @output_stream << @encrypter.encrypt(@buffer_stream.string)
23
+ @output_stream << @encrypter.encrypt(@zlib_deflater.finish) until @zlib_deflater.finished?
21
24
  end
22
25
 
23
26
  attr_reader :size, :crc
@@ -74,6 +74,8 @@ module Zip
74
74
  def time
75
75
  if @extra['UniversalTime']
76
76
  @extra['UniversalTime'].mtime
77
+ elsif @extra['NTFS']
78
+ @extra['NTFS'].mtime
77
79
  else
78
80
  # Standard time field in central directory has local time
79
81
  # under archive creator. Then, we can't get timezone.
@@ -84,10 +86,10 @@ module Zip
84
86
  alias :mtime :time
85
87
 
86
88
  def time=(value)
87
- unless @extra.member?('UniversalTime')
89
+ unless @extra.member?('UniversalTime') || @extra.member?('NTFS')
88
90
  @extra.create('UniversalTime')
89
91
  end
90
- @extra['UniversalTime'].mtime = value
92
+ (@extra['UniversalTime'] || @extra['NTFS']).mtime = value
91
93
  @time = value
92
94
  end
93
95
 
@@ -141,7 +143,7 @@ module Zip
141
143
  end
142
144
 
143
145
  def next_header_offset #:nodoc:all
144
- local_entry_offset + self.compressed_size
146
+ local_entry_offset + self.compressed_size + data_descriptor_size
145
147
  end
146
148
 
147
149
  # Extracts entry to file dest_path (defaults to @name).
@@ -362,7 +364,7 @@ module Zip
362
364
  unpack_c_dir_entry(static_sized_fields_buf)
363
365
  check_c_dir_entry_signature
364
366
  set_time(@last_mod_date, @last_mod_time)
365
- @name = io.read(@name_length).gsub('\\', '/')
367
+ @name = io.read(@name_length).tr('\\', '/')
366
368
  read_c_dir_extra_field(io)
367
369
  @comment = io.read(@comment_length)
368
370
  check_c_dir_entry_comment_size
@@ -569,7 +571,7 @@ module Zip
569
571
  def set_time(binary_dos_date, binary_dos_time)
570
572
  @time = ::Zip::DOSTime.parse_binary_dos_format(binary_dos_date, binary_dos_time)
571
573
  rescue ArgumentError
572
- puts "Invalid date/time in zip entry"
574
+ puts "Invalid date/time in zip entry" if ::Zip.warn_invalid_date
573
575
  end
574
576
 
575
577
  def create_file(dest_path, continue_on_exists_proc = proc { Zip.continue_on_exists_proc })
@@ -646,6 +648,10 @@ module Zip
646
648
  end
647
649
  end
648
650
 
651
+ def data_descriptor_size
652
+ (@gp_flags & 0x0008) > 0 ? 16 : 0
653
+ end
654
+
649
655
  # create a zip64 extra information field if we need one
650
656
  def prep_zip64_extra(for_local_header) #:nodoc:all
651
657
  return unless ::Zip.write_zip64_support
@@ -38,17 +38,13 @@ module Zip
38
38
  end
39
39
 
40
40
  def each(&block)
41
- @entry_set = @entry_set.dup.each do |_, value|
41
+ @entry_set = sorted_entries.dup.each do |_, value|
42
42
  block.call(value)
43
43
  end
44
44
  end
45
45
 
46
46
  def entries
47
- if ::Zip.sort_entries == true
48
- @entry_set.values.sort_by{|x| x.name}
49
- else
50
- @entry_set.values
51
- end
47
+ sorted_entries.values
52
48
  end
53
49
 
54
50
  # deep clone
@@ -74,6 +70,9 @@ module Zip
74
70
  end
75
71
 
76
72
  protected
73
+ def sorted_entries
74
+ ::Zip.sort_entries ? Hash[@entry_set.sort] : @entry_set
75
+ end
77
76
 
78
77
  private
79
78
  def to_key(entry)
@@ -94,6 +94,7 @@ require 'zip/extra_field/old_unix'
94
94
  require 'zip/extra_field/unix'
95
95
  require 'zip/extra_field/zip64'
96
96
  require 'zip/extra_field/zip64_placeholder'
97
+ require 'zip/extra_field/ntfs'
97
98
 
98
99
  # Copyright (C) 2002, 2003 Thomas Sondergaard
99
100
  # rubyzip is free software; you can redistribute it and/or