rubyzip 1.3.0 → 3.4.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.
Files changed (138) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog.md +516 -0
  3. data/LICENSE.md +24 -0
  4. data/README.md +213 -51
  5. data/Rakefile +17 -12
  6. data/lib/rubyzip.rb +3 -0
  7. data/lib/zip/central_directory.rb +196 -123
  8. data/lib/zip/compressor.rb +3 -1
  9. data/lib/zip/constants.rb +81 -21
  10. data/lib/zip/crypto/aes_encryption.rb +120 -0
  11. data/lib/zip/crypto/decrypted_io.rb +46 -0
  12. data/lib/zip/crypto/encryption.rb +4 -2
  13. data/lib/zip/crypto/null_encryption.rb +5 -13
  14. data/lib/zip/crypto/traditional_encryption.rb +22 -16
  15. data/lib/zip/decompressor.rb +21 -2
  16. data/lib/zip/deflater.rb +12 -8
  17. data/lib/zip/dirtyable.rb +32 -0
  18. data/lib/zip/dos_time.rb +65 -13
  19. data/lib/zip/entry.rb +433 -249
  20. data/lib/zip/entry_set.rb +13 -9
  21. data/lib/zip/errors.rb +152 -15
  22. data/lib/zip/extra_field/aes.rb +50 -0
  23. data/lib/zip/extra_field/generic.rb +18 -18
  24. data/lib/zip/extra_field/ntfs.rb +10 -4
  25. data/lib/zip/extra_field/old_unix.rb +6 -2
  26. data/lib/zip/extra_field/universal_time.rb +45 -13
  27. data/lib/zip/extra_field/unix.rb +7 -3
  28. data/lib/zip/extra_field/unknown.rb +35 -0
  29. data/lib/zip/extra_field/zip64.rb +23 -7
  30. data/lib/zip/extra_field.rb +31 -27
  31. data/lib/zip/file.rb +225 -241
  32. data/lib/zip/file_split.rb +91 -0
  33. data/lib/zip/filesystem/dir.rb +86 -0
  34. data/lib/zip/filesystem/directory_iterator.rb +48 -0
  35. data/lib/zip/filesystem/file.rb +263 -0
  36. data/lib/zip/filesystem/file_stat.rb +110 -0
  37. data/lib/zip/filesystem/zip_file_name_mapper.rb +81 -0
  38. data/lib/zip/filesystem.rb +32 -585
  39. data/lib/zip/inflater.rb +28 -37
  40. data/lib/zip/input_stream.rb +113 -54
  41. data/lib/zip/ioextras/abstract_input_stream.rb +154 -55
  42. data/lib/zip/ioextras/abstract_output_stream.rb +13 -3
  43. data/lib/zip/ioextras.rb +8 -8
  44. data/lib/zip/null_compressor.rb +3 -1
  45. data/lib/zip/null_decompressor.rb +7 -12
  46. data/lib/zip/null_input_stream.rb +3 -1
  47. data/lib/zip/output_stream.rb +69 -50
  48. data/lib/zip/pass_thru_compressor.rb +5 -3
  49. data/lib/zip/pass_thru_decompressor.rb +17 -23
  50. data/lib/zip/streamable_directory.rb +6 -4
  51. data/lib/zip/streamable_stream.rb +10 -11
  52. data/lib/zip/version.rb +4 -1
  53. data/lib/zip.rb +32 -7
  54. data/rubyzip.gemspec +39 -0
  55. data/samples/example.rb +9 -4
  56. data/samples/example_filesystem.rb +4 -3
  57. data/samples/example_recursive.rb +3 -1
  58. data/samples/gtk_ruby_zip.rb +23 -21
  59. data/samples/qtzip.rb +13 -12
  60. data/samples/write_simple.rb +3 -4
  61. data/samples/zipfind.rb +24 -22
  62. metadata +85 -177
  63. data/TODO +0 -15
  64. data/lib/zip/extra_field/zip64_placeholder.rb +0 -15
  65. data/test/basic_zip_file_test.rb +0 -60
  66. data/test/case_sensitivity_test.rb +0 -69
  67. data/test/central_directory_entry_test.rb +0 -69
  68. data/test/central_directory_test.rb +0 -100
  69. data/test/crypto/null_encryption_test.rb +0 -57
  70. data/test/crypto/traditional_encryption_test.rb +0 -80
  71. data/test/data/WarnInvalidDate.zip +0 -0
  72. data/test/data/file1.txt +0 -46
  73. data/test/data/file1.txt.deflatedData +0 -0
  74. data/test/data/file2.txt +0 -1504
  75. data/test/data/globTest/foo/bar/baz/foo.txt +0 -0
  76. data/test/data/globTest/foo.txt +0 -0
  77. data/test/data/globTest/food.txt +0 -0
  78. data/test/data/globTest.zip +0 -0
  79. data/test/data/gpbit3stored.zip +0 -0
  80. data/test/data/mimetype +0 -1
  81. data/test/data/notzippedruby.rb +0 -7
  82. data/test/data/ntfs.zip +0 -0
  83. data/test/data/oddExtraField.zip +0 -0
  84. data/test/data/path_traversal/Makefile +0 -10
  85. data/test/data/path_traversal/jwilk/README.md +0 -5
  86. data/test/data/path_traversal/jwilk/absolute1.zip +0 -0
  87. data/test/data/path_traversal/jwilk/absolute2.zip +0 -0
  88. data/test/data/path_traversal/jwilk/dirsymlink.zip +0 -0
  89. data/test/data/path_traversal/jwilk/dirsymlink2a.zip +0 -0
  90. data/test/data/path_traversal/jwilk/dirsymlink2b.zip +0 -0
  91. data/test/data/path_traversal/jwilk/relative0.zip +0 -0
  92. data/test/data/path_traversal/jwilk/relative2.zip +0 -0
  93. data/test/data/path_traversal/jwilk/symlink.zip +0 -0
  94. data/test/data/path_traversal/relative1.zip +0 -0
  95. data/test/data/path_traversal/tilde.zip +0 -0
  96. data/test/data/path_traversal/tuzovakaoff/README.md +0 -3
  97. data/test/data/path_traversal/tuzovakaoff/absolutepath.zip +0 -0
  98. data/test/data/path_traversal/tuzovakaoff/symlink.zip +0 -0
  99. data/test/data/rubycode.zip +0 -0
  100. data/test/data/rubycode2.zip +0 -0
  101. data/test/data/test.xls +0 -0
  102. data/test/data/testDirectory.bin +0 -0
  103. data/test/data/zip64-sample.zip +0 -0
  104. data/test/data/zipWithDirs.zip +0 -0
  105. data/test/data/zipWithEncryption.zip +0 -0
  106. data/test/deflater_test.rb +0 -65
  107. data/test/encryption_test.rb +0 -42
  108. data/test/entry_set_test.rb +0 -163
  109. data/test/entry_test.rb +0 -154
  110. data/test/errors_test.rb +0 -35
  111. data/test/extra_field_test.rb +0 -76
  112. data/test/file_extract_directory_test.rb +0 -54
  113. data/test/file_extract_test.rb +0 -145
  114. data/test/file_permissions_test.rb +0 -65
  115. data/test/file_split_test.rb +0 -57
  116. data/test/file_test.rb +0 -666
  117. data/test/filesystem/dir_iterator_test.rb +0 -58
  118. data/test/filesystem/directory_test.rb +0 -139
  119. data/test/filesystem/file_mutating_test.rb +0 -87
  120. data/test/filesystem/file_nonmutating_test.rb +0 -508
  121. data/test/filesystem/file_stat_test.rb +0 -64
  122. data/test/gentestfiles.rb +0 -126
  123. data/test/inflater_test.rb +0 -14
  124. data/test/input_stream_test.rb +0 -182
  125. data/test/ioextras/abstract_input_stream_test.rb +0 -102
  126. data/test/ioextras/abstract_output_stream_test.rb +0 -106
  127. data/test/ioextras/fake_io_test.rb +0 -18
  128. data/test/local_entry_test.rb +0 -154
  129. data/test/output_stream_test.rb +0 -128
  130. data/test/pass_thru_compressor_test.rb +0 -30
  131. data/test/pass_thru_decompressor_test.rb +0 -14
  132. data/test/path_traversal_test.rb +0 -141
  133. data/test/samples/example_recursive_test.rb +0 -37
  134. data/test/settings_test.rb +0 -95
  135. data/test/test_helper.rb +0 -234
  136. data/test/unicode_file_names_and_comments_test.rb +0 -62
  137. data/test/zip64_full_test.rb +0 -51
  138. data/test/zip64_support_test.rb +0 -14
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Zip
4
+ class DecryptedIo # :nodoc:all
5
+ CHUNK_SIZE = 32_768
6
+
7
+ def initialize(io, decrypter, compressed_size)
8
+ @io = io
9
+ @decrypter = decrypter
10
+ @bytes_remaining = compressed_size
11
+ @buffer = +''.b
12
+ end
13
+
14
+ def read(maxlen = nil)
15
+ return (maxlen.nil? || maxlen.zero? ? '' : nil) if eof?
16
+
17
+ while maxlen.nil? || (@buffer.bytesize < maxlen)
18
+ break if input_finished?
19
+
20
+ @buffer << produce_input
21
+ end
22
+
23
+ @decrypter.check_integrity!(@io) if input_finished?
24
+
25
+ @buffer.slice!(0...(maxlen || @buffer.bytesize))
26
+ end
27
+
28
+ private
29
+
30
+ def eof?
31
+ @buffer.empty? && input_finished?
32
+ end
33
+
34
+ def input_finished?
35
+ !@bytes_remaining.positive?
36
+ end
37
+
38
+ def produce_input
39
+ chunk_size = [@bytes_remaining, CHUNK_SIZE].min
40
+ return '' unless chunk_size.positive?
41
+
42
+ @bytes_remaining -= chunk_size
43
+ @decrypter.decrypt(@io.read(chunk_size))
44
+ end
45
+ end
46
+ end
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zip
2
- class Encrypter #:nodoc:all
4
+ class Encrypter # :nodoc:all
3
5
  end
