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
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zip
2
- class NullCompressor < Compressor #:nodoc:all
4
+ class NullCompressor < Compressor # :nodoc:all
3
5
  include Singleton
4
6
 
5
7
  def <<(_data)
@@ -1,24 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zip
2
- module NullDecompressor #:nodoc:all
4
+ module NullDecompressor # :nodoc:all
3
5
  module_function
4
6
 
5
- def sysread(_numberOfBytes = nil, _buf = nil)
6
- nil
7
- end
8
-
9
- def produce_input
7
+ def read(_length = nil, _outbuf = nil)
10
8
  nil
11
9
  end
12
10
 
13
- def input_finished?
14
- true
15
- end
16
-
17
- def eof
11
+ def eof?
18
12
  true
19
13
  end
20
14
 
21
- alias eof? eof
15
+ # Alias for compatibility. Remove for version 4.
16
+ alias eof eof?
22
17
  end
23
18
  end
24
19
 
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zip
2
- module NullInputStream #:nodoc:all
4
+ module NullInputStream # :nodoc:all
3
5
  include ::Zip::NullDecompressor
4
6
  include ::Zip::IOExtras::AbstractInputStream
5
7
  end
@@ -1,3 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
5
+ ##
1
6
  module Zip
2
7
  # ZipOutputStream is the basic class for writing zip files. It is
3
8
  # possible to create a ZipOutputStream object directly, passing
@@ -16,48 +21,52 @@ module Zip
16
21
  #
17
22
  # java.util.zip.ZipOutputStream is the original inspiration for this
18
23
  # class.
19
-
20
24
  class OutputStream
25
+ extend Forwardable
21
26
  include ::Zip::IOExtras::AbstractOutputStream
22
27
 
23
- attr_accessor :comment
28
+ def_delegators :@cdir, :comment, :comment=
24
29
 
25
30
  # Opens the indicated zip file. If a file with that name already
26
31
  # exists it will be overwritten.
27
- def initialize(file_name, stream = false, encrypter = nil)
32
+ def initialize(file_name, stream: false, encrypter: nil, suppress_extra_fields: false)
28
33
  super()
29
34
  @file_name = file_name
30
35
  @output_stream = if stream
31
- iostream = @file_name.dup
36
+ iostream = Zip::RUNNING_ON_WINDOWS ? @file_name : @file_name.dup
32
37
  iostream.reopen(@file_name)
33
38
  iostream.rewind
34
39
  iostream
35
40
  else
36
41
  ::File.new(@file_name, 'wb')
37
42
  end
38
- @entry_set = ::Zip::EntrySet.new
43
+ @cdir = ::Zip::CentralDirectory.new
39
44
  @compressor = ::Zip::NullCompressor.instance
40
45
  @encrypter = encrypter || ::Zip::NullEncrypter.new
46
+ @suppress_extra_fields = suppress_extra_fields
41
47
  @closed = false
42
48
  @current_entry = nil
43
- @comment = nil
44
49
  end
45
50
 
46
- # Same as #initialize but if a block is passed the opened
47
- # stream is passed to the block and closed when the block
48
- # returns.
49
51
  class << self
50
- def open(file_name, encrypter = nil)
52
+ # Same as #initialize but if a block is passed the opened
53
+ # stream is passed to the block and closed when the block
54
+ # returns.
55
+ def open(file_name, encrypter: nil, suppress_extra_fields: false)
51
56
  return new(file_name) unless block_given?
52
- zos = new(file_name, false, encrypter)
57
+
58
+ zos = new(file_name, stream: false, encrypter: encrypter,
59
+ suppress_extra_fields: suppress_extra_fields)
53
60
  yield zos
54
61
  ensure
55
62
  zos.close if zos
56
63
  end
57
64
 
58
65
  # Same as #open but writes to a filestream instead
59
- def write_buffer(io = ::StringIO.new(''), encrypter = nil)
60
- zos = new(io, true, encrypter)
66
+ def write_buffer(io = ::StringIO.new, encrypter: nil, suppress_extra_fields: false)
67
+ io.binmode if io.respond_to?(:binmode)
68
+ zos = new(io, stream: true, encrypter: encrypter,
69
+ suppress_extra_fields: suppress_extra_fields)
61
70
  yield zos
