rubyzip 0.9.9 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +284 -41
  3. data/Rakefile +11 -6
  4. data/TODO +0 -1
  5. data/lib/zip/central_directory.rb +208 -0
  6. data/lib/zip/compressor.rb +1 -2
  7. data/lib/zip/constants.rb +59 -7
  8. data/lib/zip/crypto/encryption.rb +11 -0
  9. data/lib/zip/crypto/null_encryption.rb +43 -0
  10. data/lib/zip/crypto/traditional_encryption.rb +99 -0
  11. data/lib/zip/decompressor.rb +4 -4
  12. data/lib/zip/deflater.rb +17 -13
  13. data/lib/zip/dos_time.rb +13 -14
  14. data/lib/zip/entry.rb +700 -0
  15. data/lib/zip/entry_set.rb +86 -0
  16. data/lib/zip/errors.rb +18 -0
  17. data/lib/zip/extra_field/generic.rb +43 -0
  18. data/lib/zip/extra_field/ntfs.rb +90 -0
  19. data/lib/zip/extra_field/old_unix.rb +44 -0
  20. data/lib/zip/extra_field/universal_time.rb +47 -0
  21. data/lib/zip/extra_field/unix.rb +37 -0
  22. data/lib/zip/extra_field/zip64.rb +68 -0
  23. data/lib/zip/extra_field/zip64_placeholder.rb +15 -0
  24. data/lib/zip/extra_field.rb +101 -0
  25. data/lib/zip/file.rb +443 -0
  26. data/lib/zip/{zipfilesystem.rb → filesystem.rb} +162 -157
  27. data/lib/zip/inflater.rb +29 -28
  28. data/lib/zip/input_stream.rb +173 -0
  29. data/lib/zip/ioextras/abstract_input_stream.rb +111 -0
  30. data/lib/zip/ioextras/abstract_output_stream.rb +43 -0
  31. data/lib/zip/ioextras.rb +21 -149
  32. data/lib/zip/null_compressor.rb +2 -2
  33. data/lib/zip/null_decompressor.rb +8 -6
  34. data/lib/zip/null_input_stream.rb +3 -2
  35. data/lib/zip/output_stream.rb +189 -0
  36. data/lib/zip/pass_thru_compressor.rb +6 -6
  37. data/lib/zip/pass_thru_decompressor.rb +19 -19
  38. data/lib/zip/{zip_streamable_directory.rb → streamable_directory.rb} +3 -3
  39. data/lib/zip/streamable_stream.rb +56 -0
  40. data/lib/zip/version.rb +3 -0
  41. data/lib/zip.rb +71 -0
  42. data/samples/example.rb +44 -32
  43. data/samples/example_filesystem.rb +16 -18
  44. data/samples/example_recursive.rb +33 -28
  45. data/samples/{gtkRubyzip.rb → gtk_ruby_zip.rb} +26 -28
  46. data/samples/qtzip.rb +22 -31
  47. data/samples/write_simple.rb +12 -13
  48. data/samples/zipfind.rb +31 -39
  49. data/test/basic_zip_file_test.rb +60 -0
  50. data/test/case_sensitivity_test.rb +69 -0
  51. data/test/central_directory_entry_test.rb +69 -0
  52. data/test/central_directory_test.rb +100 -0
  53. data/test/crypto/null_encryption_test.rb +57 -0
  54. data/test/crypto/traditional_encryption_test.rb +80 -0
  55. data/test/data/WarnInvalidDate.zip +0 -0
  56. data/test/data/file1.txt +46 -0
  57. data/test/data/file1.txt.deflatedData +0 -0
  58. data/test/data/file2.txt +1504 -0
  59. data/test/data/globTest/foo/bar/baz/foo.txt +0 -0
  60. data/test/data/globTest/foo.txt +0 -0
  61. data/test/data/globTest/food.txt +0 -0
  62. data/test/data/globTest.zip +0 -0
  63. data/test/data/gpbit3stored.zip +0 -0
  64. data/test/data/mimetype +1 -0
  65. data/test/data/notzippedruby.rb +7 -0
  66. data/test/data/ntfs.zip +0 -0
  67. data/test/data/oddExtraField.zip +0 -0
  68. data/test/data/path_traversal/Makefile +10 -0
  69. data/test/data/path_traversal/jwilk/README.md +5 -0
  70. data/test/data/path_traversal/jwilk/absolute1.zip +0 -0
  71. data/test/data/path_traversal/jwilk/absolute2.zip +0 -0
  72. data/test/data/path_traversal/jwilk/dirsymlink.zip +0 -0
  73. data/test/data/path_traversal/jwilk/dirsymlink2a.zip +0 -0
  74. data/test/data/path_traversal/jwilk/dirsymlink2b.zip +0 -0
  75. data/test/data/path_traversal/jwilk/relative0.zip +0 -0
  76. data/test/data/path_traversal/jwilk/relative2.zip +0 -0
  77. data/test/data/path_traversal/jwilk/symlink.zip +0 -0
  78. data/test/data/path_traversal/relative1.zip +0 -0
  79. data/test/data/path_traversal/tilde.zip +0 -0
  80. data/test/data/path_traversal/tuzovakaoff/README.md +3 -0
  81. data/test/data/path_traversal/tuzovakaoff/absolutepath.zip +0 -0
  82. data/test/data/path_traversal/tuzovakaoff/symlink.zip +0 -0
  83. data/test/data/rubycode.zip +0 -0
  84. data/test/data/rubycode2.zip +0 -0
  85. data/test/data/test.xls +0 -0
  86. data/test/data/testDirectory.bin +0 -0
  87. data/test/data/zip64-sample.zip +0 -0
  88. data/test/data/zipWithDirs.zip +0 -0
  89. data/test/data/zipWithEncryption.zip +0 -0
  90. data/test/deflater_test.rb +65 -0
  91. data/test/encryption_test.rb +42 -0
  92. data/test/entry_set_test.rb +163 -0
  93. data/test/entry_test.rb +154 -0
  94. data/test/errors_test.rb +35 -0
  95. data/test/extra_field_test.rb +76 -0
  96. data/test/file_extract_directory_test.rb +54 -0
  97. data/test/file_extract_test.rb +145 -0
  98. data/test/file_permissions_test.rb +65 -0
  99. data/test/file_split_test.rb +57 -0
  100. data/test/file_test.rb +666 -0
  101. data/test/filesystem/dir_iterator_test.rb +58 -0
  102. data/test/filesystem/directory_test.rb +139 -0
  103. data/test/filesystem/file_mutating_test.rb +87 -0
  104. data/test/filesystem/file_nonmutating_test.rb +508 -0
  105. data/test/filesystem/file_stat_test.rb +64 -0
  106. data/test/gentestfiles.rb +126 -0
  107. data/test/inflater_test.rb +14 -0
  108. data/test/input_stream_test.rb +182 -0
  109. data/test/ioextras/abstract_input_stream_test.rb +102 -0
  110. data/test/ioextras/abstract_output_stream_test.rb +106 -0
  111. data/test/ioextras/fake_io_test.rb +18 -0
  112. data/test/local_entry_test.rb +154 -0
  113. data/test/output_stream_test.rb +128 -0
  114. data/test/pass_thru_compressor_test.rb +30 -0
  115. data/test/pass_thru_decompressor_test.rb +14 -0
  116. data/test/path_traversal_test.rb +141 -0
  117. data/test/samples/example_recursive_test.rb +37 -0
  118. data/test/settings_test.rb +95 -0
  119. data/test/test_helper.rb +234 -0
  120. data/test/unicode_file_names_and_comments_test.rb +62 -0
  121. data/test/zip64_full_test.rb +51 -0
  122. data/test/zip64_support_test.rb +14 -0
  123. metadata +274 -41
  124. data/NEWS +0 -172
  125. data/lib/zip/settings.rb +0 -10
  126. data/lib/zip/tempfile_bugfixed.rb +0 -195
  127. data/lib/zip/zip.rb +0 -56
  128. data/lib/zip/zip_central_directory.rb +0 -135
  129. data/lib/zip/zip_entry.rb +0 -638
  130. data/lib/zip/zip_entry_set.rb +0 -77
  131. data/lib/zip/zip_extra_field.rb +0 -213
  132. data/lib/zip/zip_file.rb +0 -340
  133. data/lib/zip/zip_input_stream.rb +0 -144
  134. data/lib/zip/zip_output_stream.rb +0 -173
  135. data/lib/zip/zip_streamable_stream.rb +0 -47
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7861f60d52fbe54891831d9239df3e3e927a33f1f2d00abcb7b2693a8c01097d
4
+ data.tar.gz: c04844369dbc75f40583f3d8aab34cbf2cfcce293b3c30570fa76208755a3157
5
+ SHA512:
6
+ metadata.gz: f9fa8a9f92c4789f06a1691eee6201918a753f1356eb6f2ef99fe9c777d46d661b7a8f96ce71b7a18406a4e1a4098f81b8a282babe2a7284ae0c8c04d03cf758
7
+ data.tar.gz: e4650b6f572ef4bcdb33cff4bd0c7ddbd399b04278bd644d11ea29defe057510706d15d0b8ac918df28280d13f4bdfec28526aaad258ff52136ce209f29a35dd
data/README.md CHANGED
@@ -1,9 +1,30 @@
1
- # rubyzip [![Build Status](https://secure.travis-ci.org/aussiegeek/rubyzip.png)](http://travis-ci.org/aussiegeek/rubyzip)
1
+ # rubyzip
2
2
 