4
6
 
5
- class Decrypter
7
+ class Decrypter # :nodoc:all
6
8
  end
7
9
  end
8
10
 
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zip
2
- module NullEncryption
4
+ module NullEncryption # :nodoc:
3
5
  def header_bytesize
4
6
  0
5
7
  end
@@ -9,7 +11,7 @@ module Zip
9
11
  end
10
12
  end
11
13
 
12
- class NullEncrypter < Encrypter
14
+ class NullEncrypter < Encrypter # :nodoc:
13
15
  include NullEncryption
14
16
 
15
17
  def header(_mtime)
@@ -20,22 +22,12 @@ module Zip
20
22
  data
21
23
  end
22
24
 
23
- def data_descriptor(_crc32, _compressed_size, _uncomprssed_size)
25
+ def data_descriptor(_crc32, _compressed_size, _uncompressed_size)
24
26
  ''
25
27
  end
26
28
 
27
29
  def reset!; end
28
30
  end
29
-
30
- class NullDecrypter < Decrypter
31
- include NullEncryption
32
-
33
- def decrypt(data)
34
- data
35
- end
36
-
37
- def reset!(_header); end
38
- end
39
31
  end
40
32
 
41
33
  # Copyright (C) 2002, 2003 Thomas Sondergaard
@@ -1,5 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
1
5
  module Zip