62
71
  zos.close_buffer
63
72
  end
@@ -66,9 +75,10 @@ module Zip
66
75
  # Closes the stream and writes the central directory to the zip file
67
76
  def close
68
77
  return if @closed
78
+
69
79
  finalize_current_entry
70
80
  update_local_headers
71
- write_central_directory
81
+ @cdir.write_to_stream(@output_stream, suppress_extra_fields: @suppress_extra_fields)
72
82
  @output_stream.close
73
83
  @closed = true
74
84
  end
@@ -76,37 +86,44 @@ module Zip
76
86
  # Closes the stream and writes the central directory to the zip file
77
87
  def close_buffer
78
88
  return @output_stream if @closed
89
+
79
90
  finalize_current_entry
80
91
  update_local_headers
81
- write_central_directory
92
+ @cdir.write_to_stream(@output_stream, suppress_extra_fields: @suppress_extra_fields)
82
93
  @closed = true
94
+ @output_stream.flush
83
95
  @output_stream
84
96
  end
85
97
 
86
98
  # Closes the current entry and opens a new for writing.
87
99
  # +entry+ can be a ZipEntry object or a string.
88
- def put_next_entry(entry_name, comment = nil, extra = nil, compression_method = Entry::DEFLATED, level = Zip.default_compression)
100
+ def put_next_entry(
101
+ entry_name, comment = '', extra = ExtraField.new,
102
+ compression_method = Entry::DEFLATED, level = Zip.default_compression
103
+ )
89
104
  raise Error, 'zip stream is closed' if @closed
90
- new_entry = if entry_name.kind_of?(Entry)
91
- entry_name
92
- else
93
- Entry.new(@file_name, entry_name.to_s)
94
- end
95
- new_entry.comment = comment unless comment.nil?
96
- unless extra.nil?
97
- new_entry.extra = extra.is_a?(ExtraField) ? extra : ExtraField.new(extra.to_s)
98
- end
99
- new_entry.compression_method = compression_method unless compression_method.nil?
100
- init_next_entry(new_entry, level)
105
+
106
+ new_entry =
107
+ if entry_name.kind_of?(Entry) || entry_name.kind_of?(StreamableStream)
108
+ entry_name
109
+ else
110
+ Entry.new(
111
+ @file_name, entry_name.to_s, comment: comment, extra: extra,
112
+ compression_method: compression_method, compression_level: level
113
+ )
114
+ end
115
+
116
+ init_next_entry(new_entry)
101
117
  @current_entry = new_entry
102
118
  end
103
119
 
104
- def copy_raw_entry(entry)
120
+ def copy_raw_entry(entry) # :nodoc:
105
121
  entry = entry.dup
106
122
  raise Error, 'zip stream is closed' if @closed
107
- raise Error, 'entry is not a ZipEntry' unless entry.is_a?(Entry)
123
+ raise Error, 'entry is not a ZipEntry' unless entry.kind_of?(Entry)
124
+
108
125
  finalize_current_entry
109
- @entry_set << entry
126
+ @cdir << entry
110
127
  src_pos = entry.local_header_offset
111
128
  entry.write_local_entry(@output_stream)
112
129
  @compressor = NullCompressor.instance
@@ -123,54 +140,56 @@ module Zip
123
140
 
124
141
  def finalize_current_entry
125
142
  return unless @current_entry
143
+
126
144
  finish
127
- @current_entry.compressed_size = @output_stream.tell - @current_entry.local_header_offset - @current_entry.calculate_local_header_size
145
+ @current_entry.compressed_size = @output_stream.tell -
146
+ @current_entry.local_header_offset -
147
+ @current_entry.calculate_local_header_size
128
148
  @current_entry.size = @compressor.size
129
149
  @current_entry.crc = @compressor.crc
130
- @output_stream << @encrypter.data_descriptor(@current_entry.crc, @current_entry.compressed_size, @current_entry.size)
150
+ @output_stream << @encrypter.data_descriptor(
151
+ @current_entry.crc,
152
+ @current_entry.compressed_size,
153
+ @current_entry.size
154
+ )
131
155
  @current_entry.gp_flags |= @encrypter.gp_flags