3
- rubyzip is a ruby library for reading and writing zip files.
3
+ [![Gem Version](https://badge.fury.io/rb/rubyzip.svg)](http://badge.fury.io/rb/rubyzip)
4
+ [![Build Status](https://secure.travis-ci.org/rubyzip/rubyzip.svg)](http://travis-ci.org/rubyzip/rubyzip)
5
+ [![Code Climate](https://codeclimate.com/github/rubyzip/rubyzip.svg)](https://codeclimate.com/github/rubyzip/rubyzip)
6
+ [![Coverage Status](https://img.shields.io/coveralls/rubyzip/rubyzip.svg)](https://coveralls.io/r/rubyzip/rubyzip?branch=master)
7
+
8
+ Rubyzip is a ruby library for reading and writing zip files.
9
+
10
+ ## Important note
11
+
12
+ The Rubyzip interface has changed!!! No need to do `require "zip/zip"` and `Zip` prefix in class names removed.
13
+
14
+ If you have issues with any third-party gems that require an old version of rubyzip, you can use this workaround:
15
+
16
+ ```ruby
17
+ gem 'rubyzip', '>= 1.0.0' # will load new rubyzip version
18
+ gem 'zip-zip' # will load compatibility for old rubyzip API.
19
+ ```
20
+
21
+ ## Requirements
22
+
23
+ - Ruby 1.9.2 or greater
4
24
 
5
25
  ## Installation
6
- rubyzip is available on RubyGems, so:
26
+
27
+ Rubyzip is available on RubyGems:
7
28
 
8
29
  ```
9
30
  gem install rubyzip
@@ -21,72 +42,294 @@ gem 'rubyzip'
21
42
 
22
43
  ```ruby
23
44
  require 'rubygems'
24
- require 'zip/zip'
25
-
45
+ require 'zip'
46
+
26
47
  folder = "Users/me/Desktop/stuff_to_zip"
27
48
  input_filenames = ['image.jpg', 'description.txt', 'stats.csv']
28
-
49
+
29
50
  zipfile_name = "/Users/me/Desktop/archive.zip"
30
-
31
- Zip::ZipFile.open(zipfile_name, Zip::ZipFile::CREATE) do |zipfile|
51
+
52
+ Zip::File.open(zipfile_name, Zip::File::CREATE) do |zipfile|
32
53
  input_filenames.each do |filename|
33
54
  # Two arguments:
34
55
  # - The name of the file as it will appear in the archive
35
56
  # - The original file, including the path to find it
36
- zipfile.add(filename, folder + '/' + filename)
57
+ zipfile.add(filename, File.join(folder, filename))
37
58
  end
59
+ zipfile.get_output_stream("myFile") { |f| f.write "myFile contains just this" }
38
60
  end
39
61
  ```
40
62
 
41
- ## Further Documentation
63
+ ### Zipping a directory recursively
42
64
 
43
- There is more than one way to access or create a zip archive with
44
- rubyzip. The basic API is modeled after the classes in
45
- java.util.zip from the Java SDK. This means there are classes such
46
- as Zip::ZipInputStream, Zip::ZipOutputStream and
47
- Zip::ZipFile. Zip::ZipInputStream provides a basic interface for
48
- iterating through the entries in a zip archive and reading from the
49
- entries in the same way as from a regular File or IO
50
- object. ZipOutputStream is the corresponding basic output
51
- facility. Zip::ZipFile provides a mean for accessing the archives
52
- central directory and provides means for accessing any entry without
53
- having to iterate through the archive. Unlike Java's
54
- java.util.zip.ZipFile rubyzip's Zip::ZipFile is mutable, which means
55
- it can be used to change zip files as well.
65
+ Copy from [here](https://github.com/rubyzip/rubyzip/blob/9d891f7353e66052283562d3e252fe380bb4b199/samples/example_recursive.rb)
56
66
 
57
- Another way to access a zip archive with rubyzip is to use rubyzip's
58
- Zip::ZipFileSystem API. Using this API files can be read from and
59
- written to the archive in much the same manner as ruby's builtin
60
- classes allows files to be read from and written to the file system.
67
+ ```ruby
68
+ require 'zip'
69
+
70
+ # This is a simple example which uses rubyzip to
71
+ # recursively generate a zip file from the contents of
72
+ # a specified directory. The directory itself is not
73
+ # included in the archive, rather just its contents.
74
+ #
75
+ # Usage:
76
+ # directory_to_zip = "/tmp/input"
77
+ # output_file = "/tmp/out.zip"
78
+ # zf = ZipFileGenerator.new(directory_to_zip, output_file)
79
+ # zf.write()
80
+ class ZipFileGenerator
81
+ # Initialize with the directory to zip and the location of the output archive.
82
+ def initialize(input_dir, output_file)
83
+ @input_dir = input_dir
84
+ @output_file = output_file
85
+ end
61
86
 
62
- rubyzip also features the
63
- zip/ziprequire.rb[link:files/lib/zip/ziprequire_rb.html] module which
64
- allows ruby to load ruby modules from zip archives.
87
+ # Zip the input directory.
88
+ def write
89
+ entries = Dir.entries(@input_dir) - %w[. ..]
65
90
 
66
- For details about the specific behaviour of classes and methods refer
67
- to the test suite. Finally you can generate the rdoc documentation or
68
- visit http://rubyzip.sourceforge.net.
91
+ ::Zip::File.open(@output_file, ::Zip::File::CREATE) do |zipfile|
92
+ write_entries entries, '', zipfile
93
+ end
94
+ end
69
95
 
96
+ private
70
97
 
71
- ## Configuration
98
+ # A helper method to make the recursion work.
99
+ def write_entries(entries, path, zipfile)
100
+ entries.each do |e|
101
+ zipfile_path = path == '' ? e : File.join(path, e)
102
+ disk_file_path = File.join(@input_dir, zipfile_path)
103
+
104
+ if File.directory? disk_file_path
105
+ recursively_deflate_directory(disk_file_path, zipfile, zipfile_path)
106
+ else
107
+ put_into_archive(disk_file_path, zipfile, zipfile_path)
108
+ end
109
+ end
110
+ end
111
+
112
+ def recursively_deflate_directory(disk_file_path, zipfile, zipfile_path)
113
+ zipfile.mkdir zipfile_path
114
+ subdir = Dir.entries(disk_file_path) - %w[. ..]
115
+ write_entries subdir, zipfile_path, zipfile
116
+ end
117
+
118
+ def put_into_archive(disk_file_path, zipfile, zipfile_path)
119
+ zipfile.add(zipfile_path, disk_file_path)
120
+ end
121
+ end
122
+ ```
123
+
124
+ ### Save zip archive entries in sorted by name state
125
+
126
+ To save zip archives in sorted order like below, you need to set `::Zip.sort_entries` to `true`
127
+
128
+ ```
129
+ Vegetable/
130
+ Vegetable/bean
131
+ Vegetable/carrot
132
+ Vegetable/celery
133
+ fruit/
134
+ fruit/apple
135
+ fruit/kiwi
136
+ fruit/mango
137
+ fruit/orange
138
+ ```
139
+
140
+ After this, entries in the zip archive will be saved in ordered state.
141
+
142
+ ### Default permissions of zip archives
143
+
144
+ On Posix file systems the default file permissions applied to a new archive
145
+ are (0666 - umask), which mimics the behavior of standard tools such as `touch`.
146
+
147
+ On Windows the default file permissions are set to 0644 as suggested by the
148
+ [Ruby File documentation](http://ruby-doc.org/core-2.2.2/File.html).
149
+
150
+ When modifying a zip archive the file permissions of the archive are preserved.
151
+
152
+ ### Reading a Zip file
153
+
154
+ ```ruby
155
+ MAX_SIZE = 1024**2 # 1MiB (but of course you can increase this)
156
+ Zip::File.open('foo.zip') do |zip_file|
157
+ # Handle entries one by one
158
+ zip_file.each do |entry|
159
+ puts "Extracting #{entry.name}"
160
+ raise 'File too large when extracted' if entry.size > MAX_SIZE
161
+
162
+ # Extract to file or directory based on name in the archive
163
+ entry.extract
164
+
165
+ # Read into memory
166
+ content = entry.get_input_stream.read
167
+ end
168
+
169
+ # Find specific entry
170
+ entry = zip_file.glob('*.csv').first
171
+ raise 'File too large when extracted' if entry.size > MAX_SIZE
172
+ puts entry.get_input_stream.read
173
+ end
174
+ ```
175
+
176
+ #### Notice about ::Zip::InputStream
177
+
178
+ `::Zip::InputStream` usable for fast reading zip file content because it not read Central directory.
179
+
180
+ But there is one exception when it is not working - General Purpose Flag Bit 3.
181
+
182
+ > If bit 3 (0x08) of the general-purpose flags field is set, then the CRC-32 and file sizes are not known when the header is written. The fields in the local header are filled with zero, and the CRC-32 and size are appended in a 12-byte structure (optionally preceded by a 4-byte signature) immediately after the compressed data
183
+
184
+ If `::Zip::InputStream` finds such entry in the zip archive it will raise an exception.
185
+
186
+ ### Password Protection (Experimental)
187
+
188
+ 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.:
189
+
190
+ ```ruby
191
+ Zip::OutputStream.write_buffer(::StringIO.new(''), Zip::TraditionalEncrypter.new('password')) do |out|
192
+ out.put_next_entry("my_file.txt")
193
+ out.write my_data
194
+ end.string
195
+ ```
196
+
197
+ This is an experimental feature and the interface for encryption may change in future versions.
198
+
199
+ ## Known issues
200
+
201
+ ### Modify docx file with rubyzip
202
+
203
+ Use `write_buffer` instead `open`. Thanks to @jondruse
204
+
205
+ ```ruby
206
+ buffer = Zip::OutputStream.write_buffer do |out|
207
+ @zip_file.entries.each do |e|
208
+ unless [DOCUMENT_FILE_PATH, RELS_FILE_PATH].include?(e.name)
209
+ out.put_next_entry(e.name)
210
+ out.write e.get_input_stream.read
211
+ end
212
+ end
72
213
 
73
- By default, rubyzip will not overwrite files if they already exist inside of the extracted path. To change this behavior, you may specify a configuration option like so:
214
+ out.put_next_entry(DOCUMENT_FILE_PATH)
215
+ out.write xml_doc.to_xml(:indent => 0).gsub("\n","")
216
+
217
+ out.put_next_entry(RELS_FILE_PATH)
218
+ out.write rels.to_xml(:indent => 0).gsub("\n","")
219
+ end
74
220
 
221
+ File.open(new_path, "wb") {|f| f.write(buffer.string) }
75
222
  ```
76
- Zip.options[:on_exists_proc] = true
223
+
224
+ ## Configuration
225
+
226
+ ### Existing Files
227
+
228
+ By default, rubyzip will not overwrite files if they already exist inside of the extracted path. To change this behavior, you may specify a configuration option like so:
229
+
230
+ ```ruby
231
+ Zip.on_exists_proc = true
77
232
  ```
78
233
 
79
234
  If you're using rubyzip with rails, consider placing this snippet of code in an initializer file such as `config/initializers/rubyzip.rb`
80
235
 
81
236
  Additionally, if you want to configure rubyzip to overwrite existing files while creating a .zip file, you can do so with the following:
82
237
 
238
+ ```ruby
239
+ Zip.continue_on_exists_proc = true
83
240
  ```
84
- Zip.options[:continue_on_exists_proc] = true
241
+
242
+ ### Non-ASCII Names
243
+
244
+ If you want to store non-english names and want to open them on Windows(pre 7) you need to set this option:
245
+
246
+ ```ruby
247
+ Zip.unicode_names = true
248
+ ```
249
+
250
+ Sometimes file names inside zip contain non-ASCII characters. If you can assume which encoding was used for such names and want to be able to find such entries using `find_entry` then you can force assumed encoding like so:
251
+
252
+ ```ruby
253
+ Zip.force_entry_names_encoding = 'UTF-8'
254
+ ```
255
+
256
+ Allowed encoding names are the same as accepted by `String#force_encoding`
257
+
258
+ ### Date Validation
259
+
260
+ Some zip files might have an invalid date format, which will raise a warning. You can hide this warning with the following setting:
261
+
262
+ ```ruby
263
+ Zip.warn_invalid_date = false
264
+ ```
265
+
266
+ ### Size Validation
267
+
268
+ **This setting defaults to `false` in rubyzip 1.3 for backward compatibility, but it will default to `true` in rubyzip 2.0.**
269
+
270
+ If you set
271
+ ```
272
+ Zip.validate_entry_sizes = true
273
+ ```
274
+ then `rubyzip`'s `extract` method checks that an entry's reported uncompressed size is not (significantly) smaller than its actual size. This is to help you protect your application against [zip bombs](https://en.wikipedia.org/wiki/Zip_bomb). Before `extract`ing an entry, you should check that its size is in the range you expect. For example, if your application supports processing up to 100 files at once, each up to 10MiB, your zip extraction code might look like:
275
+
276
+ ```ruby
277
+ MAX_FILE_SIZE = 10 * 1024**2 # 10MiB
278
+ MAX_FILES = 100
279
+ Zip::File.open('foo.zip') do |zip_file|
280
+ num_files = 0
281
+ zip_file.each do |entry|
282
+ num_files += 1 if entry.file?
283
+ raise 'Too many extracted files' if num_files > MAX_FILES
284
+ raise 'File too large when extracted' if entry.size > MAX_FILE_SIZE
285
+ entry.extract
286
+ end
287
+ end
288
+ ```
289
+
290
+ If you need to extract zip files that report incorrect uncompressed sizes and you really trust them not too be too large, you can disable this setting with
291
+ ```ruby
292
+ Zip.validate_entry_sizes = false
293
+ ```
294
+
295
+ Note that if you use the lower level `Zip::InputStream` interface, `rubyzip` does *not* check the entry `size`s. In this case, the caller is responsible for making sure it does not read more data than expected from the input stream.
296
+
297
+ ### Default Compression
298
+
299
+ You can set the default compression level like so:
300
+
301
+ ```ruby
302
+ Zip.default_compression = Zlib::DEFAULT_COMPRESSION
303
+ ```
304
+
305
+ It defaults to `Zlib::DEFAULT_COMPRESSION`. Possible values are `Zlib::BEST_COMPRESSION`, `Zlib::DEFAULT_COMPRESSION` and `Zlib::NO_COMPRESSION`
306
+
307
+ ### Zip64 Support
308
+
309
+ By default, Zip64 support is disabled for writing. To enable it do this:
310
+
311
+ ```ruby
312
+ Zip.write_zip64_support = true
313
+ ```
314
+
315
+ _NOTE_: If you will enable Zip64 writing then you will need zip extractor with Zip64 support to extract archive.
316
+
317
+ ### Block Form
318
+
319
+ You can set multiple settings at the same time by using a block:
320
+
321
+ ```ruby
322
+ Zip.setup do |c|
323
+ c.on_exists_proc = true
324
+ c.continue_on_exists_proc = true
325
+ c.unicode_names = true
326
+ c.default_compression = Zlib::BEST_COMPRESSION
327
+ end
85
328
  ```
86
329
 
87
330
  ## Developing
88
331
 
89
- To run tests you need run next commands:
332
+ To run the test you need to do this:
90
333
 
91
334
  ```
92
335
  bundle install
@@ -95,9 +338,9 @@ rake
95
338
 
96
339
  ## Website and Project Home
97
340
 
98
- http://github.com/aussiegeek/rubyzip
341
+ http://github.com/rubyzip/rubyzip
99
342
 
100
- http://rdoc.info/github/aussiegeek/rubyzip/master/frames
343
+ http://rdoc.info/github/rubyzip/rubyzip/master/frames
101
344
 
102
345
  ## Authors
103
346
 
@@ -113,5 +356,5 @@ extra-field support contributed by Tatsuki Sugiura (sugi at nemui.org)
113
356
 
114
357
  ## License
115
358
 
116
- rubyzip is distributed under the same license as ruby. See
359
+ Rubyzip is distributed under the same license as ruby. See
117
360
  http://www.ruby-lang.org/en/LICENSE.txt
data/Rakefile CHANGED
@@ -1,13 +1,18 @@
1
- require 'rubygems'
1
+ require 'bundler/gem_tasks'
2
2
  require 'rake/testtask'
3
3
 
4
- task :default => [:test]
4
+ task default: :test
5
5
 
6
6
  Rake::TestTask.new(:test) do |test|
7
- test.libs << File.join(File.dirname(__FILE__), 'lib')
8
- test.libs << File.join(File.dirname(__FILE__), 'test')
9
- test.pattern = File.join(File.dirname(__FILE__), 'test/alltests.rb')
7
+ test.libs << 'lib'
8
+ test.libs << 'test'
9
+ test.pattern = 'test/**/*_test.rb'
10
10
  test.verbose = true
11
- Dir.chdir File.join(File.dirname(__FILE__), 'test')
12
11
  end
13
12
 
13
+ # Rake::TestTask.new(:zip64_full_test) do |test|
14
+ # test.libs << File.join(File.dirname(__FILE__), 'lib')
15
+ # test.libs << File.join(File.dirname(__FILE__), 'test')
16
+ # test.pattern = File.join(File.dirname(__FILE__), 'test/zip64_full_test.rb')
17
+ # test.verbose = true
18
+ # end
data/TODO CHANGED
@@ -4,7 +4,6 @@
4
4
  * Suggestion: Add ZipFile/ZipInputStream example that demonstrates extracting all entries.
5
5
  * Suggestion: ZipFile#extract destination should default to "."
6
6
  * Suggestion: ZipEntry should have extract(), get_input_stream() methods etc
7
- * Suggestion: ZipInputStream/ZipOutputStream should accept an IO object in addition to a filename.
8
7
  * (is buffering used anywhere with write?)
9
8
  * Inflater.sysread should pass the buffer to produce_input.
10
9
  * Implement ZipFsDir.glob
@@ -0,0 +1,208 @@
1
+ module Zip
2
+ class CentralDirectory
3
+ include Enumerable
4
+
5
+ END_OF_CDS = 0x06054b50
6
+ ZIP64_END_OF_CDS = 0x06064b50
7
+ ZIP64_EOCD_LOCATOR = 0x07064b50
8
+ MAX_END_OF_CDS_SIZE = 65_536 + 18
9
+ STATIC_EOCD_SIZE = 22
10
+
11
+ attr_reader :comment
12
+
13
+ # Returns an Enumerable containing the entries.
14
+ def entries
15
+ @entry_set.entries
16
+ end
17
+
18
+ def initialize(entries = EntrySet.new, comment = '') #:nodoc:
19
+ super()
20
+ @entry_set = entries.kind_of?(EntrySet) ? entries : EntrySet.new(entries)
21
+ @comment = comment
22
+ end
23
+
24
+ def write_to_stream(io) #:nodoc:
25
+ cdir_offset = io.tell
26
+ @entry_set.each { |entry| entry.write_c_dir_entry(io) }
27
+ eocd_offset = io.tell
28
+ cdir_size = eocd_offset - cdir_offset
29
+ if ::Zip.write_zip64_support
30
+ need_zip64_eocd = cdir_offset > 0xFFFFFFFF || cdir_size > 0xFFFFFFFF || @entry_set.size > 0xFFFF
31
+ need_zip64_eocd ||= @entry_set.any? { |entry| entry.extra['Zip64'] }
32
+ if need_zip64_eocd
33
+ write_64_e_o_c_d(io, cdir_offset, cdir_size)
34
+ write_64_eocd_locator(io, eocd_offset)
35
+ end
36
+ end
37
+ write_e_o_c_d(io, cdir_offset, cdir_size)
38
+ end
39
+
40
+ def write_e_o_c_d(io, offset, cdir_size) #:nodoc:
41
+ tmp = [
42
+ END_OF_CDS,
43
+ 0, # @numberOfThisDisk
44
+ 0, # @numberOfDiskWithStartOfCDir
45
+ @entry_set ? [@entry_set.size, 0xFFFF].min : 0,
46
+ @entry_set ? [@entry_set.size, 0xFFFF].min : 0,
47
+ [cdir_size, 0xFFFFFFFF].min,
48
+ [offset, 0xFFFFFFFF].min,
49
+ @comment ? @comment.bytesize : 0
50
+ ]
51
+ io << tmp.pack('VvvvvVVv')
52
+ io << @comment
53
+ end
54
+
55
+ private :write_e_o_c_d
56
+
57
+ def write_64_e_o_c_d(io, offset, cdir_size) #:nodoc:
58
+ tmp = [
59
+ ZIP64_END_OF_CDS,
60
+ 44, # size of zip64 end of central directory record (excludes signature and field itself)
61
+ VERSION_MADE_BY,
62
+ VERSION_NEEDED_TO_EXTRACT_ZIP64,
63
+ 0, # @numberOfThisDisk
64
+ 0, # @numberOfDiskWithStartOfCDir
65
+ @entry_set ? @entry_set.size : 0, # number of entries on this disk
66
+ @entry_set ? @entry_set.size : 0, # number of entries total
67
+ cdir_size, # size of central directory
68
+ offset, # offset of start of central directory in its disk
69
+ ]
70
+ io << tmp.pack('VQ<vvVVQ<Q<Q<Q<')
71
+ end
72
+
73
+ private :write_64_e_o_c_d
74
+
75
+ def write_64_eocd_locator(io, zip64_eocd_offset)
76
+ tmp = [
77
+ ZIP64_EOCD_LOCATOR,
78
+ 0, # number of disk containing the start of zip64 eocd record
79
+ zip64_eocd_offset, # offset of the start of zip64 eocd record in its disk
80
+ 1 # total number of disks
81
+ ]
82
+ io << tmp.pack('VVQ<V')
83
+ end
84
+
85
+ private :write_64_eocd_locator
86
+
87
+ def read_64_e_o_c_d(buf) #:nodoc:
88
+ buf = get_64_e_o_c_d(buf)
89
+ @size_of_zip64_e_o_c_d = Entry.read_zip_64_long(buf)
90
+ @version_made_by = Entry.read_zip_short(buf)
91
+ @version_needed_for_extract = Entry.read_zip_short(buf)
92
+ @number_of_this_disk = Entry.read_zip_long(buf)
93
+ @number_of_disk_with_start_of_cdir = Entry.read_zip_long(buf)
94
+ @total_number_of_entries_in_cdir_on_this_disk = Entry.read_zip_64_long(buf)
95
+ @size = Entry.read_zip_64_long(buf)
96
+ @size_in_bytes = Entry.read_zip_64_long(buf)
97
+ @cdir_offset = Entry.read_zip_64_long(buf)
98
+ @zip_64_extensible = buf.slice!(0, buf.bytesize)
99
+ raise Error, 'Zip consistency problem while reading eocd structure' unless buf.empty?
100
+ end
101
+
102
+ def read_e_o_c_d(buf) #:nodoc:
103
+ buf = get_e_o_c_d(buf)
104
+ @number_of_this_disk = Entry.read_zip_short(buf)
105
+ @number_of_disk_with_start_of_cdir = Entry.read_zip_short(buf)
106
+ @total_number_of_entries_in_cdir_on_this_disk = Entry.read_zip_short(buf)
107
+ @size = Entry.read_zip_short(buf)
108
+ @size_in_bytes = Entry.read_zip_long(buf)
109
+ @cdir_offset = Entry.read_zip_long(buf)
110
+ comment_length = Entry.read_zip_short(buf)
111
+ @comment = if comment_length.to_i <= 0
112
+ buf.slice!(0, buf.size)
113
+ else
114
+ buf.read(comment_length)
115
+ end
116
+ raise Error, 'Zip consistency problem while reading eocd structure' unless buf.empty?
117
+ end
118
+
119
+ def read_central_directory_entries(io) #:nodoc:
120
+ begin
121
+ io.seek(@cdir_offset, IO::SEEK_SET)
122
+ rescue Errno::EINVAL
123
+ raise Error, 'Zip consistency problem while reading central directory entry'
124
+ end
125
+ @entry_set = EntrySet.new
126
+ @size.times do
127
+ @entry_set << Entry.read_c_dir_entry(io)
128
+ end
129
+ end
130
+
131
+ def read_from_stream(io) #:nodoc:
132
+ buf = start_buf(io)
133
+ if zip64_file?(buf)
134
+ read_64_e_o_c_d(buf)
135
+ else
136
+ read_e_o_c_d(buf)
137
+ end
138
+ read_central_directory_entries(io)
139
+ end
140
+
141
+ def get_e_o_c_d(buf) #:nodoc:
142
+ sig_index = buf.rindex([END_OF_CDS].pack('V'))
143
+ raise Error, 'Zip end of central directory signature not found' unless sig_index
144
+ buf = buf.slice!((sig_index + 4)..(buf.bytesize))
145
+
146
+ def buf.read(count)
147
+ slice!(0, count)
148
+ end
149
+
150
+ buf
151
+ end
152
+
153
+ def zip64_file?(buf)
154
+ buf.rindex([ZIP64_END_OF_CDS].pack('V')) && buf.rindex([ZIP64_EOCD_LOCATOR].pack('V'))
155
+ end
156
+
157
+ def start_buf(io)
158
+ begin
159
+ io.seek(-MAX_END_OF_CDS_SIZE, IO::SEEK_END)
160
+ rescue Errno::EINVAL
161
+ io.seek(0, IO::SEEK_SET)
162
+ end
163
+ io.read
164
+ end
165
+
166
+ def get_64_e_o_c_d(buf) #:nodoc:
167
+ zip_64_start = buf.rindex([ZIP64_END_OF_CDS].pack('V'))
168
+ raise Error, 'Zip64 end of central directory signature not found' unless zip_64_start
169
+ zip_64_locator = buf.rindex([ZIP64_EOCD_LOCATOR].pack('V'))
170
+ raise Error, 'Zip64 end of central directory signature locator not found' unless zip_64_locator
171
+ buf = buf.slice!((zip_64_start + 4)..zip_64_locator)
172
+
173
+ def buf.read(count)
174
+ slice!(0, count)
175
+ end
176
+
177
+ buf
178
+ end
179
+
180
+ # For iterating over the entries.
181
+ def each(&proc)
182
+ @entry_set.each(&proc)
183
+ end
184
+
185
+ # Returns the number of entries in the central directory (and
186
+ # consequently in the zip archive).
187
+ def size
188
+ @entry_set.size
189
+ end
190
+
191
+ def self.read_from_stream(io) #:nodoc:
192
+ cdir = new
193
+ cdir.read_from_stream(io)
194
+ return cdir
195
+ rescue Error
196
+ return nil
197
+ end
198
+
199
+ def ==(other) #:nodoc:
200
+ return false unless other.kind_of?(CentralDirectory)
201
+ @entry_set.entries.sort == other.entries.sort && comment == other.comment
202
+ end
203
+ end
204
+ end
205
+
206
+ # Copyright (C) 2002, 2003 Thomas Sondergaard
207
+ # rubyzip is free software; you can redistribute it and/or
208
+ # modify it under the terms of the ruby license.
@@ -1,7 +1,6 @@
1
1
  module Zip
2
2
  class Compressor #:nodoc:all
3
- def finish
4
- end
3
+ def finish; end
5
4
  end
6
5
  end
7
6