2
- module TraditionalEncryption
6
+ module TraditionalEncryption # :nodoc:
3
7
  def initialize(password)
4
8
  @password = password
5
9
  reset_keys!
@@ -24,9 +28,9 @@ module Zip
24
28
  end
25
29
  end
26
30
 
27
- def update_keys(n)
28
- @key0 = ~Zlib.crc32(n, ~@key0)
29
- @key1 = ((@key1 + (@key0 & 0xff)) * 134_775_813 + 1) & 0xffffffff
31
+ def update_keys(num)
32
+ @key0 = ~Zlib.crc32(num, ~@key0)
33
+ @key1 = (((@key1 + (@key0 & 0xff)) * 134_775_813) + 1) & 0xffffffff
30
34
  @key2 = ~Zlib.crc32((@key1 >> 24).chr, ~@key2)
31
35
  end
32
36
 
@@ -36,13 +40,13 @@ module Zip
36
40
  end
37
41
  end
38
42
 
39
- class TraditionalEncrypter < Encrypter
43
+ class TraditionalEncrypter < Encrypter # :nodoc:
40
44
  include TraditionalEncryption
41
45
 
42
46
  def header(mtime)
43
47
  [].tap do |header|
44
48
  (header_bytesize - 2).times do
45
- header << Random.rand(0..255)
49
+ header << SecureRandom.rand(0..255)
46
50
  end
47
51
  header << (mtime.to_binary_dos_time & 0xff)
48
52
  header << (mtime.to_binary_dos_time >> 8)