132
156
  @current_entry = nil
133
157
  @compressor = ::Zip::NullCompressor.instance
134
158
  end
135
159
 
136
- def init_next_entry(entry, level = Zip.default_compression)
160
+ def init_next_entry(entry)
137
161
  finalize_current_entry
138
- @entry_set << entry
139
- entry.write_local_entry(@output_stream)
162
+ @cdir << entry
163
+ entry.write_local_entry(@output_stream, suppress_extra_fields: @suppress_extra_fields)
140
164
  @encrypter.reset!
141
165
  @output_stream << @encrypter.header(entry.mtime)
142
- @compressor = get_compressor(entry, level)
166
+ @compressor = get_compressor(entry)
143
167
  end
144
168
 
145
- def get_compressor(entry, level)
169
+ def get_compressor(entry)
146
170
  case entry.compression_method
147
- when Entry::DEFLATED then
148
- ::Zip::Deflater.new(@output_stream, level, @encrypter)
149
- when Entry::STORED then
171
+ when Entry::DEFLATED
172
+ ::Zip::Deflater.new(@output_stream, entry.compression_level, @encrypter)
173
+ when Entry::STORED
150
174
  ::Zip::PassThruCompressor.new(@output_stream)
151
175
  else
152
- raise ::Zip::CompressionMethodError,
153
- "Invalid compression method: '#{entry.compression_method}'"
176
+ raise ::Zip::CompressionMethodError, entry.compression_method
154
177
  end
155
178
  end
156
179
 
157
180
  def update_local_headers
158
181
  pos = @output_stream.pos
159
- @entry_set.each do |entry|
182
+ @cdir.each do |entry|
160
183
  @output_stream.pos = entry.local_header_offset
161
- entry.write_local_entry(@output_stream, true)
184
+ entry.write_local_entry(@output_stream, suppress_extra_fields: @suppress_extra_fields,
185
+ rewrite: true)
162
186
  end
163
187
  @output_stream.pos = pos
164
188
  end
165
189
 
166
- def write_central_directory
167
- cdir = CentralDirectory.new(@entry_set, @comment)
168
- cdir.write_to_stream(@output_stream)
169
- end
170
-
171
190
  protected
172
191
 
173
- def finish
192
+ def finish # :nodoc:
174
193
  @compressor.finish
175
194
  end
176
195
 
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zip
2
- class PassThruCompressor < Compressor #:nodoc:all
3
- def initialize(outputStream)
4
+ class PassThruCompressor < Compressor # :nodoc:all
5
+ def initialize(output_stream)
4
6
  super()
5
- @output_stream = outputStream
7
+ @output_stream = output_stream
6
8
  @crc = Zlib.crc32
7
9
  @size = 0
8
10
  end
@@ -1,38 +1,32 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zip
2
- class PassThruDecompressor < Decompressor #:nodoc:all
3
- def initialize(input_stream, chars_to_read)
4
- super(input_stream)
5
- @chars_to_read = chars_to_read
4
+ class PassThruDecompressor < Decompressor # :nodoc:all
5
+ def initialize(*args)
6
+ super
6
7
  @read_so_far = 0
7
- @has_returned_empty_string = false
8
8
  end
9
9
 
10
- def sysread(number_of_bytes = nil, buf = '')
11
- if input_finished?
12
- has_returned_empty_string_val = @has_returned_empty_string
13
- @has_returned_empty_string = true
14
- return '' unless has_returned_empty_string_val
15
- return
16
- end
10
+ def read(maxlen = nil)
11
+ return (maxlen.nil? || maxlen.zero? ? '' : nil) if eof?
17
12
 
18
- if number_of_bytes.nil? || @read_so_far + number_of_bytes > @chars_to_read
19
- number_of_bytes = @chars_to_read - @read_so_far
13
+ if maxlen.nil? || (@read_so_far + maxlen) > decompressed_size
14
+ maxlen = decompressed_size - @read_so_far
20
15
  end
21
- @read_so_far += number_of_bytes
22
- @input_stream.read(number_of_bytes, buf)
23
- end
24
16
 