@@ -53,8 +57,8 @@ module Zip
53
57
  data.unpack('C*').map { |x| encode x }.pack('C*')
54
58
  end
55
59
 
56
- def data_descriptor(crc32, compressed_size, uncomprssed_size)
57
- [0x08074b50, crc32, compressed_size, uncomprssed_size].pack('VVVV')
60
+ def data_descriptor(crc32, compressed_size, uncompressed_size)
61
+ [0x08074b50, crc32, compressed_size, uncompressed_size].pack('VVVV')
58
62
  end
59
63
 
60
64
  def reset!
@@ -63,14 +67,14 @@ module Zip
63
67
 
64
68
  private
65
69
 
66
- def encode(n)
70
+ def encode(num)
67
71
  t = decrypt_byte
68
- update_keys(n.chr)
69
- t ^ n
72
+ update_keys(num.chr)
73
+ t ^ num
70
74
  end
71
75
  end
72
76
 
73
- class TraditionalDecrypter < Decrypter
77
+ class TraditionalDecrypter < Decrypter # :nodoc:
74
78
  include TraditionalEncryption
75
79
 
76
80
  def decrypt(data)
@@ -84,12 +88,14 @@ module Zip
84
88
  end
85
89
  end
86
90
 
91
+ def check_integrity!(_io); end
92
+
87
93
  private
88
94
 
89
- def decode(n)
90
- n ^= decrypt_byte
91
- update_keys(n.chr)
92
- n
95
+ def decode(num)
96
+ num ^= decrypt_byte
97
+ update_keys(num.chr)
98
+ num
93
99
  end
94
100
  end
95
101
  end
@@ -1,9 +1,28 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zip
2
- class Decompressor #:nodoc:all
4
+ class Decompressor # :nodoc:all
3
5
  CHUNK_SIZE = 32_768
4
- def initialize(input_stream)
6
+
7
+ def self.decompressor_classes
8
+ @decompressor_classes ||= {}
9
+ end
10
+
11
+ def self.register(compression_method, decompressor_class)
12
+ decompressor_classes[compression_method] = decompressor_class
13
+ end
14
+
15
+ def self.find_by_compression_method(compression_method)
16
+ decompressor_classes[compression_method]
17
+ end
18
+
19
+ attr_reader :decompressed_size, :input_stream
20
+
21
+ def initialize(input_stream, decompressed_size = nil)
5
22
  super()
23
+
6
24
  @input_stream = input_stream
25
+ @decompressed_size = decompressed_size
7
26
  end
8
27
  end
9
28
  end
data/lib/zip/deflater.rb CHANGED
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zip
2
- class Deflater < Compressor #:nodoc:all
4
+ class Deflater < Compressor # :nodoc:all
3
5
  def initialize(output_stream, level = Zip.default_compression, encrypter = NullEncrypter.new)
4
6
  super()
5
7
  @output_stream = output_stream
@@ -13,16 +15,18 @@ module Zip
13
15
  val = data.to_s
14
16
  @crc = Zlib.crc32(val, @crc)
15
17
  @size += val.bytesize
16
- buffer = @zlib_deflater.deflate(data)
17
- if buffer.empty?
18
- @output_stream
19
- else
20
- @output_stream << @encrypter.encrypt(buffer)
21
- end
18
+
19
+ # When JRuby#3962 is fixed, we can remove the flushing parameter here.
20
+ buffer = @zlib_deflater.deflate(data, Zip::ZLIB_FLUSHING_STRATEGY)
21
+ return @output_stream if buffer.empty?
22
+
23
+ @output_stream << @encrypter.encrypt(buffer)
22
24
  end
23
25
 
24
26
  def finish
25
- @output_stream << @encrypter.encrypt(@zlib_deflater.finish) until @zlib_deflater.finished?
27
+ buffer = @zlib_deflater.finish
28
+ @output_stream << @encrypter.encrypt(buffer) unless buffer.empty?
29
+ @zlib_deflater.close
26
30
  end
27
31
 