25
- def produce_input
26
- sysread(::Zip::Decompressor::CHUNK_SIZE)
17
+ @read_so_far += maxlen
18
+ input_stream.read(maxlen)
27
19
  end
28
20
 
29
- def input_finished?
30
- @read_so_far >= @chars_to_read
21
+ def eof?
22
+ @read_so_far >= decompressed_size
31
23
  end
32
24
 
33
- alias eof input_finished?
34
- alias eof? input_finished?
25
+ # Alias for compatibility. Remove for version 4.
26
+ alias eof eof?
35
27
  end
28
+
29
+ ::Zip::Decompressor.register(::Zip::COMPRESSION_METHOD_STORE, ::Zip::PassThruDecompressor)
36
30
  end
37
31
 
38
32
  # Copyright (C) 2002, 2003 Thomas Sondergaard
@@ -1,11 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zip
2
- class StreamableDirectory < Entry
3
- def initialize(zipfile, entry, srcPath = nil, permissionInt = nil)
4
+ class StreamableDirectory < Entry # :nodoc:
5
+ def initialize(zipfile, entry, src_path = nil, permission = nil)
4
6
  super(zipfile, entry)
5
7
 
6
8
  @ftype = :directory
7
- entry.get_extra_attributes_from_path(srcPath) if srcPath
8
- @unix_perms = permissionInt if permissionInt
9
+ entry.get_extra_attributes_from_path(src_path) if src_path
10
+ @unix_perms = permission if permission
9
11
  end
10
12
  end
11
13
  end
@@ -1,13 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zip
2
- class StreamableStream < DelegateClass(Entry) # nodoc:all
4
+ class StreamableStream < DelegateClass(Entry) # :nodoc:all
3
5
  def initialize(entry)
4
- super(entry)
5
- dirname = if zipfile.is_a?(::String)
6
- ::File.dirname(zipfile)
7
- else
8
- nil
9
- end
10
- @temp_file = Tempfile.new(::File.basename(name), dirname)
6
+ super
7
+ @temp_file = Tempfile.new(::File.basename(name))
11
8
  @temp_file.binmode
12
9
  end
13
10
 
@@ -27,6 +24,7 @@ module Zip
27
24
  unless @temp_file.closed?
28
25
  raise StandardError, "cannot open entry for reading while its open for writing - #{name}"
29
26
  end
27
+
30
28
  @temp_file.open # reopens tempfile from top
31
29
  @temp_file.binmode
32
30
  if block_given?
@@ -40,12 +38,13 @@ module Zip
40
38
  end
41
39
  end
42
40
 
43
- def write_to_zip_output_stream(aZipOutputStream)
44
- aZipOutputStream.put_next_entry(self)
45
- get_input_stream { |is| ::Zip::IOExtras.copy_stream(aZipOutputStream, is) }
41
+ def write_to_zip_output_stream(output_stream)
42
+ output_stream.put_next_entry(self)
43
+ get_input_stream { |is| ::Zip::IOExtras.copy_stream(output_stream, is) }
46
44
  end
47
45
 
48
46
  def clean_up
47
+ super
49
48
  @temp_file.unlink
50
49
  end
51
50
  end
data/lib/zip/version.rb CHANGED
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Zip
2
- VERSION = '1.3.0'
4
+ # The version of the Rubyzip library.
5
+ VERSION = '3.4.0'
3
6
  end
data/lib/zip.rb CHANGED
@@ -1,10 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'English'
1
4
  require 'delegate'
2
5
  require 'singleton'
3
6
  require 'tempfile'
4
- require 'tmpdir'
5
7
  require 'fileutils'
6
8
  require 'stringio'
7
9
  require 'zlib'
10
+ require 'zip/version'
11
+ require 'zip/constants'
8
12
  require 'zip/dos_time'
9
13
  require 'zip/ioextras'
10
14
  require 'rbconfig'
@@ -22,18 +26,25 @@ require 'zip/null_compressor'
22
26
  require 'zip/null_input_stream'
23
27
  require 'zip/pass_thru_compressor'
24
28
  require 'zip/pass_thru_decompressor'
29
+ require 'zip/crypto/decrypted_io'
25
30
  require 'zip/crypto/encryption'
26
31
  require 'zip/crypto/null_encryption'
27
32
  require 'zip/crypto/traditional_encryption'
33
+ require 'zip/crypto/aes_encryption'
28
34
  require 'zip/inflater'
29
35
  require 'zip/deflater'
30
36
  require 'zip/streamable_stream'
31
37
  require 'zip/streamable_directory'
32
- require 'zip/constants'
33
38
  require 'zip/errors'
34
39
 
40
+ # Rubyzip is a ruby module for reading and writing zip files.
41
+ #
42
+ # The main entry points are File, InputStream and OutputStream. For a
43
+ # file/directory interface in the style of the standard ruby ::File and
44
+ # ::Dir APIs then `require 'zip/filesystem'` and see FileSystem.
35
45
  module Zip
36
46
  extend self
47
+
37
48
  attr_accessor :unicode_names,
38
49
  :on_exists_proc,
39
50
  :continue_on_exists_proc,
@@ -43,21 +54,35 @@ module Zip
43
54
  :warn_invalid_date,
44
55
  :case_insensitive_match,
45
56
  :force_entry_names_encoding,
46
- :validate_entry_sizes
57
+ :validate_entry_sizes,
58
+ :validate_declared_number_of_entries
59
+
60
+ DEFAULT_RESTORE_OPTIONS = {
61
+ restore_ownership: false,
62
+ restore_permissions: true,
63
+ restore_times: true
64
+ }.freeze # :nodoc:
65
+
66
+ # :nodoc:
67
+ # Remove this when JRuby#3962 is fixed.
68
+ ZLIB_FLUSHING_STRATEGY = defined?(JRUBY_VERSION) ? Zlib::SYNC_FLUSH : Zlib::NO_FLUSH
47
69
 
48
- def reset!
70
+ def reset! # :nodoc:
49
71
  @_ran_once = false
50
72
  @unicode_names = false
51
73
  @on_exists_proc = false
52
74
  @continue_on_exists_proc = false
53
75
  @sort_entries = false
54
- @default_compression = ::Zlib::DEFAULT_COMPRESSION
55
- @write_zip64_support = false
76
+ @default_compression = Zlib::DEFAULT_COMPRESSION
77
+ @write_zip64_support = true
56
78
  @warn_invalid_date = true
57
79
  @case_insensitive_match = false
58
- @validate_entry_sizes = false
80
+ @force_entry_names_encoding = nil
81
+ @validate_entry_sizes = true
82
+ @validate_declared_number_of_entries = false # Set this to `true` in v4.0.0?
59
83
  end
60
84
 
85
+ # Set options for RubyZip in one block.
61
86
  def setup
62
87
  yield self unless @_ran_once
63
88
  @_ran_once = true
data/rubyzip.gemspec ADDED
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/zip/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'rubyzip'
7
+ s.version = Zip::VERSION
8
+ s.authors = ['Robert Haines', 'John Lees-Miller', 'Alexander Simonov']
9
+ s.email = [
10
+ 'hainesr@gmail.com', 'jdleesmiller@gmail.com', 'alex@simonov.me'
11
+ ]
12
+ s.homepage = 'http://github.com/rubyzip/rubyzip'
13
+ s.platform = Gem::Platform::RUBY
14
+ s.summary = 'rubyzip is a ruby module for reading and writing zip files'
15
+ s.files = Dir.glob('{samples,lib}/**/*.rb') +
16
+ %w[LICENSE.md README.md Changelog.md Rakefile rubyzip.gemspec]
17
+ s.require_paths = ['lib']
18
+ s.license = 'BSD-2-Clause'
19
+
20
+ s.metadata = {
21
+ 'bug_tracker_uri' => 'https://github.com/rubyzip/rubyzip/issues',
22
+ 'changelog_uri' => "https://github.com/rubyzip/rubyzip/blob/v#{s.version}/Changelog.md",
23
+ 'documentation_uri' => "https://www.rubydoc.info/gems/rubyzip/#{s.version}",
24
+ 'source_code_uri' => "https://github.com/rubyzip/rubyzip/tree/v#{s.version}",
25
+ 'wiki_uri' => 'https://github.com/rubyzip/rubyzip/wiki',
26
+ 'rubygems_mfa_required' => 'true'
27
+ }
28
+
29
+ s.required_ruby_version = '>= 3.0'
30
+
31
+ s.add_development_dependency 'minitest', '~> 5.25'
32
+ s.add_development_dependency 'rake', '~> 13.2'
33
+ s.add_development_dependency 'rdoc', '~> 6.11'
34
+ s.add_development_dependency 'rubocop', '~> 1.80.2'
35
+ s.add_development_dependency 'rubocop-performance', '~> 1.26.0'
36
+ s.add_development_dependency 'rubocop-rake', '~> 0.7.1'
37
+ s.add_development_dependency 'simplecov', '~> 0.22.0'
38
+ s.add_development_dependency 'simplecov-lcov', '~> 0.8'
39
+ end
data/samples/example.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
- $: << '../lib'
4
+ $LOAD_PATH << '../lib'
4
5
  system('zip example.zip example.rb gtk_ruby_zip.rb')