28
32
  attr_reader :size, :crc
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Zip
4
+ module Dirtyable # :nodoc:all
5
+ def initialize(dirty_on_create: true)
6
+ @dirty = dirty_on_create
7
+ end
8
+
9
+ def dirty?
10
+ @dirty
11
+ end
12
+
13
+ module ClassMethods # :nodoc:
14
+ def mark_dirty(*symbols) # :nodoc:
15
+ # Move the original method and call it after we've set the dirty flag.
16
+ symbols.each do |symbol|
17
+ orig_name = "orig_#{symbol}"
18
+ alias_method orig_name, symbol
19
+
20
+ define_method(symbol) do |param|
21
+ @dirty = true
22
+ send(orig_name, param)
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ def self.included(base)
29
+ base.extend(ClassMethods)
30
+ end
31
+ end
32
+ end
data/lib/zip/dos_time.rb CHANGED
@@ -1,5 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubygems'
4
+
1
5
  module Zip
2
- class DOSTime < Time #:nodoc:all
6
+ class DOSTime < Time # :nodoc:all
3
7
  # MS-DOS File Date and Time format as used in Interrupt 21H Function 57H:
4
8
 
5
9
  # Register CX, the Time:
@@ -12,6 +16,14 @@ module Zip
12
16
  # bits 5-8 month (1-12)
13
17
  # bits 9-15 year (four digit year minus 1980)
14
18
 
19
+ attr_writer :absolute_time # :nodoc:
20
+
21
+ def absolute_time?
22
+ # If absolute time is not set, we can assume it is an absolute time
23
+ # because times do have timezone information by default.
24
+ @absolute_time.nil? || @absolute_time
25
+ end
26
+
15
27
  def to_binary_dos_time
16
28
  (sec / 2) +
17
29
  (min << 5) +
@@ -24,21 +36,61 @@ module Zip
24
36
  ((year - 1980) << 9)
25
37
  end
26
38
 
27
- # Dos time is only stored with two seconds accuracy
28
- def dos_equals(other)
29
- to_i / 2 == other.to_i / 2
39
+ # Deprecated. Remove for version 4.
40
+ def dos_equals(other) # rubocop:disable Naming/PredicateMethod
41
+ warn 'Zip::DOSTime#dos_equals is deprecated. Use `==` instead.'
42
+ self == other
43
+ end
44
+
45
+ # Dos time is only stored with two seconds accuracy.
46
+ def <=>(other)
47
+ return unless other.kind_of?(Time)
48
+
49
+ (to_i / 2) <=> (other.to_i / 2)
50
+ end
51
+
52
+ # Create a DOSTime instance from a vanilla Time instance.
53
+ def self.from_time(time)
54
+ local(time.year, time.month, time.day, time.hour, time.min, time.sec)
30
55
  end
31
56
 
32
- def self.parse_binary_dos_format(binaryDosDate, binaryDosTime)
33
- second = 2 * (0b11111 & binaryDosTime)
34
- minute = (0b11111100000 & binaryDosTime) >> 5
35
- hour = (0b1111100000000000 & binaryDosTime) >> 11
36
- day = (0b11111 & binaryDosDate)
37
- month = (0b111100000 & binaryDosDate) >> 5
38
- year = ((0b1111111000000000 & binaryDosDate) >> 9) + 1980
39
- begin
40
- local(year, month, day, hour, minute, second)
57
+ def self.parse_binary_dos_format(bin_dos_date, bin_dos_time)
58
+ second = 2 * (0b11111 & bin_dos_time)
59
+ minute = (0b11111100000 & bin_dos_time) >> 5
60
+ hour = (0b1111100000000000 & bin_dos_time) >> 11
61
+ day = (0b11111 & bin_dos_date)
62
+ month = (0b111100000 & bin_dos_date) >> 5
63
+ year = ((0b1111111000000000 & bin_dos_date) >> 9) + 1980
64
+
65
+ time = local(year, month, day, hour, minute, second)
66
+ time.absolute_time = false
67
+ time
68
+ end
69
+
70
+ if defined? JRUBY_VERSION && Gem::Version.new(JRUBY_VERSION) < '9.2.18.0'
71
+ module JRubyCMP # :nodoc:
72
+ def ==(other)
73
+ (self <=> other).zero?
74
+ end
75
+
76
+ def <(other)
77
+ (self <=> other).negative?
78
+ end
79
+
80
+ def <=(other)
81
+ (self <=> other) <= 0
82
+ end
83
+
84
+ def >(other)
85
+ (self <=> other).positive?
86
+ end
87
+
88
+ def >=(other)
89
+ (self <=> other) >= 0
90
+ end
41
91
  end
92
+
93
+ include JRubyCMP
42
94
  end
43
95
  end
44
96
  end