5
6
 
6
7
  require 'zip'
@@ -20,7 +21,8 @@ end
20
21
 
21
22
  zf = Zip::File.new('example.zip')
22
23
  zf.each_with_index do |entry, index|
23
- puts "entry #{index} is #{entry.name}, size = #{entry.size}, compressed size = #{entry.compressed_size}"
24
+ puts "entry #{index} is #{entry.name}, size = #{entry.size}, " \
25
+ "compressed size = #{entry.compressed_size}"
24
26
  # use zf.get_input_stream(entry) to get a ZipInputStream for the entry
25
27
  # entry can be the ZipEntry object or any object which has a to_s method that
26
28
  # returns the name of the entry.
@@ -70,8 +72,11 @@ part_zips_count = Zip::File.split('large_zip_file.zip', 2_097_152, false)
70
72
  puts "Zip file splitted in #{part_zips_count} parts"
71
73
 
72
74
  # Track splitting an archive
73
- Zip::File.split('large_zip_file.zip', 1_048_576, true, 'part_zip_file') do |part_count, part_index, chunk_bytes, segment_bytes|
74
- puts "#{part_index} of #{part_count} part splitting: #{(chunk_bytes.to_f / segment_bytes.to_f * 100).to_i}%"
75
+ Zip::File.split(
76
+ 'large_zip_file.zip', 1_048_576, true, 'part_zip_file'
77
+ ) do |part_count, part_index, chunk_bytes, segment_bytes|
78
+ puts "#{part_index} of #{part_count} part splitting: " \
79
+ "#{(chunk_bytes.to_f / segment_bytes * 100).to_i}%"
75
80
  end
76
81
 
77
82
  # For other examples, look at zip.rb and ziptest.rb
@@ -1,14 +1,15 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
- $: << '../lib'
4
+ $LOAD_PATH << '../lib'
4
5
 
5
6
  require 'zip/filesystem'
6
7
 
7
8
  EXAMPLE_ZIP = 'filesystem.zip'
8
9
 
9
- File.delete(EXAMPLE_ZIP) if File.exist?(EXAMPLE_ZIP)
10
+ FileUtils.rm_f(EXAMPLE_ZIP)
10
11
 
11
- Zip::File.open(EXAMPLE_ZIP, Zip::File::CREATE) do |zf|
12
+ Zip::File.open(EXAMPLE_ZIP, create: true) do |zf|
12
13
  zf.file.open('file1.txt', 'w') { |os| os.write 'first file1.txt' }
13
14
  zf.dir.mkdir('dir1')
14
15
  zf.dir.chdir('dir1')
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'zip'
2
4
 
3
5
  # This is a simple example which uses rubyzip to
@@ -21,7 +23,7 @@ class ZipFileGenerator
21
23
  def write
22
24
  entries = Dir.entries(@input_dir) - %w[. ..]
23
25
 
24
- ::Zip::File.open(@output_file, ::Zip::File::CREATE) do |zipfile|
26
+ ::Zip::File.open(@output_file, create: true) do |zipfile|
25
27
  write_entries entries, '', zipfile
26
28
  end
27
29
  end