archive-zip-0.12.0/0000755000004100000410000000000013513326431014044 5ustar www-datawww-dataarchive-zip-0.12.0/README.md0000644000004100000410000001720313513326431015326 0ustar www-datawww-data# Archive::Zip - ZIP Archival Made Easy Simple, extensible, pure Ruby ZIP archive support. Basic archive creation and extraction can be handled using only a few methods. More complex operations involving the manipulation of existing archives in place (adding, removing, and modifying entries) are also possible with a little more work. Even adding advanced features such as new compression codecs are supported with a moderate amount of effort. ## LINKS * Homepage :: http://github.com/javanthropus/archive-zip * Documentation :: http://rdoc.info/gems/archive-zip/frames * Source :: http://github.com/javanthropus/archive-zip ## DESCRIPTION Archive::Zip provides a simple Ruby-esque interface to creating, extracting, and updating ZIP archives. This implementation is 100% Ruby and loosely modeled on the archive creation and extraction capabilities of InfoZip's zip and unzip tools. ## FEATURES * 100% native Ruby. (Well, almost... depends on zlib.) * Archive creation and extraction is supported with only a few lines of code. * Archives can be updated "in place" or dumped out to other files or pipes. * Files, symlinks, and directories are supported within archives. * Unix permission/mode bits are supported. * Unix user and group ownerships are supported. * Unix last accessed and last modified times are supported. * Entry extension (AKA extra field) implementations can be added on the fly. * Unknown entry extension types are preserved during archive processing. * The Deflate and Store compression codecs are supported out of the box. * More compression codecs can be added on the fly. * Traditional (weak) encryption is supported out of the box. ## KNOWN BUGS/LIMITATIONS * More testcases are needed. * All file entries are archived and extracted in binary mode. No attempt is made to normalize text files to the line ending convention of any target system. * Hard links and device files are not currently supported within archives. * Reading archives from non-seekable IO, such as pipes and sockets, is not supported. * MSDOS permission attributes are not supported. * Strong encryption is not supported. * Zip64 is not supported. * Digital signatures are not supported. ## SYNOPSIS More examples can be found in the `examples` directory of the source distribution. Create a few archives: ```ruby require 'archive/zip' # Add a_directory and its contents to example1.zip. Archive::Zip.archive('example1.zip', 'a_directory') # Add the contents of a_directory to example2.zip. Archive::Zip.archive('example2.zip', 'a_directory/.') # Add a_file and a_directory and its contents to example3.zip. Archive::Zip.archive('example3.zip', ['a_directory', 'a_file']) # Add only the files and symlinks contained in a_directory under the path # a/b/c/a_directory in example4.zip. Archive::Zip.archive( 'example4.zip', 'a_directory', :directories => false, :path_prefix => 'a/b/c' ) # Add the contents of a_directory to example5.zip and encrypt Ruby source # files. require 'archive/zip/codec/null_encryption' require 'archive/zip/codec/traditional_encryption' Archive::Zip.archive( 'example5.zip', 'a_directory/.', :encryption_codec => lambda do |entry| if entry.file? and entry.zip_path =~ /\.rb$/ then Archive::Zip::Codec::TraditionalEncryption else Archive::Zip::Codec::NullEncryption end end, :password => 'seakrit' ) # Create a new archive which will be written to a pipe. # Assume $stdout is the write end a pipe. # (ruby example.rb | cat >example.zip) Archive::Zip.open($stdout, :w) do |z| z.archive('a_directory') end ``` Now extract those archives: ```ruby require 'archive/zip' # Extract example1.zip to a_destination. Archive::Zip.extract('example1.zip', 'a_destination') # Extract example2.zip to a_destination, skipping directory entries. Archive::Zip.extract( 'example2.zip', 'a_destination', :directories => false ) # Extract example3.zip to a_destination, skipping symlinks. Archive::Zip.extract( 'example3.zip', 'a_destination', :symlinks => false ) # Extract example4.zip to a_destination, skipping entries for which files # already exist but are newer or for which files do not exist at all. Archive::Zip.extract( 'example4.zip', 'a_destination', :create => false, :overwrite => :older ) # Extract example5.zip to a_destination, decrypting the contents. Archive::Zip.extract( 'example5.zip', 'a_destination', :password => 'seakrit' ) ``` ## FUTURE WORK ITEMS (in no particular order): * Add test cases for all classes. * Add support for using non-seekable IO objects as archive sources. * Add support for 0x5855 and 0x7855 extra fields. ## REQUIREMENTS * io-like ## INSTALL Download the GEM file and install it with: $ gem install archive-zip-VERSION.gem or directly with: $ gem install archive-zip Removal is the same in either case: $ gem uninstall archive-zip ## DEVELOPERS After checking out the source, run: $ bundle install $ bundle exec rake test yard This will install all dependencies, run the tests/specs, and generate the documentation. ## AUTHORS and CONTRIBUTORS Thanks to all contributors. Without your help this project would not exist. * Jeremy Bopp :: jeremy@bopp.net * Akira Matsuda :: ronnie@dio.jp * Tatsuya Sato :: tatsuya.b.sato@rakuten.com * Kouhei Sutou :: kou@clear-code.com ## CONTRIBUTING Contributions for bug fixes, documentation, extensions, tests, etc. are encouraged. 1. Clone the repository. 2. Fix a bug or add a feature. 3. Add tests for the fix or feature. 4. Make a pull request. ### CODING STYLE The following points are not necessarily set in stone but should rather be used as a good guideline. Consistency is the goal of coding style, and changes will be more easily accepted if they are consistent with the rest of the code. * **File Encoding** * UTF-8 * **Indentation** * Two spaces; no tabs * **Line length** * Limit lines to a maximum of 80 characters * **Comments** * Document classes, attributes, methods, and code * **Method Calls with Arguments** * Use `a_method(arg, arg, etc)`; **not** `a_method( arg, arg, etc )`, `a_method arg, arg, etc`, or any other variation * **Method Calls without Arguments** * Use `a_method`; avoid parenthesis * **String Literals** * Use single quotes by default * Use double quotes when interpolation is necessary * Use `%{...}` and similar when embedding the quoting character is cumbersome * **Blocks** * `do ... end` for multi-line blocks and `{ ... }` for single-line blocks * **Boolean Operators** * Use `&&` and `||` for boolean tests; avoid `and` and `or` * **In General** * Try to follow the flow and style of the rest of the code ## LICENSE ``` (The MIT License) Copyright (c) 2019 Jeremy Bopp Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ``` archive-zip-0.12.0/spec/0000755000004100000410000000000013513326431014776 5ustar www-datawww-dataarchive-zip-0.12.0/spec/binary_stringio/0000755000004100000410000000000013513326431020200 5ustar www-datawww-dataarchive-zip-0.12.0/spec/binary_stringio/set_encoding_spec.rb0000644000004100000410000000064113513326431024201 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require 'archive/support/binary_stringio' describe "BinaryStringIO#set_encoding" do it "raises an exception when called" do unless Object.const_defined?(:Encoding) skip("Encoding methods are not supported on current Ruby (#{RUBY_DESCRIPTION})") end lambda do BinaryStringIO.new.set_encoding('utf-8') end.must_raise RuntimeError end end archive-zip-0.12.0/spec/binary_stringio/new_spec.rb0000644000004100000410000000216213513326431022331 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require 'archive/support/binary_stringio' describe "BinaryStringIO.new" do it "returns a new instance" do io = BinaryStringIO.new io.must_be_instance_of BinaryStringIO io.close end it "creates a decendent of StringIO" do io = BinaryStringIO.new io.must_be_kind_of StringIO io.close end # TODO: # This is lame. Break this out as augmentation for the "returns a new # instance" test. it "takes the same arguments as StringIO.new" do BinaryStringIO.new.must_be_instance_of BinaryStringIO BinaryStringIO.new('').must_be_instance_of BinaryStringIO BinaryStringIO.new('', 'r').must_be_instance_of BinaryStringIO BinaryStringIO.new('', 'w').must_be_instance_of BinaryStringIO lambda { BinaryStringIO.new('', 'w', 42) }.must_raise ArgumentError end it "sets the external encoding to binary" do unless Object.const_defined?(:Encoding) skip("Encoding methods are not supported on current Ruby (#{RUBY_DESCRIPTION})") end io = BinaryStringIO.new io.external_encoding.must_equal Encoding::ASCII_8BIT end end archive-zip-0.12.0/spec/archive/0000755000004100000410000000000013513326431016417 5ustar www-datawww-dataarchive-zip-0.12.0/spec/archive/dos_time_spec.rb0000644000004100000410000000657313513326431021574 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require 'archive/support/time' describe 'Archive::DOSTime.new' do let(:epoc) { 0b0000000_0001_00001_00000_000000_00000 } let(:end_times) { 0b1110111_1100_11111_11000_111011_11101 } it 'uses the current time when no structure is given' do now = Time.now.localtime dos_time = Archive::DOSTime.new.to_time now.year.must_be_close_to(dos_time.year, 1) now.month.must_be_close_to(dos_time.month, 1) now.day.must_be_close_to(dos_time.day, 1) now.hour.must_be_close_to(dos_time.hour, 1) now.min.must_be_close_to(dos_time.min, 1) now.sec.must_be_close_to(dos_time.sec, 3) end it 'accepts valid Integer structures' do Archive::DOSTime.new(epoc) Archive::DOSTime.new(end_times) end it 'accepts valid String structures' do Archive::DOSTime.new([epoc].pack('V')) Archive::DOSTime.new([end_times].pack('V')) end it 'rejects invalid Integer structures' do # Second must not be greater than 29. proc { Archive::DOSTime.new(epoc | 0b0000000_0000_00000_00000_000000_11110) }.must_raise(ArgumentError) # Minute must not be greater than 59. proc { Archive::DOSTime.new(epoc | 0b0000000_0000_00000_00000_111100_00000) }.must_raise(ArgumentError) # Hour must not be greater than 24. proc { Archive::DOSTime.new(epoc | 0b0000000_0000_00000_11001_000000_00000) }.must_raise(ArgumentError) # Day must not be zero. proc { Archive::DOSTime.new(epoc & 0b1111111_1111_00000_11111_111111_11111) }.must_raise(ArgumentError) # Month must not be zero. proc { Archive::DOSTime.new(epoc & 0b1111111_0000_11111_11111_111111_11111) }.must_raise(ArgumentError) # Month must not be greater than 12. proc { Archive::DOSTime.new(epoc | 0b0000000_1101_00000_00000_000000_00000) }.must_raise(ArgumentError) # Year must not be greater than 119. proc { Archive::DOSTime.new(epoc | 0b1111000_0000_00000_00000_000000_00000) }.must_raise(ArgumentError) end it 'rejects invalid String structures' do # Second must not be greater than 29. proc { packed = [epoc | 0b0000000_0000_00000_00000_000000_11110].pack('V') Archive::DOSTime.new(packed) }.must_raise(ArgumentError) # Minute must not be greater than 59. proc { packed = [epoc | 0b0000000_0000_00000_00000_111100_00000].pack('V') Archive::DOSTime.new(packed) }.must_raise(ArgumentError) # Hour must not be greater than 24. proc { packed = [epoc | 0b0000000_0000_00000_11001_000000_00000].pack('V') Archive::DOSTime.new(packed) }.must_raise(ArgumentError) # Day must not be zero. proc { packed = [epoc & 0b1111111_1111_00000_11111_111111_11111].pack('V') Archive::DOSTime.new(packed) }.must_raise(ArgumentError) # Month must not be zero. proc { packed = [epoc & 0b1111111_0000_11111_11111_111111_11111].pack('V') Archive::DOSTime.new(packed) }.must_raise(ArgumentError) # Month must not be greater than 12. proc { packed = [epoc | 0b0000000_1101_00000_00000_000000_00000].pack('V') Archive::DOSTime.new(packed) }.must_raise(ArgumentError) # Year must not be greater than 119. proc { packed = [epoc | 0b1111000_0000_00000_00000_000000_00000].pack('V') Archive::DOSTime.new(packed) }.must_raise(ArgumentError) end end archive-zip-0.12.0/spec/archive/zip/0000755000004100000410000000000013513326431017221 5ustar www-datawww-dataarchive-zip-0.12.0/spec/archive/zip/codec/0000755000004100000410000000000013513326431020276 5ustar www-datawww-dataarchive-zip-0.12.0/spec/archive/zip/codec/store/0000755000004100000410000000000013513326431021432 5ustar www-datawww-dataarchive-zip-0.12.0/spec/archive/zip/codec/store/fixtures/0000755000004100000410000000000013513326431023303 5ustar www-datawww-dataarchive-zip-0.12.0/spec/archive/zip/codec/store/fixtures/classes.rb0000644000004100000410000000040113513326431025260 0ustar www-datawww-data# encoding: UTF-8 class StoreSpecs class << self def test_data File.open(File.join(File.dirname(__FILE__), 'raw_file.txt'), 'rb') do |f| block_given? ? yield(f) : f.read end end alias :compressed_data :test_data end end archive-zip-0.12.0/spec/archive/zip/codec/store/fixtures/raw_file.txt0000644000004100000410000000035313513326431025635 0ustar www-datawww-datatest This is a file with test data. The quick brown fox jumps over the lazy dog. Mary had a little lamb Whose fleece was white as snow And everywhere that Mary went The lamb was sure to go She sells sea shells down by the sea shore. archive-zip-0.12.0/spec/archive/zip/codec/store/decompress/0000755000004100000410000000000013513326431023576 5ustar www-datawww-dataarchive-zip-0.12.0/spec/archive/zip/codec/store/decompress/tell_spec.rb0000644000004100000410000000104713513326431026077 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/store' describe "Archive::Zip::Codec::Store::Decompress#tell" do it "returns the current position of the stream" do StoreSpecs.compressed_data do |cd| Archive::Zip::Codec::Store::Decompress.open(cd) do |d| d.tell.must_equal(0) d.read(4) d.tell.must_equal(4) d.read d.tell.must_equal(235) d.rewind d.tell.must_equal(0) end end end end archive-zip-0.12.0/spec/archive/zip/codec/store/decompress/rewind_spec.rb0000644000004100000410000000143413513326431026427 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/store' describe "Archive::Zip::Codec::Store::Decompress#rewind" do it "can rewind the stream when the delegate responds to rewind" do StoreSpecs.compressed_data do |cd| Archive::Zip::Codec::Store::Decompress.open(cd) do |d| d.read(4) d.rewind d.read.must_equal(StoreSpecs.test_data) end end end it "raises Errno::EINVAL when attempting to rewind the stream when the delegate does not respond to rewind" do delegate = MiniTest::Mock.new delegate.expect(:close, nil) Archive::Zip::Codec::Store::Decompress.open(delegate) do |d| lambda { d.rewind }.must_raise(Errno::EINVAL) end end end archive-zip-0.12.0/spec/archive/zip/codec/store/decompress/open_spec.rb0000644000004100000410000000161713513326431026103 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/store' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::Store::Decompress.open" do it "returns a new instance when run without a block" do d = Archive::Zip::Codec::Store::Decompress.open(BinaryStringIO.new) d.must_be_instance_of(Archive::Zip::Codec::Store::Decompress) d.close end it "executes a block with a new instance as an argument" do Archive::Zip::Codec::Store::Decompress.open(BinaryStringIO.new) do |decompressor| decompressor.must_be_instance_of(Archive::Zip::Codec::Store::Decompress) end end it "closes the object after executing a block" do d = Archive::Zip::Codec::Store::Decompress.open(BinaryStringIO.new) do |decompressor| decompressor end d.closed?.must_equal(true) end end archive-zip-0.12.0/spec/archive/zip/codec/store/decompress/data_descriptor_spec.rb0000644000004100000410000000437713513326431030317 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/support/zlib' require 'archive/zip/codec/store' describe "Archive::Zip::Codec::Store::Decompress#data_descriptor" do it "is an instance of Archive::Zip::DataDescriptor" do StoreSpecs.compressed_data do |cd| closed_decompressor = Archive::Zip::Codec::Store::Decompress.open( cd ) do |decompressor| decompressor.read decompressor.data_descriptor.must_be_instance_of( Archive::Zip::DataDescriptor ) decompressor end closed_decompressor.data_descriptor.must_be_instance_of( Archive::Zip::DataDescriptor ) end end it "has a crc32 attribute containing the CRC32 checksum" do crc32 = Zlib.crc32(StoreSpecs.test_data) StoreSpecs.compressed_data do |cd| closed_decompressor = Archive::Zip::Codec::Store::Decompress.open( cd ) do |decompressor| decompressor.read decompressor.data_descriptor.crc32.must_equal(crc32) decompressor end closed_decompressor.data_descriptor.crc32.must_equal(crc32) end end it "has a compressed_size attribute containing the size of the compressed data" do compressed_size = StoreSpecs.compressed_data.size StoreSpecs.compressed_data do |cd| closed_decompressor = Archive::Zip::Codec::Store::Decompress.open( cd ) do |decompressor| decompressor.read decompressor.data_descriptor.compressed_size.must_equal(compressed_size) decompressor end closed_decompressor.data_descriptor.compressed_size.must_equal( compressed_size ) end end it "has an uncompressed_size attribute containing the size of the input data" do uncompressed_size = StoreSpecs.test_data.size StoreSpecs.compressed_data do |cd| closed_decompressor = Archive::Zip::Codec::Store::Decompress.open( cd ) do |decompressor| decompressor.read decompressor.data_descriptor.uncompressed_size.must_equal( uncompressed_size ) decompressor end closed_decompressor.data_descriptor.uncompressed_size.must_equal( uncompressed_size ) end end end archive-zip-0.12.0/spec/archive/zip/codec/store/decompress/seek_spec.rb0000644000004100000410000000413013513326431026062 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/store' describe "Archive::Zip::Codec::Store::Decompress#seek" do it "can seek to the beginning of the stream when the delegate responds to rewind" do StoreSpecs.compressed_data do |cd| Archive::Zip::Codec::Store::Decompress.open(cd) do |d| d.read(4) d.seek(0).must_equal(0) end end end it "raises Errno::EINVAL when attempting to seek to the beginning of the stream when the delegate does not respond to rewind" do delegate = MiniTest::Mock.new delegate.expect(:close, nil) Archive::Zip::Codec::Store::Decompress.open(delegate) do |d| lambda { d.seek(0) }.must_raise(Errno::EINVAL) end end it "raises Errno::EINVAL when seeking forward or backward from the current position of the stream" do StoreSpecs.compressed_data do |cd| Archive::Zip::Codec::Store::Decompress.open(cd) do |d| # Disable read buffering to avoid some seeking optimizations implemented # by IO::Like which allow seeking forward within the buffer. d.fill_size = 0 d.read(4) lambda { d.seek(1, IO::SEEK_CUR) }.must_raise(Errno::EINVAL) lambda { d.seek(-1, IO::SEEK_CUR) }.must_raise(Errno::EINVAL) end end end it "raises Errno::EINVAL when seeking a non-zero offset relative to the beginning of the stream" do StoreSpecs.compressed_data do |cd| Archive::Zip::Codec::Store::Decompress.open(cd) do |d| lambda { d.seek(-1, IO::SEEK_SET) }.must_raise(Errno::EINVAL) lambda { d.seek(1, IO::SEEK_SET) }.must_raise(Errno::EINVAL) end end end it "raises Errno::EINVAL when seeking relative to the end of the stream" do StoreSpecs.compressed_data do |cd| Archive::Zip::Codec::Store::Decompress.open(cd) do |d| lambda { d.seek(0, IO::SEEK_END) }.must_raise(Errno::EINVAL) lambda { d.seek(-1, IO::SEEK_END) }.must_raise(Errno::EINVAL) lambda { d.seek(1, IO::SEEK_END) }.must_raise(Errno::EINVAL) end end end end archive-zip-0.12.0/spec/archive/zip/codec/store/decompress/read_spec.rb0000644000004100000410000000123613513326431026052 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/store' describe "Archive::Zip::Codec::Store::Decompress#read" do it "calls the read method of the delegate" do delegate = MiniTest::Mock.new delegate.expect(:read, nil, [Integer]) delegate.expect(:close, nil) Archive::Zip::Codec::Store::Decompress.open(delegate) do |d| d.read end end it "passes data through unmodified" do StoreSpecs.compressed_data do |cd| Archive::Zip::Codec::Store::Decompress.open(cd) do |d| d.read.must_equal(StoreSpecs.test_data) end end end end archive-zip-0.12.0/spec/archive/zip/codec/store/decompress/close_spec.rb0000644000004100000410000000162113513326431026242 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/store' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::Store::Decompress#close" do it "closes the stream" do c = Archive::Zip::Codec::Store::Decompress.new(BinaryStringIO.new) c.close c.closed?.must_equal(true) end it "closes the delegate stream by default" do delegate = MiniTest::Mock.new delegate.expect(:close, nil) c = Archive::Zip::Codec::Store::Decompress.new(delegate) c.close end it "optionally leaves the delegate stream open" do delegate = MiniTest::Mock.new delegate.expect(:close, nil) c = Archive::Zip::Codec::Store::Decompress.new(delegate) c.close(true) delegate = MiniTest::Mock.new c = Archive::Zip::Codec::Store::Decompress.new(delegate) c.close(false) end end archive-zip-0.12.0/spec/archive/zip/codec/store/decompress/new_spec.rb0000644000004100000410000000066313513326431025733 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/store' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::Store::Decompress.new" do it "returns a new instance" do d = Archive::Zip::Codec::Store::Decompress.new(BinaryStringIO.new) d.must_be_instance_of(Archive::Zip::Codec::Store::Decompress) d.close end end archive-zip-0.12.0/spec/archive/zip/codec/store/compress/0000755000004100000410000000000013513326431023265 5ustar www-datawww-dataarchive-zip-0.12.0/spec/archive/zip/codec/store/compress/tell_spec.rb0000644000004100000410000000144113513326431025564 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/store' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::Store::Compress#tell" do it "returns the current position of the stream" do sio = BinaryStringIO.new Archive::Zip::Codec::Store::Compress.open(sio) do |c| c.tell.must_equal(0) c.write('test1') c.tell.must_equal(5) c.write('test2') c.tell.must_equal(10) c.rewind c.tell.must_equal(0) end end it "raises IOError on closed stream" do delegate = MiniTest::Mock.new delegate.expect(:close, nil) lambda do Archive::Zip::Codec::Store::Compress.open(delegate) { |c| c }.tell end.must_raise(IOError) end end archive-zip-0.12.0/spec/archive/zip/codec/store/compress/rewind_spec.rb0000644000004100000410000000152713513326431026121 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/store' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::Store::Compress#rewind" do it "can rewind the stream when the delegate responds to rewind" do sio = BinaryStringIO.new Archive::Zip::Codec::Store::Compress.open(sio) do |c| c.write('test') c.rewind c.write(StoreSpecs.test_data) end sio.string.must_equal(StoreSpecs.compressed_data) end it "raises Errno::EINVAL when attempting to rewind the stream when the delegate does not respond to rewind" do delegate = MiniTest::Mock.new delegate.expect(:close, nil) Archive::Zip::Codec::Store::Compress.open(delegate) do |c| lambda { c.rewind }.must_raise(Errno::EINVAL) end end end archive-zip-0.12.0/spec/archive/zip/codec/store/compress/write_spec.rb0000644000004100000410000000152013513326431025754 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/store' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::Store::Compress#write" do it "calls the write method of the delegate" do delegate = MiniTest::Mock.new delegate.expect( :write, StoreSpecs.compressed_data.size, [StoreSpecs.compressed_data] ) delegate.expect(:close, nil) Archive::Zip::Codec::Store::Compress.open(delegate) do |c| c.write(StoreSpecs.test_data) end end it "passes data through unmodified" do compressed_data = BinaryStringIO.new Archive::Zip::Codec::Store::Compress.open(compressed_data) do |c| c.write(StoreSpecs.test_data) end compressed_data.string.must_equal(StoreSpecs.compressed_data) end end archive-zip-0.12.0/spec/archive/zip/codec/store/compress/open_spec.rb0000644000004100000410000000157313513326431025573 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/store' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::Store::Compress.open" do it "returns a new instance when run without a block" do c = Archive::Zip::Codec::Store::Compress.open(BinaryStringIO.new) c.must_be_instance_of(Archive::Zip::Codec::Store::Compress) c.close end it "executes a block with a new instance as an argument" do Archive::Zip::Codec::Store::Compress.open(BinaryStringIO.new) do |compressor| compressor.must_be_instance_of(Archive::Zip::Codec::Store::Compress) end end it "closes the object after executing a block" do c = Archive::Zip::Codec::Store::Compress.open(BinaryStringIO.new) do |compressor| compressor end c.closed?.must_equal(true) end end archive-zip-0.12.0/spec/archive/zip/codec/store/compress/data_descriptor_spec.rb0000644000004100000410000000456413513326431030004 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/support/zlib' require 'archive/zip/codec/store' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::Store::Compress#data_descriptor" do it "is an instance of Archive::Zip::DataDescriptor" do test_data = StoreSpecs.test_data compressed_data = BinaryStringIO.new closed_compressor = Archive::Zip::Codec::Store::Compress.open( compressed_data ) do |compressor| compressor.write(test_data) compressor.flush compressor.data_descriptor.must_be_instance_of( Archive::Zip::DataDescriptor ) compressor end closed_compressor.data_descriptor.must_be_instance_of( Archive::Zip::DataDescriptor ) end it "has a crc32 attribute containing the CRC32 checksum" do test_data = StoreSpecs.test_data compressed_data = BinaryStringIO.new closed_compressor = Archive::Zip::Codec::Store::Compress.open( compressed_data ) do |compressor| compressor.write(test_data) compressor.flush compressor.data_descriptor.crc32.must_equal(Zlib.crc32(test_data)) compressor end closed_compressor.data_descriptor.crc32.must_equal(Zlib.crc32(test_data)) end it "has a compressed_size attribute containing the size of the compressed data" do test_data = StoreSpecs.test_data compressed_data = BinaryStringIO.new closed_compressor = Archive::Zip::Codec::Store::Compress.open( compressed_data ) do |compressor| compressor.write(test_data) compressor.flush compressor.data_descriptor.compressed_size.must_equal( compressed_data.string.size ) compressor end closed_compressor.data_descriptor.compressed_size.must_equal( compressed_data.string.size ) end it "has an uncompressed_size attribute containing the size of the input data" do test_data = StoreSpecs.test_data compressed_data = BinaryStringIO.new closed_compressor = Archive::Zip::Codec::Store::Compress.open( compressed_data ) do |compressor| compressor.write(test_data) compressor.flush compressor.data_descriptor.uncompressed_size.must_equal(test_data.size) compressor end closed_compressor.data_descriptor.uncompressed_size.must_equal( test_data.size ) end end archive-zip-0.12.0/spec/archive/zip/codec/store/compress/seek_spec.rb0000644000004100000410000000371313513326431025557 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/store' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::Store::Compress#seek" do it "can seek to the beginning of the stream when the delegate responds to rewind" do compressed_data = BinaryStringIO.new Archive::Zip::Codec::Store::Compress.open(compressed_data) do |c| c.write('test') c.seek(0).must_equal(0) end end it "raises Errno::EINVAL when attempting to seek to the beginning of the stream when the delegate does not respond to rewind" do delegate = MiniTest::Mock.new delegate.expect(:close, nil) Archive::Zip::Codec::Store::Compress.open(delegate) do |c| lambda { c.seek(0) }.must_raise(Errno::EINVAL) end end it "raises Errno::EINVAL when seeking forward or backward from the current position of the stream" do compressed_data = BinaryStringIO.new Archive::Zip::Codec::Store::Compress.open(compressed_data) do |c| c.write('test') lambda { c.seek(1, IO::SEEK_CUR) }.must_raise(Errno::EINVAL) lambda { c.seek(-1, IO::SEEK_CUR) }.must_raise(Errno::EINVAL) end end it "raises Errno::EINVAL when seeking a non-zero offset relative to the beginning of the stream" do compressed_data = BinaryStringIO.new Archive::Zip::Codec::Store::Compress.open(compressed_data) do |c| lambda { c.seek(-1, IO::SEEK_SET) }.must_raise(Errno::EINVAL) lambda { c.seek(1, IO::SEEK_SET) }.must_raise(Errno::EINVAL) end end it "raises Errno::EINVAL when seeking relative to the end of the stream" do compressed_data = BinaryStringIO.new Archive::Zip::Codec::Store::Compress.open(compressed_data) do |c| lambda { c.seek(0, IO::SEEK_END) }.must_raise(Errno::EINVAL) lambda { c.seek(-1, IO::SEEK_END) }.must_raise(Errno::EINVAL) lambda { c.seek(1, IO::SEEK_END) }.must_raise(Errno::EINVAL) end end end archive-zip-0.12.0/spec/archive/zip/codec/store/compress/close_spec.rb0000644000004100000410000000160713513326431025735 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/store' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::Store::Compress#close" do it "closes the stream" do c = Archive::Zip::Codec::Store::Compress.new(BinaryStringIO.new) c.close c.closed?.must_equal(true) end it "closes the delegate stream by default" do delegate = MiniTest::Mock.new delegate.expect(:close, nil) c = Archive::Zip::Codec::Store::Compress.new(delegate) c.close end it "optionally leaves the delegate stream open" do delegate = MiniTest::Mock.new delegate.expect(:close, nil) c = Archive::Zip::Codec::Store::Compress.new(delegate) c.close(true) delegate = MiniTest::Mock.new c = Archive::Zip::Codec::Store::Compress.new(delegate) c.close(false) end end archive-zip-0.12.0/spec/archive/zip/codec/store/compress/new_spec.rb0000644000004100000410000000065513513326431025423 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/store' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::Store::Compress.new" do it "returns a new instance" do c = Archive::Zip::Codec::Store::Compress.new(BinaryStringIO.new) c.must_be_instance_of(Archive::Zip::Codec::Store::Compress) c.close end end archive-zip-0.12.0/spec/archive/zip/codec/null_encryption/0000755000004100000410000000000013513326431023522 5ustar www-datawww-dataarchive-zip-0.12.0/spec/archive/zip/codec/null_encryption/fixtures/0000755000004100000410000000000013513326431025373 5ustar www-datawww-dataarchive-zip-0.12.0/spec/archive/zip/codec/null_encryption/fixtures/classes.rb0000644000004100000410000000041113513326431027351 0ustar www-datawww-data# encoding: UTF-8 class NullEncryptionSpecs class << self def test_data File.open(File.join(File.dirname(__FILE__), 'raw_file.txt'), 'rb') do |f| block_given? ? yield(f) : f.read end end alias :encrypted_data :test_data end end archive-zip-0.12.0/spec/archive/zip/codec/null_encryption/fixtures/raw_file.txt0000644000004100000410000000035313513326431027725 0ustar www-datawww-datatest This is a file with test data. The quick brown fox jumps over the lazy dog. Mary had a little lamb Whose fleece was white as snow And everywhere that Mary went The lamb was sure to go She sells sea shells down by the sea shore. archive-zip-0.12.0/spec/archive/zip/codec/null_encryption/encrypt/0000755000004100000410000000000013513326431025206 5ustar www-datawww-dataarchive-zip-0.12.0/spec/archive/zip/codec/null_encryption/encrypt/tell_spec.rb0000644000004100000410000000150313513326431027504 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/null_encryption' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::NullEncryption::Encrypt#tell" do it "returns the current position of the stream" do sio = BinaryStringIO.new Archive::Zip::Codec::NullEncryption::Encrypt.open(sio) do |e| e.tell.must_equal(0) e.write('test1') e.tell.must_equal(5) e.write('test2') e.tell.must_equal(10) e.rewind e.tell.must_equal(0) end end it "raises IOError on closed stream" do delegate = MiniTest::Mock.new delegate.expect(:close, nil) lambda do Archive::Zip::Codec::NullEncryption::Encrypt.open(delegate) { |e| e }.tell end.must_raise(IOError) end end archive-zip-0.12.0/spec/archive/zip/codec/null_encryption/encrypt/rewind_spec.rb0000644000004100000410000000165313513326431030042 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/null_encryption' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::NullEncryption::Encrypt#rewind" do it "can rewind the stream when the delegate responds to rewind" do encrypted_data = BinaryStringIO.new Archive::Zip::Codec::NullEncryption::Encrypt.open(encrypted_data) do |e| e.write('test') e.rewind e.write(NullEncryptionSpecs.test_data) end encrypted_data.string.must_equal(NullEncryptionSpecs.encrypted_data) end it "raises Errno::EINVAL when attempting to rewind the stream when the delegate does not respond to rewind" do delegate = MiniTest::Mock.new delegate.expect(:close, nil) Archive::Zip::Codec::NullEncryption::Encrypt.open(delegate) do |e| lambda { e.rewind }.must_raise(Errno::EINVAL) end end end archive-zip-0.12.0/spec/archive/zip/codec/null_encryption/encrypt/write_spec.rb0000644000004100000410000000164513513326431027705 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/null_encryption' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::NullEncryption::Encrypt#write" do it "calls the write method of the delegate" do delegate = MiniTest::Mock.new delegate.expect( :write, NullEncryptionSpecs.encrypted_data.size, [NullEncryptionSpecs.encrypted_data] ) delegate.expect(:close, nil) Archive::Zip::Codec::NullEncryption::Encrypt.open(delegate) do |e| e.write(NullEncryptionSpecs.test_data) end end it "passes data through unmodified" do encrypted_data = BinaryStringIO.new Archive::Zip::Codec::NullEncryption::Encrypt.open(encrypted_data) do |e| e.write(NullEncryptionSpecs.test_data) end encrypted_data.string.must_equal(NullEncryptionSpecs.encrypted_data) end end archive-zip-0.12.0/spec/archive/zip/codec/null_encryption/encrypt/open_spec.rb0000644000004100000410000000170113513326431027505 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/null_encryption' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::NullEncryption::Encrypt.open" do it "returns a new instance when run without a block" do e = Archive::Zip::Codec::NullEncryption::Encrypt.open(BinaryStringIO.new) e.must_be_instance_of(Archive::Zip::Codec::NullEncryption::Encrypt) e.close end it "executes a block with a new instance as an argument" do Archive::Zip::Codec::NullEncryption::Encrypt.open(BinaryStringIO.new) do |encryptor| encryptor.must_be_instance_of( Archive::Zip::Codec::NullEncryption::Encrypt ) end end it "closes the object after executing a block" do e = Archive::Zip::Codec::NullEncryption::Encrypt.open(BinaryStringIO.new) do |encryptor| encryptor end e.closed?.must_equal(true) end end archive-zip-0.12.0/spec/archive/zip/codec/null_encryption/encrypt/seek_spec.rb0000644000004100000410000000377513513326431027510 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/null_encryption' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::NullEncryption::Encrypt#seek" do it "can seek to the beginning of the stream when the delegate responds to rewind" do encrypted_data = BinaryStringIO.new Archive::Zip::Codec::NullEncryption::Encrypt.open(encrypted_data) do |e| e.write('test') e.seek(0).must_equal(0) end end it "raises Errno::EINVAL when attempting to seek to the beginning of the stream when the delegate does not respond to rewind" do delegate = MiniTest::Mock.new delegate.expect(:close, nil) Archive::Zip::Codec::NullEncryption::Encrypt.open(delegate) do |e| lambda { e.seek(0) }.must_raise(Errno::EINVAL) end end it "raises Errno::EINVAL when seeking forward or backward from the current position of the stream" do encrypted_data = BinaryStringIO.new Archive::Zip::Codec::NullEncryption::Encrypt.open(encrypted_data) do |e| e.write('test') lambda { e.seek(1, IO::SEEK_CUR) }.must_raise(Errno::EINVAL) lambda { e.seek(-1, IO::SEEK_CUR) }.must_raise(Errno::EINVAL) end end it "raises Errno::EINVAL when seeking a non-zero offset relative to the beginning of the stream" do encrypted_data = BinaryStringIO.new Archive::Zip::Codec::NullEncryption::Encrypt.open(encrypted_data) do |e| lambda { e.seek(-1, IO::SEEK_SET) }.must_raise(Errno::EINVAL) lambda { e.seek(1, IO::SEEK_SET) }.must_raise(Errno::EINVAL) end end it "raises Errno::EINVAL when seeking relative to the end of the stream" do encrypted_data = BinaryStringIO.new Archive::Zip::Codec::NullEncryption::Encrypt.open(encrypted_data) do |e| lambda { e.seek(0, IO::SEEK_END) }.must_raise(Errno::EINVAL) lambda { e.seek(-1, IO::SEEK_END) }.must_raise(Errno::EINVAL) lambda { e.seek(1, IO::SEEK_END) }.must_raise(Errno::EINVAL) end end end archive-zip-0.12.0/spec/archive/zip/codec/null_encryption/encrypt/close_spec.rb0000644000004100000410000000167113513326431027657 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/null_encryption' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::NullEncryption::Encrypt#close" do it "closes the stream" do e = Archive::Zip::Codec::NullEncryption::Encrypt.new(BinaryStringIO.new) e.close e.closed?.must_equal(true) end it "closes the delegate stream by default" do delegate = MiniTest::Mock.new delegate.expect(:close, nil) e = Archive::Zip::Codec::NullEncryption::Encrypt.new(delegate) e.close end it "optionally leaves the delegate stream open" do delegate = MiniTest::Mock.new delegate.expect(:close, nil) e = Archive::Zip::Codec::NullEncryption::Encrypt.new(delegate) e.close(true) delegate = MiniTest::Mock.new e = Archive::Zip::Codec::NullEncryption::Encrypt.new(delegate) e.close(false) end end archive-zip-0.12.0/spec/archive/zip/codec/null_encryption/encrypt/new_spec.rb0000644000004100000410000000071713513326431027343 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/null_encryption' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::NullEncryption::Encrypt.new" do it "returns a new instance" do e = Archive::Zip::Codec::NullEncryption::Encrypt.new(BinaryStringIO.new) e.must_be_instance_of(Archive::Zip::Codec::NullEncryption::Encrypt) e.close end end archive-zip-0.12.0/spec/archive/zip/codec/null_encryption/decrypt/0000755000004100000410000000000013513326431025174 5ustar www-datawww-dataarchive-zip-0.12.0/spec/archive/zip/codec/null_encryption/decrypt/tell_spec.rb0000644000004100000410000000110513513326431027470 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/null_encryption' describe "Archive::Zip::Codec::NullEncryption::Decrypt#tell" do it "returns the current position of the stream" do NullEncryptionSpecs.encrypted_data do |ed| Archive::Zip::Codec::NullEncryption::Decrypt.open(ed) do |d| d.tell.must_equal(0) d.read(4) d.tell.must_equal(4) d.read d.tell.must_equal(235) d.rewind d.tell.must_equal(0) end end end end archive-zip-0.12.0/spec/archive/zip/codec/null_encryption/decrypt/rewind_spec.rb0000644000004100000410000000151113513326431030021 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/null_encryption' describe "Archive::Zip::Codec::NullEncryption::Decrypt#rewind" do it "can rewind the stream when the delegate responds to rewind" do NullEncryptionSpecs.encrypted_data do |ed| Archive::Zip::Codec::NullEncryption::Decrypt.open(ed) do |d| d.read(4) d.rewind d.read.must_equal(NullEncryptionSpecs.test_data) end end end it "raises Errno::EINVAL when attempting to rewind the stream when the delegate does not respond to rewind" do delegate = MiniTest::Mock.new delegate.expect(:close, nil) Archive::Zip::Codec::NullEncryption::Decrypt.open(delegate) do |d| lambda { d.rewind }.must_raise(Errno::EINVAL) end end end archive-zip-0.12.0/spec/archive/zip/codec/null_encryption/decrypt/open_spec.rb0000644000004100000410000000166113513326431027500 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/null_encryption' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::NullEncryption::Decrypt.open" do it "returns a new instance when run without a block" do d = Archive::Zip::Codec::NullEncryption::Decrypt.open(BinaryStringIO.new) d.must_be_instance_of(Archive::Zip::Codec::NullEncryption::Decrypt) d.close end it "executes a block with a new instance as an argument" do Archive::Zip::Codec::NullEncryption::Decrypt.open(BinaryStringIO.new) do |decryptor| decryptor.must_be_instance_of(Archive::Zip::Codec::NullEncryption::Decrypt) end end it "closes the object after executing a block" do d = Archive::Zip::Codec::NullEncryption::Decrypt.open(BinaryStringIO.new) do |decryptor| decryptor end d.closed?.must_equal(true) end end archive-zip-0.12.0/spec/archive/zip/codec/null_encryption/decrypt/seek_spec.rb0000644000004100000410000000424613513326431027470 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/null_encryption' describe "Archive::Zip::Codec::NullEncryption::Decrypt#seek" do it "can seek to the beginning of the stream when the delegate responds to rewind" do NullEncryptionSpecs.encrypted_data do |ed| Archive::Zip::Codec::NullEncryption::Decrypt.open(ed) do |d| d.read(4) d.seek(0).must_equal(0) end end end it "raises Errno::EINVAL when attempting to seek to the beginning of the stream when the delegate does not respond to rewind" do delegate = MiniTest::Mock.new delegate.expect(:close, nil) Archive::Zip::Codec::NullEncryption::Decrypt.open(delegate) do |d| lambda { d.seek(0) }.must_raise(Errno::EINVAL) end end it "raises Errno::EINVAL when seeking forward or backward from the current position of the stream" do NullEncryptionSpecs.encrypted_data do |ed| Archive::Zip::Codec::NullEncryption::Decrypt.open(ed) do |d| # Disable read buffering to avoid some seeking optimizations implemented # by IO::Like which allow seeking forward within the buffer. d.fill_size = 0 d.read(4) lambda { d.seek(1, IO::SEEK_CUR) }.must_raise(Errno::EINVAL) lambda { d.seek(-1, IO::SEEK_CUR) }.must_raise(Errno::EINVAL) end end end it "raises Errno::EINVAL when seeking a non-zero offset relative to the beginning of the stream" do NullEncryptionSpecs.encrypted_data do |ed| Archive::Zip::Codec::NullEncryption::Decrypt.open(ed) do |d| lambda { d.seek(-1, IO::SEEK_SET) }.must_raise(Errno::EINVAL) lambda { d.seek(1, IO::SEEK_SET) }.must_raise(Errno::EINVAL) end end end it "raises Errno::EINVAL when seeking relative to the end of the stream" do NullEncryptionSpecs.encrypted_data do |ed| Archive::Zip::Codec::NullEncryption::Decrypt.open(ed) do |d| lambda { d.seek(0, IO::SEEK_END) }.must_raise(Errno::EINVAL) lambda { d.seek(-1, IO::SEEK_END) }.must_raise(Errno::EINVAL) lambda { d.seek(1, IO::SEEK_END) }.must_raise(Errno::EINVAL) end end end end archive-zip-0.12.0/spec/archive/zip/codec/null_encryption/decrypt/read_spec.rb0000644000004100000410000000131313513326431027444 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/null_encryption' describe "Archive::Zip::Codec::NullEncryption::Decrypt#read" do it "calls the read method of the delegate" do delegate = MiniTest::Mock.new delegate.expect(:read, nil, [Integer]) delegate.expect(:close, nil) Archive::Zip::Codec::NullEncryption::Decrypt.open(delegate) do |d| d.read end end it "passes data through unmodified" do NullEncryptionSpecs.encrypted_data do |ed| Archive::Zip::Codec::NullEncryption::Decrypt.open(ed) do |d| d.read.must_equal(NullEncryptionSpecs.test_data) end end end end archive-zip-0.12.0/spec/archive/zip/codec/null_encryption/decrypt/close_spec.rb0000644000004100000410000000167113513326431027645 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/null_encryption' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::NullEncryption::Decrypt#close" do it "closes the stream" do d = Archive::Zip::Codec::NullEncryption::Decrypt.new(BinaryStringIO.new) d.close d.closed?.must_equal(true) end it "closes the delegate stream by default" do delegate = MiniTest::Mock.new delegate.expect(:close, nil) d = Archive::Zip::Codec::NullEncryption::Decrypt.new(delegate) d.close end it "optionally leaves the delegate stream open" do delegate = MiniTest::Mock.new delegate.expect(:close, nil) d = Archive::Zip::Codec::NullEncryption::Decrypt.new(delegate) d.close(true) delegate = MiniTest::Mock.new d = Archive::Zip::Codec::NullEncryption::Decrypt.new(delegate) d.close(false) end end archive-zip-0.12.0/spec/archive/zip/codec/null_encryption/decrypt/new_spec.rb0000644000004100000410000000071713513326431027331 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/null_encryption' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::NullEncryption::Decrypt.new" do it "returns a new instance" do d = Archive::Zip::Codec::NullEncryption::Decrypt.new(BinaryStringIO.new) d.must_be_instance_of(Archive::Zip::Codec::NullEncryption::Decrypt) d.close end end archive-zip-0.12.0/spec/archive/zip/codec/deflate/0000755000004100000410000000000013513326431021702 5ustar www-datawww-dataarchive-zip-0.12.0/spec/archive/zip/codec/deflate/fixtures/0000755000004100000410000000000013513326431023553 5ustar www-datawww-dataarchive-zip-0.12.0/spec/archive/zip/codec/deflate/fixtures/compressed_file.bin0000644000004100000410000000024013513326431027404 0ustar www-datawww-data%ŽAÂ0E÷9Å?Aïà\éŒkj¨‰¦E5ÖÓKã †o¬Î)+¼S.Œ–-Á ’Ñà˜ñZóõ±J[0É÷u~*äÍæ¸ÐwC”Û‘ê†DÑm%›•Îc¸$QÆT˜¯þ-ecx£‹´pX"Øm[K\Ùdè¦Æ‹õ»¥êº/nÂÉr)>e‚¦ÞÆ=ä¸õ`ÿ±TÂarchive-zip-0.12.0/spec/archive/zip/codec/deflate/fixtures/classes.rb0000644000004100000410000000103613513326431025535 0ustar www-datawww-data# encoding: UTF-8 class DeflateSpecs def self.compressed_data_nocomp(&b) File.open( File.join(File.dirname(__FILE__), 'compressed_file_nocomp.bin'), 'rb' ) do |f| f.read end end def self.compressed_data File.open( File.join(File.dirname(__FILE__), 'compressed_file.bin'), 'rb' ) do |f| block_given? ? yield(f) : f.read end end def self.test_data File.open(File.join(File.dirname(__FILE__), 'raw_file.txt'), 'rb') do |f| block_given? ? yield(f) : f.read end end end archive-zip-0.12.0/spec/archive/zip/codec/deflate/fixtures/raw_file.txt0000644000004100000410000000035313513326431026105 0ustar www-datawww-datatest This is a file with test data. The quick brown fox jumps over the lazy dog. Mary had a little lamb Whose fleece was white as snow And everywhere that Mary went The lamb was sure to go She sells sea shells down by the sea shore. archive-zip-0.12.0/spec/archive/zip/codec/deflate/fixtures/compressed_file_nocomp.bin0000644000004100000410000000036013513326431030762 0ustar www-datawww-dataëÿtest This is a file with test data. The quick brown fox jumps over the lazy dog. Mary had a little lamb Whose fleece was white as snow And everywhere that Mary went The lamb was sure to go She sells sea shells down by the sea shore. archive-zip-0.12.0/spec/archive/zip/codec/deflate/decompress/0000755000004100000410000000000013513326431024046 5ustar www-datawww-dataarchive-zip-0.12.0/spec/archive/zip/codec/deflate/decompress/open_spec.rb0000644000004100000410000000163513513326431026353 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/deflate' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::Deflate::Decompress.open" do it "returns a new instance when run without a block" do d = Archive::Zip::Codec::Deflate::Decompress.open(BinaryStringIO.new) d.must_be_instance_of(Archive::Zip::Codec::Deflate::Decompress) d.close end it "executes a block with a new instance as an argument" do Archive::Zip::Codec::Deflate::Decompress.open(BinaryStringIO.new) do |decompressor| decompressor.must_be_instance_of(Archive::Zip::Codec::Deflate::Decompress) end end it "closes the object after executing a block" do d = Archive::Zip::Codec::Deflate::Decompress.open(BinaryStringIO.new) do |decompressor| decompressor end d.closed?.must_equal(true) end end archive-zip-0.12.0/spec/archive/zip/codec/deflate/decompress/data_descriptor_spec.rb0000644000004100000410000000437213513326431030562 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/deflate' describe "Archive::Zip::Codec::Deflate::Decompress#data_descriptor" do it "is an instance of Archive::Zip::DataDescriptor" do DeflateSpecs.compressed_data do |cd| closed_decompressor = Archive::Zip::Codec::Deflate::Decompress.open( cd ) do |decompressor| decompressor.read decompressor.data_descriptor.must_be_instance_of( Archive::Zip::DataDescriptor ) decompressor end closed_decompressor.data_descriptor.must_be_instance_of( Archive::Zip::DataDescriptor ) end end it "has a crc32 attribute containing the CRC32 checksum" do crc32 = Zlib.crc32(DeflateSpecs.test_data) DeflateSpecs.compressed_data do |cd| closed_decompressor = Archive::Zip::Codec::Deflate::Decompress.open( cd ) do |decompressor| decompressor.read decompressor.data_descriptor.crc32.must_equal(crc32) decompressor end closed_decompressor.data_descriptor.crc32.must_equal(crc32) end end it "has a compressed_size attribute containing the size of the compressed data" do compressed_size = DeflateSpecs.compressed_data.size DeflateSpecs.compressed_data do |cd| closed_decompressor = Archive::Zip::Codec::Deflate::Decompress.open( cd ) do |decompressor| decompressor.read decompressor.data_descriptor.compressed_size.must_equal(compressed_size) decompressor end closed_decompressor.data_descriptor.compressed_size.must_equal( compressed_size ) end end it "has an uncompressed_size attribute containing the size of the input data" do uncompressed_size = DeflateSpecs.test_data.size DeflateSpecs.compressed_data do |cd| closed_decompressor = Archive::Zip::Codec::Deflate::Decompress.open( cd ) do |decompressor| decompressor.read decompressor.data_descriptor.uncompressed_size.must_equal( uncompressed_size ) decompressor end closed_decompressor.data_descriptor.uncompressed_size.must_equal( uncompressed_size ) end end end archive-zip-0.12.0/spec/archive/zip/codec/deflate/decompress/checksum_spec.rb0000644000004100000410000000113613513326431027210 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/deflate' describe "Archive::Zip::Deflate::Decompress#checksum" do it "computes a CRC32 checksum" do closed_decompressor = DeflateSpecs.compressed_data do |f| Archive::Zip::Codec::Deflate::Decompress.open(f) do |decompressor| decompressor.read decompressor.checksum.must_equal(Zlib.crc32(DeflateSpecs.test_data)) decompressor end end closed_decompressor.checksum.must_equal(Zlib.crc32(DeflateSpecs.test_data)) end end archive-zip-0.12.0/spec/archive/zip/codec/deflate/decompress/crc32_spec.rb0000644000004100000410000000112513513326431026320 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/deflate' describe "Archive::Zip::Deflate::Decompress#crc32" do it "computes a CRC32 checksum" do closed_decompressor = DeflateSpecs.compressed_data do |f| Archive::Zip::Codec::Deflate::Decompress.open(f) do |decompressor| decompressor.read decompressor.crc32.must_equal(Zlib.crc32(DeflateSpecs.test_data)) decompressor end end closed_decompressor.crc32.must_equal(Zlib.crc32(DeflateSpecs.test_data)) end end archive-zip-0.12.0/spec/archive/zip/codec/deflate/decompress/close_spec.rb0000644000004100000410000000163513513326431026517 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/deflate' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::Deflate::Decompress#close" do it "closes the stream" do c = Archive::Zip::Codec::Deflate::Decompress.new(BinaryStringIO.new) c.close c.closed?.must_equal(true) end it "closes the delegate stream by default" do delegate = MiniTest::Mock.new delegate.expect(:close, nil) c = Archive::Zip::Codec::Deflate::Decompress.new(delegate) c.close end it "optionally leaves the delegate stream open" do delegate = MiniTest::Mock.new delegate.expect(:close, nil) c = Archive::Zip::Codec::Deflate::Decompress.new(delegate) c.close(true) delegate = MiniTest::Mock.new c = Archive::Zip::Codec::Deflate::Decompress.new(delegate) c.close(false) end end archive-zip-0.12.0/spec/archive/zip/codec/deflate/decompress/new_spec.rb0000644000004100000410000000067313513326431026204 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/deflate' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::Deflate::Decompress.new" do it "returns a new instance" do d = Archive::Zip::Codec::Deflate::Decompress.new(BinaryStringIO.new) d.must_be_instance_of(Archive::Zip::Codec::Deflate::Decompress) d.close end end archive-zip-0.12.0/spec/archive/zip/codec/deflate/compress/0000755000004100000410000000000013513326431023535 5ustar www-datawww-dataarchive-zip-0.12.0/spec/archive/zip/codec/deflate/compress/write_spec.rb0000644000004100000410000000625513513326431026236 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/deflate' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::Deflate::Compress#write" do it "calls the write method of the delegate" do delegate = MiniTest::Mock.new delegate.expect( :write, DeflateSpecs.compressed_data.size, [DeflateSpecs.compressed_data] ) delegate.expect(:close, nil) Archive::Zip::Codec::Deflate::Compress.open( delegate, Zlib::DEFAULT_COMPRESSION ) do |compressor| compressor.write(DeflateSpecs.test_data) end end it "writes compressed data to the delegate" do compressed_data = BinaryStringIO.new Archive::Zip::Codec::Deflate::Compress.open( compressed_data, Zlib::DEFAULT_COMPRESSION ) do |compressor| compressor.write(DeflateSpecs.test_data) end compressed_data.string.must_equal(DeflateSpecs.compressed_data) end it "writes compressed data to a delegate that only performs partial writes" do compressed_data = BinaryStringIO.new # Override compressed_data.write to perform writes 1 byte at a time. class << compressed_data alias :write_orig :write def write(buffer) write_orig(buffer.slice(0, 1)) end end Archive::Zip::Codec::Deflate::Compress.open( compressed_data, Zlib::DEFAULT_COMPRESSION ) do |compressor| compressor.write(DeflateSpecs.test_data) end compressed_data.string.must_equal(DeflateSpecs.compressed_data) end it "writes compressed data to a delegate that raises Errno::EAGAIN" do compressed_data = BinaryStringIO.new # Override compressed_data.write to raise Errno::EAGAIN every other time # it's called. class << compressed_data alias :write_orig :write def write(buffer) @error_raised ||= false if @error_raised then @error_raised = false write_orig(buffer) else @error_raised = true raise Errno::EAGAIN end end end Archive::Zip::Codec::Deflate::Compress.open( compressed_data, Zlib::DEFAULT_COMPRESSION ) do |compressor| begin compressor.write(DeflateSpecs.test_data) rescue Errno::EAGAIN retry end end compressed_data.string.must_equal(DeflateSpecs.compressed_data) end it "writes compressed data to a delegate that raises Errno::EINTR" do compressed_data = BinaryStringIO.new # Override compressed_data.write to raise Errno::EINTR every other time it's # called. class << compressed_data alias :write_orig :write def write(buffer) @error_raised ||= false if @error_raised then @error_raised = false write_orig(buffer) else @error_raised = true raise Errno::EINTR end end end Archive::Zip::Codec::Deflate::Compress.open( compressed_data, Zlib::DEFAULT_COMPRESSION ) do |compressor| begin compressor.write(DeflateSpecs.test_data) rescue Errno::EINTR retry end end compressed_data.string.must_equal(DeflateSpecs.compressed_data) end end archive-zip-0.12.0/spec/archive/zip/codec/deflate/compress/open_spec.rb0000644000004100000410000000277513513326431026050 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/deflate' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::Deflate::Compress.open" do it "returns a new instance when run without a block" do c = Archive::Zip::Codec::Deflate::Compress.open( BinaryStringIO.new, Zlib::DEFAULT_COMPRESSION ) c.must_be_instance_of(Archive::Zip::Codec::Deflate::Compress) c.close end it "executes a block with a new instance as an argument" do Archive::Zip::Codec::Deflate::Compress.open( BinaryStringIO.new, Zlib::DEFAULT_COMPRESSION ) { |c| c.must_be_instance_of(Archive::Zip::Codec::Deflate::Compress) } end it "closes the object after executing a block" do Archive::Zip::Codec::Deflate::Compress.open( BinaryStringIO.new, Zlib::DEFAULT_COMPRESSION ) { |c| c }.closed?.must_equal(true) end it "allows level to be set" do data = DeflateSpecs.test_data compressed_data = BinaryStringIO.new Archive::Zip::Codec::Deflate::Compress.open( compressed_data, Zlib::DEFAULT_COMPRESSION ) { |c| c.write(data) } compressed_data.string.must_equal(DeflateSpecs.compressed_data) data = DeflateSpecs.test_data compressed_data = BinaryStringIO.new Archive::Zip::Codec::Deflate::Compress.open( compressed_data, Zlib::NO_COMPRESSION ) { |c| c.write(data) } compressed_data.string.must_equal(DeflateSpecs.compressed_data_nocomp) end end archive-zip-0.12.0/spec/archive/zip/codec/deflate/compress/data_descriptor_spec.rb0000644000004100000410000000466113513326431030252 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/deflate' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::Deflate::Compress#data_descriptor" do it "is an instance of Archive::Zip::DataDescriptor" do test_data = DeflateSpecs.test_data compressed_data = BinaryStringIO.new closed_compressor = Archive::Zip::Codec::Deflate::Compress.open( compressed_data, Zlib::DEFAULT_COMPRESSION ) do |compressor| compressor.write(test_data) compressor.flush compressor.data_descriptor.class.must_equal(Archive::Zip::DataDescriptor) compressor end closed_compressor.data_descriptor.class.must_equal( Archive::Zip::DataDescriptor ) end it "has a crc32 attribute containing the CRC32 checksum" do test_data = DeflateSpecs.test_data compressed_data = BinaryStringIO.new closed_compressor = Archive::Zip::Codec::Deflate::Compress.open( compressed_data, Zlib::DEFAULT_COMPRESSION ) do |compressor| compressor.write(test_data) compressor.flush compressor.data_descriptor.crc32.must_equal(Zlib.crc32(test_data)) compressor end closed_compressor.data_descriptor.crc32.must_equal(Zlib.crc32(test_data)) end it "has a compressed_size attribute containing the size of the compressed data" do test_data = DeflateSpecs.test_data compressed_data = BinaryStringIO.new closed_compressor = Archive::Zip::Codec::Deflate::Compress.open( compressed_data, Zlib::DEFAULT_COMPRESSION ) do |compressor| compressor.write(test_data) compressor.flush compressor.data_descriptor.compressed_size.must_equal( compressed_data.size ) compressor end closed_compressor.data_descriptor.compressed_size.must_equal( compressed_data.size ) end it "has an uncompressed_size attribute containing the size of the input data" do test_data = DeflateSpecs.test_data compressed_data = BinaryStringIO.new closed_compressor = Archive::Zip::Codec::Deflate::Compress.open( compressed_data, Zlib::DEFAULT_COMPRESSION ) do |compressor| compressor.write(test_data) compressor.flush compressor.data_descriptor.uncompressed_size.must_equal(test_data.size) compressor end closed_compressor.data_descriptor.uncompressed_size.must_equal( test_data.size ) end end archive-zip-0.12.0/spec/archive/zip/codec/deflate/compress/checksum_spec.rb0000644000004100000410000000265713513326431026710 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/deflate' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::Deflate::Compress#checksum" do it "computes the CRC32 checksum" do compressed_data = BinaryStringIO.new closed_compressor = Archive::Zip::Codec::Deflate::Compress.open( compressed_data, Zlib::DEFAULT_COMPRESSION ) do |compressor| compressor.write(DeflateSpecs.test_data) compressor.flush compressor.checksum.must_equal Zlib.crc32(DeflateSpecs.test_data) compressor end closed_compressor.checksum.must_equal Zlib.crc32(DeflateSpecs.test_data) end it "computes the CRC32 checksum even when the delegate performs partial writes" do compressed_data = BinaryStringIO.new # Override compressed_data.write to perform writes 1 byte at a time. class << compressed_data alias :write_orig :write def write(buffer) write_orig(buffer.slice(0, 1)) end end closed_compressor = Archive::Zip::Codec::Deflate::Compress.open( compressed_data, Zlib::DEFAULT_COMPRESSION ) do |compressor| compressor.write(DeflateSpecs.test_data) compressor.flush compressor.checksum.must_equal Zlib.crc32(DeflateSpecs.test_data) compressor end closed_compressor.checksum.must_equal Zlib.crc32(DeflateSpecs.test_data) end end archive-zip-0.12.0/spec/archive/zip/codec/deflate/compress/crc32_spec.rb0000644000004100000410000000131213513326431026005 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/deflate' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::Deflate::Compress#crc32" do it "computes the CRC32 checksum" do compressed_data = BinaryStringIO.new closed_compressor = Archive::Zip::Codec::Deflate::Compress.open( compressed_data, Zlib::DEFAULT_COMPRESSION ) do |compressor| compressor.write(DeflateSpecs.test_data) compressor.flush compressor.crc32.must_equal Zlib.crc32(DeflateSpecs.test_data) compressor end closed_compressor.crc32.must_equal Zlib.crc32(DeflateSpecs.test_data) end end archive-zip-0.12.0/spec/archive/zip/codec/deflate/compress/close_spec.rb0000644000004100000410000000225113513326431026201 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/deflate' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::Deflate::Compress#close" do it "closes the stream" do c = Archive::Zip::Codec::Deflate::Compress.new( BinaryStringIO.new, Zlib::DEFAULT_COMPRESSION ) c.close c.closed?.must_equal true end it "closes the delegate stream by default" do delegate = MiniTest::Mock.new delegate.expect(:write, 8, [String]) delegate.expect(:close, nil) c = Archive::Zip::Codec::Deflate::Compress.new( delegate, Zlib::DEFAULT_COMPRESSION ) c.close end it "optionally leaves the delegate stream open" do delegate = MiniTest::Mock.new delegate.expect(:write, 8, [String]) delegate.expect(:close, nil) c = Archive::Zip::Codec::Deflate::Compress.new( delegate, Zlib::DEFAULT_COMPRESSION ) c.close(true) delegate = MiniTest::Mock.new delegate.expect(:write, 8, [String]) c = Archive::Zip::Codec::Deflate::Compress.new( delegate, Zlib::DEFAULT_COMPRESSION ) c.close(false) end end archive-zip-0.12.0/spec/archive/zip/codec/deflate/compress/new_spec.rb0000644000004100000410000000202713513326431025666 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/deflate' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::Deflate::Compress.new" do it "returns a new instance" do c = Archive::Zip::Codec::Deflate::Compress.new( BinaryStringIO.new, Zlib::DEFAULT_COMPRESSION ) c.must_be_instance_of(Archive::Zip::Codec::Deflate::Compress) c.close end it "allows level to be set" do data = DeflateSpecs.test_data compressed_data = BinaryStringIO.new c = Archive::Zip::Codec::Deflate::Compress.new( compressed_data, Zlib::DEFAULT_COMPRESSION ) c.write(data) c.close compressed_data.string.must_equal(DeflateSpecs.compressed_data) compressed_data = BinaryStringIO.new c = Archive::Zip::Codec::Deflate::Compress.new( compressed_data, Zlib::NO_COMPRESSION ) c.write(data) c.close compressed_data.string.must_equal(DeflateSpecs.compressed_data_nocomp) end end archive-zip-0.12.0/spec/archive/zip/codec/traditional_encryption/0000755000004100000410000000000013513326431025062 5ustar www-datawww-dataarchive-zip-0.12.0/spec/archive/zip/codec/traditional_encryption/fixtures/0000755000004100000410000000000013513326431026733 5ustar www-datawww-dataarchive-zip-0.12.0/spec/archive/zip/codec/traditional_encryption/fixtures/classes.rb0000644000004100000410000000101713513326431030714 0ustar www-datawww-data# encoding: UTF-8 class TraditionalEncryptionSpecs class << self def password 'p455w0rd' end def mtime Time.local(1979, 12, 31, 18, 0, 0) end def encrypted_data File.open( File.join(File.dirname(__FILE__), 'encrypted_file.bin'), 'rb' ) do |f| block_given? ? yield(f) : f.read end end def test_data File.open(File.join(File.dirname(__FILE__), 'raw_file.txt'), 'rb') do |f| block_given? ? yield(f) : f.read end end end end archive-zip-0.12.0/spec/archive/zip/codec/traditional_encryption/fixtures/raw_file.txt0000644000004100000410000000035313513326431031265 0ustar www-datawww-datatest This is a file with test data. The quick brown fox jumps over the lazy dog. Mary had a little lamb Whose fleece was white as snow And everywhere that Mary went The lamb was sure to go She sells sea shells down by the sea shore. archive-zip-0.12.0/spec/archive/zip/codec/traditional_encryption/fixtures/encrypted_file.bin0000644000004100000410000000036713513326431032427 0ustar www-datawww-datak•ÛÉZò9Ü´^?繇”ùÉW¡-4e¢UËß—]tŠVæGšÚù©T[— ×–»s“ztÇÀ6&VåÝDj)v a»aøÉÙ ŠÙæ &“+(ŽØ‡…çô;Îliz¼"ĶièNy¯I<¼¶¥ i@›ôï:'\y½ºõ³‚Z ¥>©þq¦‘óplü"’ù2Ú®æ5 Wl¡3£19“/éýµèÚ,ŒÑarchive-zip-0.12.0/spec/archive/zip/codec/traditional_encryption/encrypt/0000755000004100000410000000000013513326431026546 5ustar www-datawww-dataarchive-zip-0.12.0/spec/archive/zip/codec/traditional_encryption/encrypt/tell_spec.rb0000644000004100000410000000214513513326431031047 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/traditional_encryption' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::TraditionalEncryption::Encrypt#tell" do it "returns the current position of the stream" do encrypted_data = BinaryStringIO.new Archive::Zip::Codec::TraditionalEncryption::Encrypt.open( encrypted_data, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) do |e| e.tell.must_equal(0) e.write('test1') e.tell.must_equal(5) e.write('test2') e.tell.must_equal(10) e.rewind e.tell.must_equal(0) end end it "raises IOError on closed stream" do delegate = MiniTest::Mock.new delegate.expect(:write, 12, [String]) delegate.expect(:close, nil) lambda do Archive::Zip::Codec::TraditionalEncryption::Encrypt.open( delegate, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) { |e| e }.tell end.must_raise(IOError) end end archive-zip-0.12.0/spec/archive/zip/codec/traditional_encryption/encrypt/rewind_spec.rb0000644000004100000410000000241713513326431031401 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/traditional_encryption' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::TraditionalEncryption::Encrypt#rewind" do it "can rewind the stream when the delegate responds to rewind" do encrypted_data = BinaryStringIO.new Archive::Zip::Codec::TraditionalEncryption::Encrypt.open( encrypted_data, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) do |e| e.write('test') # Ensure repeatable test data is used for encryption header. srand(0) e.rewind e.write(TraditionalEncryptionSpecs.test_data) end encrypted_data.string.must_equal(TraditionalEncryptionSpecs.encrypted_data) end it "raises Errno::EINVAL when attempting to rewind the stream when the delegate does not respond to rewind" do delegate = MiniTest::Mock.new delegate.expect(:write, 12, [String]) delegate.expect(:close, nil) Archive::Zip::Codec::TraditionalEncryption::Encrypt.open( delegate, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) do |e| lambda { e.rewind }.must_raise(Errno::EINVAL) end end end archive-zip-0.12.0/spec/archive/zip/codec/traditional_encryption/encrypt/write_spec.rb0000644000004100000410000000662513513326431031250 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/traditional_encryption' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::TraditionalEncryption::Encrypt#write" do it "writes encrypted data to the delegate" do # Ensure repeatable test data is used for encryption header. srand(0) encrypted_data = BinaryStringIO.new Archive::Zip::Codec::TraditionalEncryption::Encrypt.open( encrypted_data, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) do |e| e.write(TraditionalEncryptionSpecs.test_data) end encrypted_data.string.must_equal(TraditionalEncryptionSpecs.encrypted_data) end it "writes encrypted data to a delegate that only performs partial writes" do # Ensure repeatable test data is used for encryption header. srand(0) encrypted_data = BinaryStringIO.new # Override encrypted_data.write to perform writes 1 byte at a time. class << encrypted_data alias :write_orig :write def write(buffer) write_orig(buffer.slice(0, 1)) end end Archive::Zip::Codec::TraditionalEncryption::Encrypt.open( encrypted_data, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) do |e| e.write(TraditionalEncryptionSpecs.test_data) end encrypted_data.string.must_equal(TraditionalEncryptionSpecs.encrypted_data) end it "writes encrypted data to a delegate that raises Errno::EAGAIN" do # Ensure repeatable test data is used for encryption header. srand(0) encrypted_data = BinaryStringIO.new # Override encrypted_data.write to raise Errno::EAGAIN every other time it's # called. class << encrypted_data alias :write_orig :write def write(buffer) @error_raised ||= false if @error_raised then @error_raised = false write_orig(buffer) else @error_raised = true raise Errno::EAGAIN end end end Archive::Zip::Codec::TraditionalEncryption::Encrypt.open( encrypted_data, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) do |e| begin e.write(TraditionalEncryptionSpecs.test_data) rescue Errno::EAGAIN retry end end encrypted_data.string.must_equal(TraditionalEncryptionSpecs.encrypted_data) end it "writes encrypted data to a delegate that raises Errno::EINTR" do # Ensure repeatable test data is used for encryption header. srand(0) encrypted_data = BinaryStringIO.new # Override encrypted_data.write to raise Errno::EINTR every other time it's # called. class << encrypted_data alias :write_orig :write def write(buffer) @error_raised ||= false if @error_raised then @error_raised = false write_orig(buffer) else @error_raised = true raise Errno::EINTR end end end Archive::Zip::Codec::TraditionalEncryption::Encrypt.open( encrypted_data, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) do |e| begin e.write(TraditionalEncryptionSpecs.test_data) rescue Errno::EINTR retry end end encrypted_data.string.must_equal(TraditionalEncryptionSpecs.encrypted_data) end end archive-zip-0.12.0/spec/archive/zip/codec/traditional_encryption/encrypt/open_spec.rb0000644000004100000410000000237713513326431031057 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/traditional_encryption' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::TraditionalEncryption::Encrypt.open" do it "returns a new instance when run without a block" do e = Archive::Zip::Codec::TraditionalEncryption::Encrypt.open( BinaryStringIO.new, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) e.must_be_instance_of(Archive::Zip::Codec::TraditionalEncryption::Encrypt) e.close end it "executes a block with a new instance as an argument" do Archive::Zip::Codec::TraditionalEncryption::Encrypt.open( BinaryStringIO.new, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) do |encryptor| encryptor.must_be_instance_of(Archive::Zip::Codec::TraditionalEncryption::Encrypt) end end it "closes the object after executing a block" do e = Archive::Zip::Codec::TraditionalEncryption::Encrypt.open( BinaryStringIO.new, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) do |encryptor| encryptor end e.closed?.must_equal(true) end end archive-zip-0.12.0/spec/archive/zip/codec/traditional_encryption/encrypt/seek_spec.rb0000644000004100000410000000506313513326431031040 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/traditional_encryption' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::TraditionalEncryption::Encrypt#seek" do it "can seek to the beginning of the stream when the delegate responds to rewind" do encrypted_data = BinaryStringIO.new Archive::Zip::Codec::TraditionalEncryption::Encrypt.open( encrypted_data, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) do |e| e.write('test') e.seek(0).must_equal(0) end end it "raises Errno::EINVAL when attempting to seek to the beginning of the stream when the delegate does not respond to rewind" do delegate = MiniTest::Mock.new delegate.expect(:write, 12, [String]) delegate.expect(:close, nil) Archive::Zip::Codec::TraditionalEncryption::Encrypt.open( delegate, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) do |e| lambda { e.seek(0) }.must_raise(Errno::EINVAL) end end it "raises Errno::EINVAL when seeking forward or backward from the current position of the stream" do encrypted_data = BinaryStringIO.new Archive::Zip::Codec::TraditionalEncryption::Encrypt.open( encrypted_data, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) do |e| e.write('test') lambda { e.seek(1, IO::SEEK_CUR) }.must_raise(Errno::EINVAL) lambda { e.seek(-1, IO::SEEK_CUR) }.must_raise(Errno::EINVAL) end end it "raises Errno::EINVAL when seeking a non-zero offset relative to the beginning of the stream" do encrypted_data = BinaryStringIO.new Archive::Zip::Codec::TraditionalEncryption::Encrypt.open( encrypted_data, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) do |e| lambda { e.seek(-1, IO::SEEK_SET) }.must_raise(Errno::EINVAL) lambda { e.seek(1, IO::SEEK_SET) }.must_raise(Errno::EINVAL) end end it "raises Errno::EINVAL when seeking relative to the end of the stream" do encrypted_data = BinaryStringIO.new Archive::Zip::Codec::TraditionalEncryption::Encrypt.open( encrypted_data, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) do |e| lambda { e.seek(0, IO::SEEK_END) }.must_raise(Errno::EINVAL) lambda { e.seek(-1, IO::SEEK_END) }.must_raise(Errno::EINVAL) lambda { e.seek(1, IO::SEEK_END) }.must_raise(Errno::EINVAL) end end end archive-zip-0.12.0/spec/archive/zip/codec/traditional_encryption/encrypt/close_spec.rb0000644000004100000410000000273513513326431031221 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/traditional_encryption' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::TraditionalEncryption::Encrypt#close" do it "closes the stream" do e = Archive::Zip::Codec::TraditionalEncryption::Encrypt.new( BinaryStringIO.new, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) e.close e.closed?.must_equal(true) end it "closes the delegate stream by default" do delegate = MiniTest::Mock.new delegate.expect(:write, 12, [String]) delegate.expect(:close, nil) e = Archive::Zip::Codec::TraditionalEncryption::Encrypt.new( delegate, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) e.close end it "optionally leaves the delegate stream open" do delegate = MiniTest::Mock.new delegate.expect(:write, 12, [String]) delegate.expect(:close, nil) e = Archive::Zip::Codec::TraditionalEncryption::Encrypt.new( delegate, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) e.close(true) delegate = MiniTest::Mock.new delegate.expect(:write, 12, [String]) e = Archive::Zip::Codec::TraditionalEncryption::Encrypt.new( delegate, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) e.close(false) end end archive-zip-0.12.0/spec/archive/zip/codec/traditional_encryption/encrypt/new_spec.rb0000644000004100000410000000111213513326431030671 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/traditional_encryption' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::TraditionalEncryption::Encrypt.new" do it "returns a new instance" do e = Archive::Zip::Codec::TraditionalEncryption::Encrypt.new( BinaryStringIO.new, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) e.must_be_instance_of(Archive::Zip::Codec::TraditionalEncryption::Encrypt) e.close end end archive-zip-0.12.0/spec/archive/zip/codec/traditional_encryption/decrypt/0000755000004100000410000000000013513326431026534 5ustar www-datawww-dataarchive-zip-0.12.0/spec/archive/zip/codec/traditional_encryption/decrypt/tell_spec.rb0000644000004100000410000000131013513326431031026 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/traditional_encryption' describe "Archive::Zip::Codec::TraditionalEncryption::Decrypt#tell" do it "returns the current position of the stream" do TraditionalEncryptionSpecs.encrypted_data do |ed| Archive::Zip::Codec::TraditionalEncryption::Decrypt.open( ed, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) do |d| d.tell.must_equal(0) d.read(4) d.tell.must_equal(4) d.read d.tell.must_equal(235) d.rewind d.tell.must_equal(0) end end end end archive-zip-0.12.0/spec/archive/zip/codec/traditional_encryption/decrypt/rewind_spec.rb0000644000004100000410000000215413513326431031365 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/traditional_encryption' describe "Archive::Zip::Codec::TraditionalEncryption::Decrypt#rewind" do it "can rewind the stream when the delegate responds to rewind" do TraditionalEncryptionSpecs.encrypted_data do |ed| Archive::Zip::Codec::TraditionalEncryption::Decrypt.open( ed, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) do |d| d.read(4) d.rewind d.read.must_equal(TraditionalEncryptionSpecs.test_data) end end end it "raises Errno::EINVAL when attempting to rewind the stream when the delegate does not respond to rewind" do delegate = MiniTest::Mock.new delegate.expect(:read, "\000" * 12, [Integer]) delegate.expect(:close, nil) Archive::Zip::Codec::TraditionalEncryption::Decrypt.open( delegate, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) do |d| lambda { d.rewind }.must_raise(Errno::EINVAL) end end end archive-zip-0.12.0/spec/archive/zip/codec/traditional_encryption/decrypt/open_spec.rb0000644000004100000410000000246613513326431031044 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/traditional_encryption' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::TraditionalEncryption::Decrypt.open" do it "returns a new instance when run without a block" do d = Archive::Zip::Codec::TraditionalEncryption::Decrypt.open( BinaryStringIO.new("\000" * 12), TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) d.must_be_instance_of(Archive::Zip::Codec::TraditionalEncryption::Decrypt) d.close end it "executes a block with a new instance as an argument" do Archive::Zip::Codec::TraditionalEncryption::Decrypt.open( BinaryStringIO.new("\000" * 12), TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) do |decryptor| decryptor.must_be_instance_of( Archive::Zip::Codec::TraditionalEncryption::Decrypt ) end end it "closes the object after executing a block" do d = Archive::Zip::Codec::TraditionalEncryption::Decrypt.open( BinaryStringIO.new("\000" * 12), TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) do |decryptor| decryptor end d.closed?.must_equal(true) end end archive-zip-0.12.0/spec/archive/zip/codec/traditional_encryption/decrypt/seek_spec.rb0000644000004100000410000000544113513326431031026 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/traditional_encryption' describe "Archive::Zip::Codec::TraditionalEncryption::Decrypt#seek" do it "can seek to the beginning of the stream when the delegate responds to rewind" do TraditionalEncryptionSpecs.encrypted_data do |ed| Archive::Zip::Codec::TraditionalEncryption::Decrypt.open( ed, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) do |d| d.read(4) d.seek(0).must_equal(0) end end end it "raises Errno::EINVAL when attempting to seek to the beginning of the stream when the delegate does not respond to rewind" do delegate = MiniTest::Mock.new delegate.expect(:read, "\000" * 12, [Integer]) delegate.expect(:close, nil) Archive::Zip::Codec::TraditionalEncryption::Decrypt.open( delegate, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) do |d| lambda { d.seek(0) }.must_raise(Errno::EINVAL) end end it "raises Errno::EINVAL when seeking forward or backward from the current position of the stream" do TraditionalEncryptionSpecs.encrypted_data do |ed| Archive::Zip::Codec::TraditionalEncryption::Decrypt.open( ed, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) do |d| # Disable read buffering to avoid some seeking optimizations implemented # by IO::Like which allow seeking forward within the buffer. d.fill_size = 0 d.read(4) lambda { d.seek(1, IO::SEEK_CUR) }.must_raise(Errno::EINVAL) lambda { d.seek(-1, IO::SEEK_CUR) }.must_raise(Errno::EINVAL) end end end it "raises Errno::EINVAL when seeking a non-zero offset relative to the beginning of the stream" do TraditionalEncryptionSpecs.encrypted_data do |ed| Archive::Zip::Codec::TraditionalEncryption::Decrypt.open( ed, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) do |d| lambda { d.seek(-1, IO::SEEK_SET) }.must_raise(Errno::EINVAL) lambda { d.seek(1, IO::SEEK_SET) }.must_raise(Errno::EINVAL) end end end it "raises Errno::EINVAL when seeking relative to the end of the stream" do TraditionalEncryptionSpecs.encrypted_data do |ed| Archive::Zip::Codec::TraditionalEncryption::Decrypt.open( ed, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) do |d| lambda { d.seek(0, IO::SEEK_END) }.must_raise(Errno::EINVAL) lambda { d.seek(-1, IO::SEEK_END) }.must_raise(Errno::EINVAL) lambda { d.seek(1, IO::SEEK_END) }.must_raise(Errno::EINVAL) end end end end archive-zip-0.12.0/spec/archive/zip/codec/traditional_encryption/decrypt/read_spec.rb0000644000004100000410000000705013513326431031010 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/traditional_encryption' describe "Archive::Zip::Codec::TraditionalEncryption::Decrypt#read" do it "calls the read method of the delegate" do delegate = MiniTest::Mock.new delegate.expect(:read, "\000" * 12, [Integer]) delegate.expect(:read, nil, [Integer]) delegate.expect(:close, nil) Archive::Zip::Codec::TraditionalEncryption::Decrypt.open( delegate, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) do |d| d.read end end it "decrypts data read from the delegate" do TraditionalEncryptionSpecs.encrypted_data do |ed| Archive::Zip::Codec::TraditionalEncryption::Decrypt.open( ed, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) do |d| d.read.must_equal(TraditionalEncryptionSpecs.test_data) end end end it "decrypts data read from a delegate that only returns 1 byte at a time" do TraditionalEncryptionSpecs.encrypted_data do |ed| # Override ed.read to raise Errno::EAGAIN every other time it's called. class << ed alias :read_orig :read def read(length = nil, buffer = nil) read_orig(1, buffer) end end Archive::Zip::Codec::TraditionalEncryption::Decrypt.open( ed, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) do |d| buffer = '' begin buffer << d.read rescue Errno::EAGAIN retry end buffer.must_equal(TraditionalEncryptionSpecs.test_data) end end end it "decrypts data read from a delegate that raises Errno::EAGAIN" do TraditionalEncryptionSpecs.encrypted_data do |ed| # Override ed.read to raise Errno::EAGAIN every other time it's called. class << ed alias :read_orig :read def read(length = nil, buffer = nil) @error_raised ||= false if @error_raised then @error_raised = false read_orig(length, buffer) else @error_raised = true raise Errno::EAGAIN end end end Archive::Zip::Codec::TraditionalEncryption::Decrypt.open( ed, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) do |d| buffer = '' begin buffer << d.read rescue Errno::EAGAIN retry end buffer.must_equal(TraditionalEncryptionSpecs.test_data) end end end it "decrypts data read from a delegate that raises Errno::EINTR" do TraditionalEncryptionSpecs.encrypted_data do |ed| # Override ed.read to raise Errno::EINTR every other time it's called. class << ed alias :read_orig :read def read(length = nil, buffer = nil) @error_raised ||= false if @error_raised then @error_raised = false read_orig(length, buffer) else @error_raised = true raise Errno::EINTR end end end Archive::Zip::Codec::TraditionalEncryption::Decrypt.open( ed, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) do |d| buffer = '' begin buffer << d.read rescue Errno::EINTR retry end buffer.must_equal(TraditionalEncryptionSpecs.test_data) end end end end archive-zip-0.12.0/spec/archive/zip/codec/traditional_encryption/decrypt/close_spec.rb0000644000004100000410000000300513513326431031176 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/traditional_encryption' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::TraditionalEncryption::Decrypt#close" do it "closes the stream" do d = Archive::Zip::Codec::TraditionalEncryption::Decrypt.new( BinaryStringIO.new("\000" * 12), TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) d.close d.closed?.must_equal(true) end it "closes the delegate stream by default" do delegate = MiniTest::Mock.new delegate.expect(:read, "\000" * 12, [Integer]) delegate.expect(:close, nil) d = Archive::Zip::Codec::TraditionalEncryption::Decrypt.new( delegate, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) d.close end it "optionally leaves the delegate stream open" do delegate = MiniTest::Mock.new delegate.expect(:read, "\000" * 12, [Integer]) delegate.expect(:close, nil) d = Archive::Zip::Codec::TraditionalEncryption::Decrypt.new( delegate, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) d.close(true) delegate = MiniTest::Mock.new delegate.expect(:read, "\000" * 12, [Integer]) d = Archive::Zip::Codec::TraditionalEncryption::Decrypt.new( delegate, TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) d.close(false) end end archive-zip-0.12.0/spec/archive/zip/codec/traditional_encryption/decrypt/new_spec.rb0000644000004100000410000000112713513326431030665 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/zip/codec/traditional_encryption' require 'archive/support/binary_stringio' describe "Archive::Zip::Codec::TraditionalEncryption::Decrypt.new" do it "returns a new instance" do d = Archive::Zip::Codec::TraditionalEncryption::Decrypt.new( BinaryStringIO.new("\000" * 12), TraditionalEncryptionSpecs.password, TraditionalEncryptionSpecs.mtime ) d.must_be_instance_of(Archive::Zip::Codec::TraditionalEncryption::Decrypt) d.close end end archive-zip-0.12.0/spec/archive/zip/archive_spec.rb0000644000004100000410000000303013513326431022175 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require 'tmpdir' require 'archive/zip' describe 'Archive::Zip#archive' do it 'adds file entries' do file_name = 'file' Dir.mktmpdir('archive_zip#archive') do |dir| file_path = File.join(dir, file_name) File.open(file_path, 'wb') { |f| f.write('data') } archive_file_path = File.join(dir, 'archive.zip') Archive::Zip.open(archive_file_path, 'w') do |a| a.archive(file_path) end Archive::Zip.open(archive_file_path, 'r') do |a| entry = a.first entry.wont_be_nil entry.zip_path.must_equal(file_name) entry.file?.must_equal(true) entry.file_data.read.must_equal('data') end end end it 'adds entries with multibyte names' do unless Object.const_defined?(:Encoding) skip("String encodings are not supported on current Ruby (#{RUBY_DESCRIPTION})") end mb_file_name = '☂file☄' Dir.mktmpdir('archive_zip#archive') do |dir| mb_file_path = File.join(dir, mb_file_name) File.open(mb_file_path, 'wb') { |f| f.write('data') } archive_file_path = File.join(dir, 'archive.zip') Archive::Zip.open(archive_file_path, 'w') do |a| a.archive(mb_file_path) end Archive::Zip.open(archive_file_path, 'r') do |a| entry = a.first entry.wont_be_nil entry.zip_path.must_equal(mb_file_name.dup.force_encoding('binary')) entry.file?.must_equal(true) entry.file_data.read.must_equal('data') end end end end archive-zip-0.12.0/spec/ioextensions/0000755000004100000410000000000013513326431017525 5ustar www-datawww-dataarchive-zip-0.12.0/spec/ioextensions/read_exactly_spec.rb0000644000004100000410000000300013513326431023521 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require 'archive/support/ioextensions.rb' require 'archive/support/binary_stringio' describe "IOExtensions.read_exactly" do it "reads and returns length bytes from a given IO object" do io = BinaryStringIO.new('This is test data') IOExtensions.read_exactly(io, 4).must_equal 'This' IOExtensions.read_exactly(io, 13).must_equal ' is test data' end it "raises an error when too little data is available" do io = BinaryStringIO.new('This is test data') lambda do IOExtensions.read_exactly(io, 18) end.must_raise EOFError end it "takes an optional buffer argument and fills it" do io = BinaryStringIO.new('This is test data') buffer = '' IOExtensions.read_exactly(io, 4, buffer) buffer.must_equal 'This' buffer = '' IOExtensions.read_exactly(io, 13, buffer) buffer.must_equal ' is test data' end it "empties the optional buffer before filling it" do io = BinaryStringIO.new('This is test data') buffer = '' IOExtensions.read_exactly(io, 4, buffer) buffer.must_equal 'This' IOExtensions.read_exactly(io, 13, buffer) buffer.must_equal ' is test data' end it "can read 0 bytes" do io = BinaryStringIO.new('This is test data') IOExtensions.read_exactly(io, 0).must_equal '' end it "retries partial reads" do io = MiniTest::Mock.new io.expect(:read, 'hello', [10]) io.expect(:read, 'hello', [5]) IOExtensions.read_exactly(io, 10).must_equal 'hellohello' end end archive-zip-0.12.0/spec/zlib/0000755000004100000410000000000013513326431015736 5ustar www-datawww-dataarchive-zip-0.12.0/spec/zlib/fixtures/0000755000004100000410000000000013513326431017607 5ustar www-datawww-dataarchive-zip-0.12.0/spec/zlib/fixtures/compressed_file.bin0000644000004100000410000000024613513326431023446 0ustar www-datawww-dataxœ%ŽAÂ0E÷9Å?Aïà\éŒkj¨‰¦E5ÖÓKã †o¬Î)+¼S.Œ–-Á ’Ñà˜ñZóõ±J[0É÷u~*äÍæ¸ÐwC”Û‘ê†DÑm%›•Îc¸$QÆT˜¯þ-ecx£‹´pX"Øm[K\Ùdè¦Æ‹õ»¥êº/nÂÉr)>e‚¦ÞÆ=ä¸õ`ÿ±TÂãCQëarchive-zip-0.12.0/spec/zlib/fixtures/compressed_file_huffman.bin0000644000004100000410000000025113513326431025146 0ustar www-datawww-dataxÁÁqÃ0 À?«¸ Ôƒ ð+šÉ²NcˆHÈ S}v“‘eÕ¨ÁQ5ÉHì’²”U‰Ÿ«>^غ†Ãñuß³#•0ù›Øý¹”r—>¡²C`5Ó“s+ŸêAF>ˆ!¡5 DóQnmßìs(;‘*‰»ô‰Á–eUÂäÜ0$W'ÒñôR>”š‚‚Pšv ÛD*„zçRþãCQëarchive-zip-0.12.0/spec/zlib/fixtures/compressed_file_raw.bin0000644000004100000410000000024013513326431024311 0ustar www-datawww-data%ŽAÂ0E÷9Å?Aïà\éŒkj¨‰¦E5ÖÓKã †o¬Î)+¼S.Œ–-Á ’Ñà˜ñZóõ±J[0É÷u~*äÍæ¸ÐwC”Û‘ê†DÑm%›•Îc¸$QÆT˜¯þ-ecx£‹´pX"Øm[K\Ùdè¦Æ‹õ»¥êº/nÂÉr)>e‚¦ÞÆ=ä¸õ`ÿ±TÂarchive-zip-0.12.0/spec/zlib/fixtures/compressed_file_gzip.bin0000644000004100000410000000026213513326431024475 0ustar www-datawww-data‹%ŽAÂ0E÷9Å?Aïà\éŒkj¨‰¦E5ÖÓKã †o¬Î)+¼S.Œ–-Á ’Ñà˜ñZóõ±J[0É÷u~*äÍæ¸ÐwC”Û‘ê†DÑm%›•Îc¸$QÆT˜¯þ-ecx£‹´pX"Øm[K\Ùdè¦Æ‹õ»¥êº/nÂÉr)>e‚¦ÞÆ=ä¸õ`ÿ±TÂÆUêÀëarchive-zip-0.12.0/spec/zlib/fixtures/compressed_file_minwin.bin0000644000004100000410000000024613513326431025027 0ustar www-datawww-data•%ŽAÂ0E÷9Å?Aïà\éŒkj¨‰¦E5ÖÓKã †o¬Î)+¼S.Œ–-Á ’Ñà˜ñZóõ±J[0É÷u~*äÍæ¸ÐwC”Û‘ê†DÑm%›•Îc¸$QÆT˜¯þ-ecx£‹´pX"Øm[K\Ùdè¦Æ‹õ»¥êº/nÂÉr)>e‚¦ÞÆ=ä¸õ`ÿ±TÂãCQëarchive-zip-0.12.0/spec/zlib/fixtures/classes.rb0000644000004100000410000000271713513326431021600 0ustar www-datawww-data# encoding: UTF-8 class ZlibSpecs def self.compressed_data File.open( File.join(File.dirname(__FILE__), 'compressed_file.bin'), 'rb' ) do |f| block_given? ? yield(f) : f.read end end def self.compressed_data_nocomp File.open( File.join(File.dirname(__FILE__), 'compressed_file_nocomp.bin'), 'rb' ) do |f| block_given? ? yield(f) : f.read end end def self.compressed_data_minwin File.open( File.join(File.dirname(__FILE__), 'compressed_file_minwin.bin'), 'rb' ) do |f| block_given? ? yield(f) : f.read end end def self.compressed_data_minmem File.open( File.join(File.dirname(__FILE__), 'compressed_file_minmem.bin'), 'rb' ) do |f| block_given? ? yield(f) : f.read end end def self.compressed_data_huffman File.open( File.join(File.dirname(__FILE__), 'compressed_file_huffman.bin'), 'rb' ) do |f| block_given? ? yield(f) : f.read end end def self.compressed_data_gzip File.open( File.join(File.dirname(__FILE__), 'compressed_file_gzip.bin'), 'rb' ) do |f| block_given? ? yield(f) : f.read end end def self.compressed_data_raw File.open( File.join(File.dirname(__FILE__), 'compressed_file_raw.bin'), 'rb' ) do |f| block_given? ? yield(f) : f.read end end def self.test_data File.open(File.join(File.dirname(__FILE__), 'raw_file.txt'), 'rb') do |f| f.read end end end archive-zip-0.12.0/spec/zlib/fixtures/raw_file.txt0000644000004100000410000000035313513326431022141 0ustar www-datawww-datatest This is a file with test data. The quick brown fox jumps over the lazy dog. Mary had a little lamb Whose fleece was white as snow And everywhere that Mary went The lamb was sure to go She sells sea shells down by the sea shore. archive-zip-0.12.0/spec/zlib/fixtures/compressed_file_nocomp.bin0000644000004100000410000000036613513326431025024 0ustar www-datawww-dataxëÿtest This is a file with test data. The quick brown fox jumps over the lazy dog. Mary had a little lamb Whose fleece was white as snow And everywhere that Mary went The lamb was sure to go She sells sea shells down by the sea shore. ãCQëarchive-zip-0.12.0/spec/zlib/fixtures/compressed_file_minmem.bin0000644000004100000410000000027313513326431025010 0ustar www-datawww-dataxœÍÁ 1 ÀªØ ® à‡ÄÛwqˆ!Câ#„ê1Ò>Vi׸[¸déð’ÆË0D2Zœ¯C¶Ö¦£"é÷cvè›̹Ðw"êm áLm"Sôµ"fåû®Y;#æÍ¨cd1†—^u„S`_›ã·ŒÔ¢T ™‰% `“ÊSóJÀ.™ÖX\ R¯žÏÅ ”(NÍÉЦ&*g€™) G&U‚Î/JÕããCQëarchive-zip-0.12.0/spec/zlib/zwriter/0000755000004100000410000000000013513326431017444 5ustar www-datawww-dataarchive-zip-0.12.0/spec/zlib/zwriter/tell_spec.rb0000644000004100000410000000134613513326431021747 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/support/zlib' require 'archive/support/binary_stringio' describe "Zlib::ZWriter#tell" do it "returns the current position of the stream" do sio = BinaryStringIO.new Zlib::ZWriter.open(sio) do |zw| zw.tell.must_equal 0 zw.write('test1') zw.tell.must_equal 5 zw.write('test2') zw.tell.must_equal 10 zw.rewind zw.tell.must_equal 0 end end it "raises IOError on closed stream" do delegate = MiniTest::Mock.new delegate.expect(:write, 8, [String]) lambda do Zlib::ZWriter.open(delegate) { |zw| zw }.tell end.must_raise IOError end end archive-zip-0.12.0/spec/zlib/zwriter/rewind_spec.rb0000644000004100000410000000143113513326431022272 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/support/zlib' require 'archive/support/binary_stringio' describe "Zlib::ZWriter#rewind" do it "can rewind the stream when the delegate responds to rewind" do sio = BinaryStringIO.new Zlib::ZWriter.open(sio) do |zw| zw.write('test') zw.rewind zw.write(ZlibSpecs.test_data) end sio.string.must_equal ZlibSpecs.compressed_data end it "raises Errno::EINVAL when attempting to rewind the stream when the delegate does not respond to rewind" do delegate = MiniTest::Mock.new delegate.expect(:write, 8, [String]) Zlib::ZWriter.open(delegate) do |zw| lambda { zw.rewind }.must_raise Errno::EINVAL end end end archive-zip-0.12.0/spec/zlib/zwriter/write_spec.rb0000644000004100000410000000132613513326431022137 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/support/zlib' require 'archive/support/binary_stringio' describe "Zlib::ZWriter#write" do it "calls the write method of the delegate" do delegate = MiniTest::Mock.new delegate.expect( :write, ZlibSpecs.compressed_data.size, [ZlibSpecs.compressed_data] ) Zlib::ZWriter.open(delegate) do |zw| zw.write(ZlibSpecs.test_data) end end it "compresses data" do compressed_data = BinaryStringIO.new Zlib::ZWriter.open(compressed_data) do |zw| zw.write(ZlibSpecs.test_data) end compressed_data.string.must_equal ZlibSpecs.compressed_data end end archive-zip-0.12.0/spec/zlib/zwriter/open_spec.rb0000644000004100000410000000405213513326431021745 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/support/zlib' require 'archive/support/binary_stringio' describe "Zlib::ZWriter.open" do it "returns a new instance when run without a block" do zw = Zlib::ZWriter.open(BinaryStringIO.new) zw.class.must_equal Zlib::ZWriter zw.close end it "executes a block with a new instance as an argument" do Zlib::ZWriter.open(BinaryStringIO.new) { |zr| zr.class.must_equal Zlib::ZWriter } end it "closes the object after executing a block" do Zlib::ZWriter.open(BinaryStringIO.new) { |zr| zr }.closed?.must_equal true end it "provides default settings for level, window_bits, mem_level, and strategy" do data = ZlibSpecs.test_data compressed_data = BinaryStringIO.new Zlib::ZWriter.open(compressed_data) { |zw| zw.write(data) } compressed_data.string.must_equal ZlibSpecs.compressed_data end it "allows level to be set" do data = ZlibSpecs.test_data compressed_data = BinaryStringIO.new Zlib::ZWriter.open(compressed_data, Zlib::NO_COMPRESSION) do |zw| zw.write(data) end compressed_data.string.must_equal ZlibSpecs.compressed_data_nocomp end it "allows window_bits to be set" do data = ZlibSpecs.test_data compressed_data = BinaryStringIO.new Zlib::ZWriter.open(compressed_data, nil, 8) { |zw| zw.write(data) } compressed_data.string.must_equal ZlibSpecs.compressed_data_minwin end it "allows mem_level to be set" do data = ZlibSpecs.test_data compressed_data = BinaryStringIO.new Zlib::ZWriter.open(compressed_data, nil, nil, 1) { |zw| zw.write(data) } compressed_data.string.must_equal ZlibSpecs.compressed_data_minmem end it "allows strategy to be set" do data = ZlibSpecs.test_data compressed_data = BinaryStringIO.new Zlib::ZWriter.open( compressed_data, nil, nil, nil, Zlib::HUFFMAN_ONLY ) do |zw| zw.write(data) end compressed_data.string.must_equal ZlibSpecs.compressed_data_huffman end end archive-zip-0.12.0/spec/zlib/zwriter/compressed_size_spec.rb0000644000004100000410000000106413513326431024202 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/support/zlib' require 'archive/support/binary_stringio' describe "Zlib::ZWriter#compressed_size" do it "returns the number of bytes of compressed data" do compressed_data = BinaryStringIO.new closed_zw = Zlib::ZWriter.open(compressed_data, nil, -15) do |zw| zw.sync = true zw.write(ZlibSpecs.test_data) zw.compressed_size.must_be :>=, 0 zw end closed_zw.compressed_size.must_equal 160 end end archive-zip-0.12.0/spec/zlib/zwriter/checksum_spec.rb0000644000004100000410000000240213513326431022603 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/support/zlib' require 'archive/support/binary_stringio' describe "Zlib::ZWriter#checksum" do it "computes the ADLER32 checksum of zlib formatted data" do compressed_data = BinaryStringIO.new closed_zw = Zlib::ZWriter.open(compressed_data, nil, 15) do |zw| zw.write(ZlibSpecs.test_data) zw.flush zw.checksum.must_equal Zlib.adler32(ZlibSpecs.test_data) zw end closed_zw.checksum.must_equal Zlib.adler32(ZlibSpecs.test_data) end it "computes the CRC32 checksum of gzip formatted data" do compressed_data = BinaryStringIO.new closed_zw = Zlib::ZWriter.open(compressed_data, nil, 31) do |zw| zw.write(ZlibSpecs.test_data) zw.flush zw.checksum.must_equal Zlib.crc32(ZlibSpecs.test_data) zw end closed_zw.checksum.must_equal Zlib.crc32(ZlibSpecs.test_data) end it "does not compute a checksum for raw zlib data" do compressed_data = BinaryStringIO.new closed_zw = Zlib::ZWriter.open(compressed_data, nil, -15) do |zw| zw.write(ZlibSpecs.test_data) zw.flush zw.checksum.must_be_nil zw end closed_zw.checksum.must_be_nil end end archive-zip-0.12.0/spec/zlib/zwriter/seek_spec.rb0000644000004100000410000000347513513326431021743 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/support/zlib' require 'archive/support/binary_stringio' describe "Zlib::ZWriter#seek" do it "can seek to the beginning of the stream when the delegate responds to rewind" do compressed_data = BinaryStringIO.new Zlib::ZWriter.open(compressed_data) do |c| c.write('test') c.seek(0).must_equal 0 end end it "raises Errno::EINVAL when attempting to seek to the beginning of the stream when the delegate does not respond to rewind" do delegate = MiniTest::Mock.new delegate.expect(:write, 8, [String]) Zlib::ZWriter.open(delegate) do |c| lambda { c.seek(0) }.must_raise Errno::EINVAL end end it "raises Errno::EINVAL when seeking forward or backward from the current position of the stream" do compressed_data = BinaryStringIO.new Zlib::ZWriter.open(compressed_data) do |c| c.write('test') lambda { c.seek(1, IO::SEEK_CUR) }.must_raise Errno::EINVAL lambda { c.seek(-1, IO::SEEK_CUR) }.must_raise Errno::EINVAL end end it "raises Errno::EINVAL when seeking a non-zero offset relative to the beginning of the stream" do compressed_data = BinaryStringIO.new Zlib::ZWriter.open(compressed_data) do |c| lambda { c.seek(-1, IO::SEEK_SET) }.must_raise Errno::EINVAL lambda { c.seek(1, IO::SEEK_SET) }.must_raise Errno::EINVAL end end it "raises Errno::EINVAL when seeking relative to the end of the stream" do compressed_data = BinaryStringIO.new Zlib::ZWriter.open(compressed_data) do |c| lambda { c.seek(0, IO::SEEK_END) }.must_raise Errno::EINVAL lambda { c.seek(-1, IO::SEEK_END) }.must_raise Errno::EINVAL lambda { c.seek(1, IO::SEEK_END) }.must_raise Errno::EINVAL end end end archive-zip-0.12.0/spec/zlib/zwriter/close_spec.rb0000644000004100000410000000053213513326431022110 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/support/zlib' require 'archive/support/binary_stringio' describe "Zlib::ZWriter.close" do it "closes the stream" do zw = Zlib::ZWriter.new(BinaryStringIO.new) zw.close zw.closed?.must_equal true end end archive-zip-0.12.0/spec/zlib/zwriter/new_spec.rb0000644000004100000410000000340013513326431021571 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/support/zlib' require 'archive/support/binary_stringio' describe "Zlib::ZWriter.new" do it "returns a new instance" do zw = Zlib::ZWriter.new(BinaryStringIO.new) zw.class.must_equal Zlib::ZWriter zw.close end it "provides default settings for level, window_bits, mem_level, and strategy" do data = ZlibSpecs.test_data compressed_data = BinaryStringIO.new zw = Zlib::ZWriter.new(compressed_data) zw.write(data) zw.close compressed_data.string.must_equal ZlibSpecs.compressed_data end it "allows level to be set" do data = ZlibSpecs.test_data compressed_data = BinaryStringIO.new zw = Zlib::ZWriter.new(compressed_data, Zlib::NO_COMPRESSION) zw.write(data) zw.close compressed_data.string.must_equal ZlibSpecs.compressed_data_nocomp end it "allows window_bits to be set" do data = ZlibSpecs.test_data compressed_data = BinaryStringIO.new zw = Zlib::ZWriter.new(compressed_data, nil, 8) zw.write(data) zw.close compressed_data.string.must_equal ZlibSpecs.compressed_data_minwin end it "allows mem_level to be set" do data = ZlibSpecs.test_data compressed_data = BinaryStringIO.new zw = Zlib::ZWriter.new(compressed_data, nil, nil, 1) zw.write(data) zw.close compressed_data.string.must_equal ZlibSpecs.compressed_data_minmem end it "allows strategy to be set" do data = ZlibSpecs.test_data compressed_data = BinaryStringIO.new zw = Zlib::ZWriter.new(compressed_data, nil, nil, nil, Zlib::HUFFMAN_ONLY) zw.write(data) zw.close compressed_data.string.must_equal ZlibSpecs.compressed_data_huffman end end archive-zip-0.12.0/spec/zlib/zwriter/uncompressed_size_spec.rb0000644000004100000410000000107413513326431024546 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/support/zlib' require 'archive/support/binary_stringio' describe "Zlib::ZWriter#uncompressed_size" do it "returns the number of bytes of uncompressed data" do compressed_data = BinaryStringIO.new closed_zw = Zlib::ZWriter.open(compressed_data, nil, -15) do |zw| zw.sync = true zw.write(ZlibSpecs.test_data) zw.uncompressed_size.must_equal 235 zw end closed_zw.uncompressed_size.must_equal 235 end end archive-zip-0.12.0/spec/zlib/zreader/0000755000004100000410000000000013513326431017372 5ustar www-datawww-dataarchive-zip-0.12.0/spec/zlib/zreader/tell_spec.rb0000644000004100000410000000076513513326431021701 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/support/zlib' describe "Zlib::ZReader#tell" do it "returns the current position of the stream" do ZlibSpecs.compressed_data do |cd| Zlib::ZReader.open(cd) do |zr| zr.tell.must_equal 0 zr.read(4) zr.tell.must_equal 4 zr.read zr.tell.must_equal 235 zr.rewind zr.tell.must_equal 0 end end end end archive-zip-0.12.0/spec/zlib/zreader/rewind_spec.rb0000644000004100000410000000121713513326431022222 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/support/zlib' describe "Zlib::ZReader#rewind" do it "can rewind the stream when the delegate responds to rewind" do ZlibSpecs.compressed_data do |cd| Zlib::ZReader.open(cd) do |zr| zr.read(4) zr.rewind zr.read.must_equal ZlibSpecs.test_data end end end it "raises Errno::EINVAL when attempting to rewind the stream when the delegate does not respond to rewind" do Zlib::ZReader.open(Object.new) do |zr| lambda { zr.rewind }.must_raise Errno::EINVAL end end end archive-zip-0.12.0/spec/zlib/zreader/open_spec.rb0000644000004100000410000000254313513326431021676 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/support/zlib' require 'archive/support/binary_stringio' describe "Zlib::ZReader.open" do it "returns a new instance when run without a block" do Zlib::ZReader.open(BinaryStringIO.new).class.must_equal Zlib::ZReader end it "executes a block with a new instance as an argument" do Zlib::ZReader.open(BinaryStringIO.new) { |zr| zr.class.must_equal Zlib::ZReader } end it "closes the object after executing a block" do Zlib::ZReader.open(BinaryStringIO.new) { |zr| zr }.closed?.must_equal true end it "does not require window_bits to be set" do data = ZlibSpecs.test_data compressed_data = BinaryStringIO.new Zlib::ZWriter.open(compressed_data) do |zw| zw.write(data) end compressed_data.rewind Zlib::ZReader.open(compressed_data) do |zr| zr.read.must_equal data end end it "allows window_bits to be set" do data = ZlibSpecs.test_data compressed_data = BinaryStringIO.new window_bits = -Zlib::MAX_WBITS Zlib::ZWriter.open( compressed_data, Zlib::DEFAULT_COMPRESSION, window_bits ) do |zw| zw.write(data) end compressed_data.rewind Zlib::ZReader.open(compressed_data, window_bits) do |zr| zr.read.must_equal data end end end archive-zip-0.12.0/spec/zlib/zreader/compressed_size_spec.rb0000644000004100000410000000077213513326431024135 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/support/zlib' describe "Zlib::ZReader#compressed_size" do it "returns the number of bytes of compressed data" do closed_zr = ZlibSpecs.compressed_data_raw do |compressed_data| Zlib::ZReader.open(compressed_data, -15) do |zr| zr.read zr.compressed_size.must_equal 160 zr end end closed_zr.compressed_size.must_equal 160 end end archive-zip-0.12.0/spec/zlib/zreader/checksum_spec.rb0000644000004100000410000000214313513326431022533 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/support/zlib' describe "Zlib::ZReader#checksum" do it "computes the ADLER32 checksum of zlib formatted data" do closed_zr = ZlibSpecs.compressed_data do |f| Zlib::ZReader.open(f, 15) do |zr| zr.read zr.checksum.must_equal Zlib.adler32(ZlibSpecs.test_data) zr end end closed_zr.checksum.must_equal Zlib.adler32(ZlibSpecs.test_data) end it "computes the CRC32 checksum of gzip formatted data" do closed_zr = ZlibSpecs.compressed_data_gzip do |f| Zlib::ZReader.open(f, 31) do |zr| zr.read zr.checksum.must_equal Zlib.crc32(ZlibSpecs.test_data) zr end end closed_zr.checksum.must_equal Zlib.crc32(ZlibSpecs.test_data) end it "does not compute a checksum for raw zlib data" do closed_zr = ZlibSpecs.compressed_data_raw do |f| Zlib::ZReader.open(f, -15) do |zr| zr.read zr.checksum.must_be_nil zr end end closed_zr.checksum.must_be_nil end end archive-zip-0.12.0/spec/zlib/zreader/seek_spec.rb0000644000004100000410000000360213513326431021661 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/support/zlib' describe "Zlib::ZReader#seek" do it "can seek to the beginning of the stream when the delegate responds to rewind" do ZlibSpecs.compressed_data do |cd| Zlib::ZReader.open(cd) do |zr| zr.read(4) zr.seek(0).must_equal 0 end end end it "raises Errno::EINVAL when attempting to seek to the beginning of the stream when the delegate does not respond to rewind" do Zlib::ZReader.open(Object.new) do |zr| lambda { zr.seek(0) }.must_raise Errno::EINVAL end end it "raises Errno::EINVAL when seeking forward or backward from the current position of the stream" do ZlibSpecs.compressed_data do |cd| Zlib::ZReader.open(cd) do |zr| # Disable read buffering to avoid some seeking optimizations implemented # by IO::Like which allow seeking forward within the buffer. zr.fill_size = 0 zr.read(4) lambda { zr.seek(1, IO::SEEK_CUR) }.must_raise Errno::EINVAL lambda { zr.seek(-1, IO::SEEK_CUR) }.must_raise Errno::EINVAL end end end it "raises Errno::EINVAL when seeking a non-zero offset relative to the beginning of the stream" do ZlibSpecs.compressed_data do |cd| Zlib::ZReader.open(cd) do |zr| lambda { zr.seek(-1, IO::SEEK_SET) }.must_raise Errno::EINVAL lambda { zr.seek(1, IO::SEEK_SET) }.must_raise Errno::EINVAL end end end it "raises Errno::EINVAL when seeking relative to the end of the stream" do ZlibSpecs.compressed_data do |cd| Zlib::ZReader.open(cd) do |zr| lambda { zr.seek(0, IO::SEEK_END) }.must_raise Errno::EINVAL lambda { zr.seek(-1, IO::SEEK_END) }.must_raise Errno::EINVAL lambda { zr.seek(1, IO::SEEK_END) }.must_raise Errno::EINVAL end end end end archive-zip-0.12.0/spec/zlib/zreader/read_spec.rb0000644000004100000410000000322413513326431021645 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/support/zlib' require 'archive/support/binary_stringio' describe "Zlib::ZReader#read" do it "calls the read method of the delegate" do delegate = MiniTest::Mock.new delegate.expect(:read, nil, [Integer]) Zlib::ZReader.open(delegate) do |zr| # Capture and ignore the Zlib::BufError which is generated due to mocking. begin zr.read rescue Zlib::BufError end # Avoid warnings from zlib caused by closing the un-finished inflater. def zr.close; end end end it "decompresses compressed data" do ZlibSpecs.compressed_data do |cd| Zlib::ZReader.open(cd) do |zr| zr.read.must_equal ZlibSpecs.test_data end end end it "raises Zlib::DataError when reading invalid data" do Zlib::ZReader.open(BinaryStringIO.new('This is not compressed data')) do |zr| lambda { zr.read }.must_raise Zlib::DataError end end it "raises Zlib::BufError when reading truncated data" do truncated_data = ZlibSpecs.compressed_data { |cd| cd.read(100) } Zlib::ZReader.open(BinaryStringIO.new(truncated_data)) do |zr| lambda { zr.read }.must_raise Zlib::BufError # Avoid warnings from zlib caused by closing the un-finished inflater. def zr.close; end end end it "raises Zlib::BufError when reading empty data" do Zlib::ZReader.open(BinaryStringIO.new()) do |zr| lambda { zr.read }.must_raise Zlib::BufError # Avoid warnings from zlib caused by closing the un-finished inflater. def zr.close; end end end end archive-zip-0.12.0/spec/zlib/zreader/close_spec.rb0000644000004100000410000000053213513326431022036 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/support/zlib' require 'archive/support/binary_stringio' describe "Zlib::ZReader.close" do it "closes the stream" do zr = Zlib::ZReader.new(BinaryStringIO.new) zr.close zr.closed?.must_equal true end end archive-zip-0.12.0/spec/zlib/zreader/new_spec.rb0000644000004100000410000000204113513326431021517 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/support/zlib' require 'archive/support/binary_stringio' describe "Zlib::ZReader.new" do it "returns a new instance" do Zlib::ZReader.new(BinaryStringIO.new).class.must_equal Zlib::ZReader end it "does not require window_bits to be set" do data = ZlibSpecs.test_data compressed_data = BinaryStringIO.new Zlib::ZWriter.open(compressed_data) do |zw| zw.write(data) end compressed_data.rewind zr = Zlib::ZReader.new(compressed_data) zr.read.must_equal data zr.close end it "allows window_bits to be set" do data = ZlibSpecs.test_data compressed_data = BinaryStringIO.new window_bits = -Zlib::MAX_WBITS Zlib::ZWriter.open( compressed_data, Zlib::DEFAULT_COMPRESSION, window_bits ) do |zw| zw.write(data) end compressed_data.rewind zr = Zlib::ZReader.new(compressed_data, window_bits) zr.read.must_equal data zr.close end end archive-zip-0.12.0/spec/zlib/zreader/uncompressed_size_spec.rb0000644000004100000410000000100213513326431024463 0ustar www-datawww-data# encoding: UTF-8 require 'minitest/autorun' require File.expand_path('../../fixtures/classes', __FILE__) require 'archive/support/zlib' describe "Zlib::ZReader#uncompressed_size" do it "returns the number of bytes of uncompressed data" do closed_zr = ZlibSpecs.compressed_data_raw do |compressed_data| Zlib::ZReader.open(compressed_data, -15) do |zr| zr.read zr.uncompressed_size.must_equal 235 zr end end closed_zr.uncompressed_size.must_equal 235 end end archive-zip-0.12.0/LICENSE0000644000004100000410000000206213513326431015051 0ustar www-datawww-data(The MIT License) Copyright (c) 2019 Jeremy Bopp Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. archive-zip-0.12.0/Rakefile0000644000004100000410000001767013513326431015524 0ustar www-datawww-data# encoding: UTF-8 # -*- ruby -*- require 'rubygems' require 'erb' require 'rake/testtask' require 'rubygems/package_task' require 'rake/clean' require 'yard' # Load the gemspec file for this project. GEMSPEC = Dir['*.gemspec'].first SPEC = eval(File.read(GEMSPEC), nil, GEMSPEC) # The path to the version.rb file and a string to eval to find the version. VERSION_RB = "lib/#{SPEC.name.gsub('-', '/')}/version.rb" VERSION_REF = SPEC.name.split('-').map do |subname| subname.split('_').map(&:capitalize).join end.join('::') + "::VERSION" # A dynamically generated list of files that should match the manifest (the # combined contents of SPEC.files and SPEC.test_files). The idea is for this # list to contain all project files except for those that have been explicitly # excluded. This list will be compared with the manifest from the SPEC in order # to help catch the addition or removal of files to or from the project that # have not been accounted for either by an exclusion here or an inclusion in the # SPEC manifest. # # NOTE: # It is critical that the manifest is *not* automatically generated via globbing # and the like; otherwise, this will yield a simple comparison between # redundantly generated lists of files that probably will not protect the # project from the unintentional inclusion or exclusion of files in the # distribution. PKG_FILES = FileList.new(Dir.glob('**/*', File::FNM_DOTMATCH)) do |files| # Exclude anything that doesn't exist as well as directories. files.exclude {|file| ! File.exist?(file) || File.directory?(file)} # Exclude Git administrative files. files.exclude(%r{(^|[/\\])\.git(ignore|modules|keep)?([/\\]|$)}) # Exclude editor swap/temporary files. files.exclude('**/.*.sw?') # Exclude gemspec files. files.exclude('*.gemspec') # Exclude the README template file. files.exclude('README.md.erb') # Exclude resources for bundler. files.exclude('Gemfile', 'Gemfile.lock') files.exclude(%r{^.bundle([/\\]|$)}) files.exclude(%r{^vendor/bundle([/\\]|$)}) # Exclude generated content, except for the README file. files.exclude(%r{^(pkg|doc|.yardoc)([/\\]|$)}) # Exclude Rubinius compiled Ruby files. files.exclude('**/*.rbc') files.exclude('.rbx/**/*') end # Make sure that :clean and :clobber will not whack the repository files. CLEAN.exclude('.git/**') # Vim swap files are fair game for clean up. CLEAN.include('**/.*.sw?') # Returns the value of the VERSION environment variable as a Gem::Version object # assuming it is set and a valid Gem version string. Otherwise, raises an # exception. def get_version_argument version = ENV['VERSION'] if version.to_s.empty? raise "No version specified: Add VERSION=X.Y.Z to the command line" end begin Gem::Version.create(version.dup) rescue ArgumentError raise "Invalid version specified in `VERSION=#{version}'" end end # Performs an in place, per line edit of the file indicated by _path_ by calling # the sub method on each line and passing _pattern_, _replacement_, and _b_ as # arguments. def file_sub(path, pattern, replacement = nil, &b) tmp_path = "#{path}.tmp" File.open(path) do |infile| File.open(tmp_path, 'w') do |outfile| infile.each do |line| outfile.write(line.sub(pattern, replacement, &b)) end end end File.rename(tmp_path, path) end # Updates the version string in the gemspec file and a version.rb file it to the # string in _version_. def set_version(version) file_sub(GEMSPEC, /(\.version\s*=\s*).*/, "\\1'#{version}'") file_sub(VERSION_RB, /^(\s*VERSION\s*=\s*).*/, "\\1'#{version}'") end # Returns a string that is line wrapped at word boundaries, where each line is # no longer than _line_width_ characters. # # This is mostly lifted directly from ActionView::Helpers::TextHelper. def word_wrap(text, line_width = 80) text.split("\n").collect do |line| line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1\n").strip : line end * "\n" end desc 'Alias for build:gem' task :build => 'build:gem' # Build related tasks. namespace :build do # Ensure that the manifest is consulted when building the gem. Any # generated/compiled files should be available at that time. task :gem => :check_manifest # Create the gem and package tasks. Gem::PackageTask.new(SPEC).define desc 'Verify the manifest' task :check_manifest do manifest_files = (SPEC.files + SPEC.test_files).sort.uniq pkg_files = PKG_FILES.sort.uniq if manifest_files != pkg_files then common_files = manifest_files & pkg_files manifest_files -= common_files pkg_files -= common_files message = ["The manifest does not match the automatic file list."] unless manifest_files.empty? then message << " Extraneous files:\n " + manifest_files.join("\n ") end unless pkg_files.empty? message << " Missing files:\n " + pkg_files.join("\n ") end raise message.join("\n") end end # Creates the README.md file from a template, the license file and the gemspec # contents. file 'README.md' => ['README.md.erb', 'LICENSE', GEMSPEC] do spec = SPEC File.open('README.md', 'w') do |readme| readme.write( ERB.new(File.read('README.md.erb'), nil, '-').result(binding) ) end end end # Ensure that the clobber task also clobbers package files. task :clobber => 'build:clobber_package' # Create the documentation task. YARD::Rake::YardocTask.new # Ensure that the README file is (re)generated first. task :yard => 'README.md' # Gem related tasks. namespace :gem do desc 'Alias for build:gem' task :build => 'build:gem' desc 'Publish the gemfile' task :publish => ['version:check', :test, 'repo:tag', :build] do sh "gem push pkg/#{SPEC.name}-#{SPEC.version}*.gem" end end Rake::TestTask.new do |t| t.pattern = 'spec/**/*_spec.rb' end # Version string management tasks. namespace :version do desc 'Set the version for the project to a specified version' task :set do set_version(get_version_argument) end desc 'Set the version for the project back to 0.0.0' task :reset do set_version('0.0.0') end desc 'Check that all version strings are correctly set' task :check => ['version:check:spec', 'version:check:version_rb', 'version:check:news'] namespace :check do desc 'Check that the version in the gemspec is correctly set' task :spec do version = get_version_argument if version != SPEC.version raise "The given version `#{version}' does not match the gemspec version `#{SPEC.version}'" end end desc 'Check that the version in the version.rb file is correctly set' task :version_rb do version = get_version_argument begin load VERSION_RB internal_version = Gem::Version.create(eval(VERSION_REF)) if version != internal_version raise "The given version `#{version}' does not match the version.rb version `#{internal_version}'" end rescue ArgumentError raise "Invalid version specified in `#{VERSION_RB}'" end end desc 'Check that the NEWS.md file mentions the version' task :news do version = get_version_argument begin File.open('NEWS.md') do |news| unless news.each_line.any? {|l| l =~ /^## v#{Regexp.escape(version.to_s)} /} raise "The NEWS.md file does not mention version `#{version}'" end end rescue Errno::ENOENT raise 'No NEWS.md file found' end end end end # Repository and workspace management tasks. namespace :repo do desc 'Tag the current HEAD with the version string' task :tag => :check_workspace do version = get_version_argument sh "git tag -s -m 'Release v#{version}' v#{version}" end desc 'Ensure the workspace is fully committed and clean' task :check_workspace => ['README.md'] do unless `git status --untracked-files=all --porcelain`.empty? raise 'Workspace has been modified. Commit pending changes and try again.' end end end archive-zip-0.12.0/lib/0000755000004100000410000000000013513326431014612 5ustar www-datawww-dataarchive-zip-0.12.0/lib/archive/0000755000004100000410000000000013513326431016233 5ustar www-datawww-dataarchive-zip-0.12.0/lib/archive/zip/0000755000004100000410000000000013513326431017035 5ustar www-datawww-dataarchive-zip-0.12.0/lib/archive/zip/version.rb0000644000004100000410000000016013513326431021044 0ustar www-datawww-data# encoding: UTF-8 module Archive; class Zip # The current version of this gem. VERSION = '0.12.0' end; end archive-zip-0.12.0/lib/archive/zip/data_descriptor.rb0000644000004100000410000000426313513326431022536 0ustar www-datawww-data# encoding: UTF-8 module Archive; class Zip # Archive::Zip::DataDescriptor is a convenience class which bundles important # information concerning the compressed data in a ZIP archive entry and allows # easy comparisons between instances of itself. class DataDescriptor # Create a new instance of this class where crc32, # _compressed_size_, and _uncompressed_size_ are all integers representing a # CRC32 checksum of uncompressed data, the size of compressed data, and the # size of uncompressed data respectively. def initialize(crc32, compressed_size, uncompressed_size) @crc32 = crc32 @compressed_size = compressed_size @uncompressed_size = uncompressed_size end # A CRC32 checksum over some set of uncompressed data. attr_reader :crc32 # A count of the number of bytes of compressed data associated with a set of # uncompressed data. attr_reader :compressed_size # A count of the number of bytes of a set of uncompressed data. attr_reader :uncompressed_size # Compares the crc32 and uncompressed_size attributes of this object with # like-named attributes of _other_ and raises Archive::Zip::Error for any # mismatches. # # NOTE: The compressed_size attribute is not checked because # encrypted entries may have misleading compressed sizes. Checking only the # CRC32 and uncompressed size of the data should be sufficient to ensure # that an entry has been successfully extracted. def verify(other) unless crc32 == other.crc32 then raise Zip::Error, "CRC32 mismatch: #{crc32.to_s(16)} vs. #{other.crc32.to_s(16)}" end unless uncompressed_size == other.uncompressed_size then raise Zip::Error, "uncompressed size mismatch: #{uncompressed_size} vs. #{other.uncompressed_size}" end end # Writes the data wrapped in this object to _io_ which must be a writable, # IO-like object providing a _write_ method. Returns the number of bytes # written. def dump(io) io.write( [ crc32, compressed_size, uncompressed_size ].pack('VVV') ) end end end; end archive-zip-0.12.0/lib/archive/zip/codec.rb0000644000004100000410000000443413513326431020444 0ustar www-datawww-data# encoding: UTF-8 module Archive; class Zip # Archive::Zip::Codec is a factory class for generating codec object instances # based on the compression method and general purpose flag fields of ZIP # entries. When adding a new codec, add a mapping in the _CODECS_ constant # from the compression method field value reserved for the codec in the ZIP # specification to the class implementing the codec. See the implementations # of Archive::Zip::Codec::Deflate and Archive::Zip::Codec::Store for details # on implementing custom codecs. module Codec # A Hash mapping compression methods to compression codec implementations. # New compression codecs must add a mapping here when defined in order to be # used. COMPRESSION_CODECS = {} # A Hash mapping encryption methods to encryption codec implementations. # New encryption codecs must add a mapping here when defined in order to be # used. ENCRYPTION_CODECS = {} # Returns a new compression codec instance based on _compression_method_ and # _general_purpose_flags_. def self.create_compression_codec(compression_method, general_purpose_flags) # Load the standard compression codecs. require 'archive/zip/codec/deflate' require 'archive/zip/codec/store' codec = COMPRESSION_CODECS[compression_method] raise Zip::Error, 'unsupported compression codec' if codec.nil? codec.new(general_purpose_flags) end # Returns a new encryption codec instance based on _general_purpose_flags_. # # NOTE: The signature of this method will have to change in order to # support the strong encryption codecs. This is intended to be an internal # method anyway, so this fact should not cause major issues for users of # this library. def self.create_encryption_codec(general_purpose_flags) general_purpose_flags &= 0b0000000001000001 if general_purpose_flags == 0b0000000000000000 then require 'archive/zip/codec/null_encryption' codec = NullEncryption.new elsif general_purpose_flags == 0b0000000000000001 then require 'archive/zip/codec/traditional_encryption' codec = TraditionalEncryption.new end raise Zip::Error, 'unsupported encryption codec' if codec.nil? codec end end end; end archive-zip-0.12.0/lib/archive/zip/extra_field/0000755000004100000410000000000013513326431021323 5ustar www-datawww-dataarchive-zip-0.12.0/lib/archive/zip/extra_field/unix.rb0000644000004100000410000001135013513326431022633 0ustar www-datawww-data# encoding: UTF-8 require 'archive/zip/error' module Archive; class Zip; module ExtraField # Archive::Zip::Entry::ExtraField::Unix represents an extra field which # contains the last modified time, last accessed time, user name, and group # name for a ZIP archive entry. Times are in Unix time format (seconds since # the epoc). # # This class also optionally stores either major and minor numbers for devices # or a link target for either hard or soft links. Which is in use when given # and instance of this class depends upon the external file attributes for the # ZIP archive entry associated with this extra field. class Unix # The identifier reserved for this extra field type. ID = 0x000d # Register this extra field for use. EXTRA_FIELDS[ID] = self class << self # This method signature is part of the interface contract expected by # Archive::Zip::Entry for extra field objects. # # Parses _data_ which is expected to be a String formatted according to # the official ZIP specification. # # Raises Archive::Zip::ExtraFieldError if _data_ contains invalid data. def parse_central(data) unless data.length >= 12 then raise Zip::ExtraFieldError, "invalid size for Unix data: #{data.size}" end atime, mtime, uid, gid, rest = data.unpack('VVvva') new(Time.at(mtime), Time.at(atime), uid, gid, rest) end alias :parse_local :parse_central end # Creates a new instance of this class. _mtime_ and _atime_ should be Time # instances. _uid_ and _gid_ should be user and group IDs as Integers # respectively. _data_ should be a string containing either major and minor # device numbers consecutively packed as little endian, 4-byte, unsigned # integers (see the _V_ directive of Array#pack) or a path to use as a link # target. def initialize(mtime, atime, uid, gid, data = '') @header_id = ID @mtime = mtime @atime = atime @uid = uid @gid = gid @data = data end # Returns the header ID for this ExtraField. attr_reader :header_id # A Time object representing the last accessed time for an entry. attr_accessor :atime # A Time object representing the last modified time for an entry. attr_accessor :mtime # An integer representing the user ownership for an entry. attr_accessor :uid # An integer representing the group ownership for an entry. attr_accessor :gid # Attempts to return a two element array representing the major and minor # device numbers which may be stored in the variable data section of this # object. def device_numbers @data.unpack('VV') end # Takes a two element array containing major and minor device numbers and # stores the numbers into the variable data section of this object. def device_numbers=(major_minor) @data = major_minor.pack('VV') end # Attempts to return a string representing the path of a file which is # either a symlink or hard link target which may be stored in the variable # data section of this object. def link_target @data end # Takes a string containing the path to a file which is either a symlink or # a hardlink target and stores it in the variable data section of this # object. def link_target=(link_target) @data = link_target end # This method signature is part of the interface contract expected by # Archive::Zip::Entry for extra field objects. # # Merges the attributes of _other_ into this object and returns +self+. # # Raises ArgumentError if _other_ is not the same class as this object. def merge(other) if self.class != other.class then raise ArgumentError, "#{self.class} is not the same as #{other.class}" end @atime = other.atime @mtime = other.mtime @uid = other.uid @gid = other.gid @data = other.data self end # This method signature is part of the interface contract expected by # Archive::Zip::Entry for extra field objects. # # Returns a String suitable to writing to a local file record in a ZIP # archive file which contains the data for this object. def dump_local [ ID, 12 + @data.size, @atime.to_i, @mtime.to_i, @uid, @gid ].pack('vvVVvv') + @data end # This method signature is part of the interface contract expected by # Archive::Zip::Entry for extra field objects. # # Returns a String suitable to writing to a central file record in a ZIP # archive file which contains the data for this object. alias :dump_central :dump_local protected attr_reader :data end end; end; end archive-zip-0.12.0/lib/archive/zip/extra_field/raw.rb0000644000004100000410000000600213513326431022437 0ustar www-datawww-data# encoding: UTF-8 module Archive; class Zip; module ExtraField # Archive::Zip::Entry::ExtraField::Raw represents an unknown extra field. It # is used to store extra fields the Archive::Zip library does not directly # support. # # Do not use this class directly. Define a new class which supports the extra # field of interest directly instead. class Raw class << self # Simply stores _header_id_ and _data_ for later reproduction by # #dump_central. # This is essentially and alias for #new. def parse_central(header_id, data) new(header_id, data, true) end # Simply stores _header_id_ and _data_ for later reproduction by # #dump_local. # This is essentially and alias for #new. def parse_local(header_id, data) new(header_id, data, false) end end # Simply stores _header_id_ and _data_ for later reproduction by # #dump_central or #dump_local. _central_record_ indicates that this field # resides in the central file record for an entry when +true+. When # +false+, it indicates that this field resides in the local file record for # an entry. def initialize(header_id, data, central_record) @header_id = header_id @central_record_data = [] @local_record_data = [] if central_record then @central_record_data << data else @local_record_data << data end end # Returns the header ID for this ExtraField. attr_reader :header_id # Returns the data contained within this ExtraField. attr_reader :central_record_data attr_reader :local_record_data # This method signature is part of the interface contract expected by # Archive::Zip::Entry for extra field objects. # # Merges the attributes of _other_ into this object and returns +self+. # # Raises ArgumentError if _other_ does not have the same header ID as this # object. def merge(other) if header_id != other.header_id then raise ArgumentError, "Header ID mismatch: #{header_id} != #{other.header_id}" end @central_record_data += other.central_record_data @local_record_data += other.local_record_data self end # This method signature is part of the interface contract expected by # Archive::Zip::Entry for extra field objects. # # Returns a String suitable to writing to a central file record in a ZIP # archive file which contains the data for this object. def dump_central @central_record_data.collect do |data| [header_id, data.size].pack('vv') + data end end # This method signature is part of the interface contract expected by # Archive::Zip::Entry for extra field objects. # # Returns a String suitable to writing to a local file record in a ZIP # archive file which contains the data for this object. def dump_local @local_record_data.collect do |data| [header_id, data.size].pack('vv') + data end end end end; end; end archive-zip-0.12.0/lib/archive/zip/extra_field/extended_timestamp.rb0000644000004100000410000001535713513326431025546 0ustar www-datawww-data# encoding: UTF-8 require 'archive/zip/error' module Archive; class Zip; module ExtraField # Archive::Zip::Entry::ExtraField::ExtendedTimestamp represents an extra field # which optionally contains the last modified time, last accessed time, and # file creation time for a ZIP archive entry and stored in a Unix time format # (seconds since the epoc). class ExtendedTimestamp # The identifier reserved for this extra field type. ID = 0x5455 # Register this extra field for use. EXTRA_FIELDS[ID] = self class << self # This method signature is part of the interface contract expected by # Archive::Zip::Entry for extra field objects. # # Parses _data_ which is expected to be a String formatted according to # the documentation provided with InfoZip's sources. # # Raises Archive::Zip::ExtraFieldError if _data_ contains invalid data. def parse_central(data) unless data.size == 5 || data.size == 9 || data.size == 13 then raise Zip::ExtraFieldError, "invalid size for extended timestamp: #{data.size}" end flags, *times = data.unpack('CV*') mtime = nil atime = nil crtime = nil if flags & 0b001 != 0 then if times.size == 0 then # Report an error if the flags indicate that the last modified time # field should be present when it is not. raise Zip::ExtraFieldError, 'corrupt extended timestamp: last modified time field not present' end mtime = Time.at(times.shift) end if flags & 0b010 != 0 then # If parsing the central file record version of this field, this flag # may be set without having the corresponding time value. # Use the time value if available, but ignore it if it's missing. if times.size > 0 then atime = Time.at(times.shift) end end if flags & 0b100 != 0 then # If parsing the central file record version of this field, this flag # may be set without having the corresponding time value. # Use the time value if available, but ignore it if it's missing. if times.size > 0 then crtime = Time.at(times.shift) end end new(mtime, atime, crtime) end # This method signature is part of the interface contract expected by # Archive::Zip::Entry for extra field objects. # # Parses _data_ which is expected to be a String formatted according to # the documentation provided with InfoZip's sources. # # Raises Archive::Zip::ExtraFieldError if _data_ contains invalid data. def parse_local(data) unless data.size == 5 || data.size == 9 || data.size == 13 then raise Zip::ExtraFieldError, "invalid size for extended timestamp: #{data.size}" end flags, *times = data.unpack('CV*') mtime = nil atime = nil crtime = nil if flags & 0b001 != 0 then if times.size == 0 then # Report an error if the flags indicate that the last modified time # field should be present when it is not. raise Zip::ExtraFieldError, 'corrupt extended timestamp: last modified time field not present' end mtime = Time.at(times.shift) end if flags & 0b010 != 0 then if times.size == 0 then # Report an error if the flags indicate that the last modified time # field should be present when it is not. raise Zip::ExtraFieldError, 'corrupt extended timestamp: last accessed time field not present' end atime = Time.at(times.shift) end if flags & 0b100 != 0 then if times.size == 0 then # Report an error if the flags indicate that the file creation time # field should be present when it is not. raise Zip::ExtraFieldError, 'corrupt extended timestamp: file creation time field not present' end crtime = Time.at(times.shift) end new(mtime, atime, crtime) end end # Creates a new instance of this class. _mtime_, _atime_, and _crtime_ # should be Time instances or +nil+. When set to +nil+ the field is # considered to be unset and will not be stored in the archive. def initialize(mtime, atime, crtime) @header_id = ID self.mtime = mtime unless mtime.nil? self.atime = atime unless atime.nil? self.crtime = crtime unless crtime.nil? end # Returns the header ID for this ExtraField. attr_reader :header_id # The last modified time for an entry. Set to either a Time instance or # +nil+. attr_accessor :mtime # The last accessed time for an entry. Set to either a Time instance or # +nil+. attr_accessor :atime # The creation time for an entry. Set to either a Time instance or +nil+. attr_accessor :crtime # This method signature is part of the interface contract expected by # Archive::Zip::Entry for extra field objects. # # Merges the attributes of _other_ into this object and returns +self+. # # Raises ArgumentError if _other_ is not the same class as this object. def merge(other) if self.class != other.class then raise ArgumentError, "#{self.class} is not the same as #{other.class}" end @mtime = other.mtime unless other.mtime.nil? @atime = other.atime unless other.atime.nil? @crtime = other.crtime unless other.crtime.nil? self end # This method signature is part of the interface contract expected by # Archive::Zip::Entry for extra field objects. # # Returns a String suitable to writing to a central file record in a ZIP # archive file which contains the data for this object. def dump_central times = [] times << mtime.to_i unless mtime.nil? ([ID, 4 * times.size + 1, flags] + times).pack('vvC' + 'V' * times.size) end # This method signature is part of the interface contract expected by # Archive::Zip::Entry for extra field objects. # # Returns a String suitable to writing to a local file record in a ZIP # archive file which contains the data for this object. def dump_local times = [] times << mtime.to_i unless mtime.nil? times << atime.to_i unless atime.nil? times << crtime.to_i unless crtime.nil? ([ID, 4 * times.size + 1, flags] + times).pack('vvC' + 'V' * times.size) end private def flags flags = 0 flags |= 0b001 unless mtime.nil? flags |= 0b010 unless atime.nil? flags |= 0b100 unless crtime.nil? flags end end end; end; end archive-zip-0.12.0/lib/archive/zip/codec/0000755000004100000410000000000013513326431020112 5ustar www-datawww-dataarchive-zip-0.12.0/lib/archive/zip/codec/null_encryption.rb0000644000004100000410000002051613513326431023667 0ustar www-datawww-data# encoding: UTF-8 require 'archive/support/io-like' require 'archive/zip/codec' module Archive; class Zip; module Codec # Archive::Zip::Codec::NullEncryption is a handle for an encryption codec # which passes data through itself unchanged. class NullEncryption # Archive::Zip::Codec::NullEncryption::Encrypt is a writable, IO-like object # which writes all data written to it directly to a delegate IO object. A # _close_ method is also provided which can optionally closed the delegate # object. class Encrypt include IO::Like # Creates a new instance of this class with the given argument using #new # and then passes the instance to the given block. The #close method is # guaranteed to be called after the block completes. # # Equivalent to #new if no block is given. def self.open(io) encrypt_io = new(io) return encrypt_io unless block_given? begin yield(encrypt_io) ensure encrypt_io.close unless encrypt_io.closed? end end # Creates a new instance of this class using _io_ as a data sink. _io_ # must be writable and must provide a write method as IO does or errors # will be raised when performing write operations. # # The _flush_size_ attribute is set to 0 by default under the # assumption that _io_ is already buffered. def initialize(io) @io = io # Keep track of the total number of bytes written. @total_bytes_in = 0 # Assume that the delegate IO object is already buffered. self.flush_size = 0 end # Closes this object so that further write operations will fail. If # _close_delegate_ is +true+, the delegate object used as a data sink will # also be closed using its close method. def close(close_delegate = true) super() @io.close if close_delegate end private # Allows resetting this object and the delegate object back to the # beginning of the stream or reporting the current position in the stream. # # Raises Errno::EINVAL unless _offset_ is 0 and _whence_ is # either IO::SEEK_SET or IO::SEEK_CUR. Raises Errno::EINVAL if _whence_ # is IO::SEEK_SEK and the delegate object does not respond to the _rewind_ # method. def unbuffered_seek(offset, whence = IO::SEEK_SET) unless offset == 0 && ((whence == IO::SEEK_SET && @io.respond_to?(:rewind)) || whence == IO::SEEK_CUR) then raise Errno::EINVAL end case whence when IO::SEEK_SET @io.rewind @total_bytes_in = 0 when IO::SEEK_CUR @total_bytes_in end end # Writes _string_ to the delegate IO object and returns the result. def unbuffered_write(string) bytes_written = @io.write(string) @total_bytes_in += bytes_written bytes_written end end # Archive::Zip::Codec::NullEncryption::Decrypt is a readable, IO-like object # which reads data directly from a delegate IO object, making no changes. A # _close_ method is also provided which can optionally closed the delegate # object. class Decrypt include IO::Like # Creates a new instance of this class with the given argument using #new # and then passes the instance to the given block. The #close method is # guaranteed to be called after the block completes. # # Equivalent to #new if no block is given. def self.open(io) decrypt_io = new(io) return decrypt_io unless block_given? begin yield(decrypt_io) ensure decrypt_io.close unless decrypt_io.closed? end end # Creates a new instance of this class using _io_ as a data source. _io_ # must be readable and provide a _read_ method as an IO instance would or # errors will be raised when performing read operations. # # This class has extremely limited seek capabilities. It is possible to # seek with an offset of 0 and a whence of IO::SEEK_CUR. # As a result, the _pos_ and _tell_ methods also work as expected. # # Due to certain optimizations within IO::Like#seek and if there is data # in the read buffer, the _seek_ method can be used to seek forward from # the current stream position up to the end of the buffer. Unless it is # known definitively how much data is in the buffer, it is best to avoid # relying on this behavior. # # If _io_ also responds to _rewind_, then the _rewind_ method of this # class can be used to reset the whole stream back to the beginning. Using # _seek_ of this class to seek directly to offset 0 using # IO::SEEK_SET for whence will also work in this case. # # Any other seeking attempts, will raise Errno::EINVAL exceptions. # # The _fill_size_ attribute is set to 0 by default under the # assumption that _io_ is already buffered. def initialize(io) @io = io # Keep track of the total number of bytes read. @total_bytes_out = 0 # Assume that the delegate IO object is already buffered. self.fill_size = 0 end # Closes this object so that further write operations will fail. If # _close_delegate_ is +true+, the delegate object used as a data source # will also be closed using its close method. def close(close_delegate = true) super() @io.close if close_delegate end private # Reads and returns at most _length_ bytes from the delegate IO object. # # Raises EOFError if there is no data to read. def unbuffered_read(length) buffer = @io.read(length) raise EOFError, 'end of file reached' if buffer.nil? @total_bytes_out += buffer.length buffer end # Allows resetting this object and the delegate object back to the # beginning of the stream or reporting the current position in the stream. # # Raises Errno::EINVAL unless _offset_ is 0 and _whence_ is # either IO::SEEK_SET or IO::SEEK_CUR. Raises Errno::EINVAL if _whence_ # is IO::SEEK_SEK and the delegate object does not respond to the _rewind_ # method. def unbuffered_seek(offset, whence = IO::SEEK_SET) unless offset == 0 && ((whence == IO::SEEK_SET && @io.respond_to?(:rewind)) || whence == IO::SEEK_CUR) then raise Errno::EINVAL end case whence when IO::SEEK_SET @io.rewind @total_bytes_out = 0 when IO::SEEK_CUR @total_bytes_out end end end # This method signature is part of the interface contract expected by # Archive::Zip::Entry for encryption codec objects. # # A convenience method for creating an # Archive::Zip::Codec::NullEncryption::Encrypt object using that class' open # method. def encryptor(io, password, &b) Encrypt.open(io, &b) end # This method signature is part of the interface contract expected by # Archive::Zip::Entry for encryption codec objects. # # A convenience method for creating an # Archive::Zip::Codec::NullEncryption::Decrypt object using that class' open # method. def decryptor(io, password, &b) Decrypt.open(io, &b) end # This method signature is part of the interface contract expected by # Archive::Zip::Entry for encryption codec objects. # # Returns an integer which indicates the version of the official ZIP # specification which introduced support for this encryption codec. def version_needed_to_extract 0x0014 end # This method signature is part of the interface contract expected by # Archive::Zip::Entry for encryption codec objects. # # Returns an integer representing the general purpose flags of a ZIP archive # entry using this encryption codec. def general_purpose_flags 0b0000000000000000 end # This method signature is part of the interface contract expected by # Archive::Zip::Entry for encryption codec objects. # # Returns the size of the encryption header in bytes. def header_size 0 end end end; end; end archive-zip-0.12.0/lib/archive/zip/codec/store.rb0000644000004100000410000002542313513326431021601 0ustar www-datawww-data# encoding: UTF-8 require 'archive/support/io-like' require 'archive/support/zlib' require 'archive/zip/codec' require 'archive/zip/data_descriptor' module Archive; class Zip; module Codec # Archive::Zip::Codec::Store is a handle for the store-unstore (no # compression) codec. class Store # Archive::Zip::Codec::Store::Compress is simply a writable, IO-like wrapper # around a writable, IO-like object which provides a CRC32 checksum of the # data written through it as well as the count of the total amount of data. # A _close_ method is also provided which can optionally close the delegate # object. In addition a convenience method is provided for generating # DataDescriptor objects based on the data which is passed through this # object. # # Instances of this class should only be accessed via the # Archive::Zip::Codec::Store#compressor method. class Compress include IO::Like # Creates a new instance of this class with the given argument using #new # and then passes the instance to the given block. The #close method is # guaranteed to be called after the block completes. # # Equivalent to #new if no block is given. def self.open(io) store_io = new(io) return store_io unless block_given? begin yield(store_io) ensure store_io.close unless store_io.closed? end end # Creates a new instance of this class using _io_ as a data sink. _io_ # must be writable and must provide a write method as IO does or errors # will be raised when performing write operations. # # The _flush_size_ attribute is set to 0 by default under the # assumption that _io_ is already buffered. def initialize(io) @io = io @crc32 = 0 @uncompressed_size = 0 # Assume that the delegate IO object is already buffered. self.flush_size = 0 end # Closes this object so that further write operations will fail. If # _close_delegate_ is +true+, the delegate object used as a data sink will # also be closed using its close method. def close(close_delegate = true) super() @io.close if close_delegate nil end # Returns an instance of Archive::Zip::DataDescriptor with information # regarding the data which has passed through this object to the delegate # object. The close or flush methods should be called before using this # method in order to ensure that any possibly buffered data is flushed to # the delegate object; otherwise, the contents of the data descriptor may # be inaccurate. def data_descriptor DataDescriptor.new( @crc32, @uncompressed_size, @uncompressed_size ) end private # Allows resetting this object and the delegate object back to the # beginning of the stream or reporting the current position in the stream. # # Raises Errno::EINVAL unless _offset_ is 0 and _whence_ is # either IO::SEEK_SET or IO::SEEK_CUR. Raises Errno::EINVAL if _whence_ # is IO::SEEK_SEK and the delegate object does not respond to the _rewind_ # method. def unbuffered_seek(offset, whence = IO::SEEK_SET) unless offset == 0 && ((whence == IO::SEEK_SET && @io.respond_to?(:rewind)) || whence == IO::SEEK_CUR) then raise Errno::EINVAL end case whence when IO::SEEK_SET @io.rewind @crc32 = 0 @uncompressed_size = 0 when IO::SEEK_CUR @uncompressed_size end end # Writes _string_ to the delegate object and returns the number of bytes # actually written. Updates the uncompressed_size and crc32 attributes as # a side effect. def unbuffered_write(string) bytes_written = @io.write(string) @uncompressed_size += bytes_written @crc32 = Zlib.crc32(string.slice(0, bytes_written), @crc32) bytes_written end end # Archive::Zip::Codec::Store::Decompress is a readable, IO-like wrapper # around a readable, IO-like object which provides a CRC32 checksum of the # data read through it as well as the count of the total amount of data. A # _close_ method is also provided which can optionally close the delegate # object. In addition a convenience method is provided for generating # DataDescriptor objects based on the data which is passed through this # object. # # Instances of this class should only be accessed via the # Archive::Zip::Codec::Store#decompressor method. class Decompress include IO::Like # Creates a new instance of this class with the given arguments using #new # and then passes the instance to the given block. The #close method is # guaranteed to be called after the block completes. # # Equivalent to #new if no block is given. def self.open(io) unstore_io = new(io) return unstore_io unless block_given? begin yield(unstore_io) ensure unstore_io.close unless unstore_io.closed? end end # Creates a new instance of this class using _io_ as a data source. _io_ # must be readable and provide a _read_ method as an IO instance would or # errors will be raised when performing read operations. # # This class has extremely limited seek capabilities. It is possible to # seek with an offset of 0 and a whence of IO::SEEK_CUR. # As a result, the _pos_ and _tell_ methods also work as expected. # # Due to certain optimizations within IO::Like#seek and if there is data # in the read buffer, the _seek_ method can be used to seek forward from # the current stream position up to the end of the buffer. Unless it is # known definitively how much data is in the buffer, it is best to avoid # relying on this behavior. # # If _io_ also responds to _rewind_, then the _rewind_ method of this # class can be used to reset the whole stream back to the beginning. Using # _seek_ of this class to seek directly to offset 0 using # IO::SEEK_SET for whence will also work in this case. # # Any other seeking attempts, will raise Errno::EINVAL exceptions. # # The _fill_size_ attribute is set to 0 by default under the # assumption that _io_ is already buffered. def initialize(io) @io = io @crc32 = 0 @uncompressed_size = 0 # Assume that the delegate IO object is already buffered. self.fill_size = 0 end # Closes this object so that further read operations will fail. If # _close_delegate_ is +true+, the delegate object used as a data source # will also be closed using its close method. def close(close_delegate = true) super() @io.close if close_delegate nil end # Returns an instance of Archive::Zip::DataDescriptor with information # regarding the data which has passed through this object from the # delegate object. It is recommended to call the close method before # calling this in order to ensure that no further read operations change # the state of this object. def data_descriptor DataDescriptor.new( @crc32, @uncompressed_size, @uncompressed_size ) end private # Returns at most _length_ bytes from the delegate object. Updates the # uncompressed_size and crc32 attributes as a side effect. def unbuffered_read(length) buffer = @io.read(length) raise EOFError, 'end of file reached' if buffer.nil? @uncompressed_size += buffer.length @crc32 = Zlib.crc32(buffer, @crc32) buffer end # Allows resetting this object and the delegate object back to the # beginning of the stream or reporting the current position in the stream. # # Raises Errno::EINVAL unless _offset_ is 0 and _whence_ is # either IO::SEEK_SET or IO::SEEK_CUR. Raises Errno::EINVAL if _whence_ # is IO::SEEK_SEK and the delegate object does not respond to the _rewind_ # method. def unbuffered_seek(offset, whence = IO::SEEK_SET) unless offset == 0 && ((whence == IO::SEEK_SET && @io.respond_to?(:rewind)) || whence == IO::SEEK_CUR) then raise Errno::EINVAL end case whence when IO::SEEK_SET @io.rewind @crc32 = 0 @uncompressed_size = 0 when IO::SEEK_CUR @uncompressed_size end end end # The numeric identifier assigned to this compresion codec by the ZIP # specification. ID = 0 # Register this compression codec. COMPRESSION_CODECS[ID] = self # This method signature is part of the interface contract expected by # Archive::Zip::Entry for compression codec objects. # # Creates a new instance of this class. _general_purpose_flags_ is not # used. def initialize(general_purpose_flags = 0) end # This method signature is part of the interface contract expected by # Archive::Zip::Entry for compression codec objects. # # A convenience method for creating an Archive::Zip::Codec::Store::Compress # object using that class' open method. def compressor(io, &b) Compress.open(io, &b) end # This method signature is part of the interface contract expected by # Archive::Zip::Entry for compression codec objects. # # A convenience method for creating an # Archive::Zip::Codec::Store::Decompress object using that class' open # method. def decompressor(io, &b) Decompress.open(io, &b) end # This method signature is part of the interface contract expected by # Archive::Zip::Entry for compression codec objects. # # Returns an integer which indicates the version of the official ZIP # specification which introduced support for this compression codec. def version_needed_to_extract 0x000a end # This method signature is part of the interface contract expected by # Archive::Zip::Entry for compression codec objects. # # Returns an integer used to flag that this compression codec is used for a # particular ZIP archive entry. def compression_method ID end # This method signature is part of the interface contract expected by # Archive::Zip::Entry for compression codec objects. # # Returns 0 since this compression codec does not make use of # general purpose flags of ZIP archive entries. def general_purpose_flags 0 end end end; end; end archive-zip-0.12.0/lib/archive/zip/codec/traditional_encryption.rb0000644000004100000410000003602313513326431025227 0ustar www-datawww-data# encoding: UTF-8 require 'archive/support/integer' require 'archive/support/io-like' require 'archive/support/time' require 'archive/support/zlib' require 'archive/zip/codec' module Archive; class Zip; module Codec # Archive::Zip::Codec::TraditionalEncryption is a handle for the traditional # encryption codec. class TraditionalEncryption # Archive::Zip::Codec::TraditionalEncryption::Base provides some basic # methods which are shared between # Archive::Zip::Codec::TraditionalEncryption::Encrypt and # Archive::Zip::Codec::TraditionalEncryption::Decrypt. # # Do not use this class directly. class Base # Creates a new instance of this class. _io_ must be an IO-like object to # be used as a delegate for IO operations. _password_ should be the # encryption key. _mtime_ must be the last modified time of the entry to # be encrypted/decrypted. def initialize(io, password, mtime) @io = io @password = password.nil? ? '' : password @mtime = mtime initialize_keys end protected # The delegate IO-like object. attr_reader :io # The encryption key. attr_reader :password # The last modified time of the entry being encrypted. This is used in # the entryption header as a way to check the password. attr_reader :mtime private # Initializes the keys used for encrypting/decrypting data by setting the # keys to well known values and then processing them with the password. def initialize_keys @key0 = 0x12345678 @key1 = 0x23456789 @key2 = 0x34567890 @password.each_byte { |byte| update_keys(byte.chr) } nil end # Updates the keys following the ZIP specification using _char_, which # must be a single byte String. def update_keys(char) # For some reason not explained in the ZIP specification but discovered # in the source for InfoZIP, the old CRC value must first have its bits # flipped before processing. The new CRC value must have its bits # flipped as well for storage and later use. This applies to the # handling of @key0 and @key2. @key0 = ~Zlib.crc32(char, ~@key0) @key1 = ((@key1 + (@key0 & 0xff)) * 134775813 + 1) & 0xffffffff @key2 = ~Zlib.crc32((@key1 >> 24).chr, ~@key2) nil end # Returns the next decryption byte based on the current keys. def decrypt_byte temp = (@key2 | 2) & 0x0000ffff ((temp * (temp ^ 1)) >> 8) & 0x000000ff end end # Archive::Zip::Codec::TraditionalEncryption::Encrypt is a writable, IO-like # object which encrypts data written to it using the traditional encryption # algorithm as documented in the ZIP specification and writes the result to # a delegate IO object. A _close_ method is also provided which can # optionally close the delegate object. # # Instances of this class should only be accessed via the # Archive::Zip::Codec::TraditionalEncryption#compressor method. class Encrypt < Base include IO::Like # Creates a new instance of this class with the given argument using #new # and then passes the instance to the given block. The #close method is # guaranteed to be called after the block completes. # # Equivalent to #new if no block is given. def self.open(io, password, mtime) encrypt_io = new(io, password, mtime) return encrypt_io unless block_given? begin yield(encrypt_io) ensure encrypt_io.close unless encrypt_io.closed? end end # Creates a new instance of this class using _io_ as a data sink. _io_ # must be writable and must provide a write method as IO does or errors # will be raised when performing write operations. _password_ should be # the encryption key. _mtime_ must be the last modified time of the entry # to be encrypted/decrypted. # # The _flush_size_ attribute is set to 0 by default under the # assumption that _io_ is already buffered. def initialize(io, password, mtime) # Keep track of the total number of bytes written. # Set this here so that the call to #initialize_keys caused by the call # to super below does not cause errors in #unbuffered_write due to this # attribute being uninitialized. @total_bytes_in = 0 # This buffer is used to hold the encrypted version of the string most # recently sent to #unbuffered_write. @encrypt_buffer = '' super(io, password, mtime) # Assume that the delegate IO object is already buffered. self.flush_size = 0 end # Closes the stream after flushing the encryption buffer to the delegate. # If _close_delegate_ is +true+, the delegate object used as a data sink # will also be closed using its close method. # # Raises IOError if called more than once. def close(close_delegate = true) flush() begin until @encrypt_buffer.empty? do @encrypt_buffer.slice!(0, io.write(@encrypt_buffer)) end rescue Errno::EAGAIN, Errno::EINTR retry if write_ready? end super() io.close if close_delegate nil end private # Extend the inherited initialize_keys method to further initialize the # keys by encrypting and writing a 12 byte header to the delegate IO # object. def initialize_keys super # Create and encrypt a 12 byte header to protect the encrypted file data # from attack. The first 10 bytes are random, and the last 2 bytes are # the low order word in little endian byte order of the last modified # time of the entry in DOS format. header = '' 10.times do header << rand(256).chr end header << mtime.to_dos_time.pack[0, 2] # Take care to ensure that all bytes in the header are written. while header.size > 0 do begin header.slice!(0, unbuffered_write(header)) rescue Errno::EAGAIN, Errno::EINTR sleep(1) end end # Reset the total bytes written in order to disregard the header. @total_bytes_in = 0 nil end # Allows resetting this object and the delegate object back to the # beginning of the stream or reporting the current position in the stream. # # Raises Errno::EINVAL unless _offset_ is 0 and _whence_ is # either IO::SEEK_SET or IO::SEEK_CUR. Raises Errno::EINVAL if _whence_ # is IO::SEEK_SEK and the delegate object does not respond to the _rewind_ # method. def unbuffered_seek(offset, whence = IO::SEEK_SET) unless offset == 0 && ((whence == IO::SEEK_SET && @io.respond_to?(:rewind)) || whence == IO::SEEK_CUR) then raise Errno::EINVAL end case whence when IO::SEEK_SET io.rewind @encrypt_buffer = '' initialize_keys @total_bytes_in = 0 when IO::SEEK_CUR @total_bytes_in end end # Encrypts and writes _string_ to the delegate IO object. Returns the # number of bytes of _string_ written. def unbuffered_write(string) # First try to write out the contents of the encrypt buffer because if # that raises a failure we can let that pass up the call stack without # having polluted the encryption state. until @encrypt_buffer.empty? do @encrypt_buffer.slice!(0, io.write(@encrypt_buffer)) end # At this point we can encrypt the given string into a new buffer and # behave as if it was written. string.each_byte do |byte| temp = decrypt_byte @encrypt_buffer << (byte ^ temp).chr update_keys(byte.chr) end @total_bytes_in += string.length string.length end end # Archive::Zip::Codec::TraditionalEncryption::Decrypt is a readable, IO-like # object which decrypts data data it reads from a delegate IO object using # the traditional encryption algorithm as documented in the ZIP # specification. A _close_ method is also provided which can optionally # close the delegate object. # # Instances of this class should only be accessed via the # Archive::Zip::Codec::TraditionalEncryption#decompressor method. class Decrypt < Base include IO::Like # Creates a new instance of this class with the given argument using #new # and then passes the instance to the given block. The #close method is # guaranteed to be called after the block completes. # # Equivalent to #new if no block is given. def self.open(io, password, mtime) decrypt_io = new(io, password, mtime) return decrypt_io unless block_given? begin yield(decrypt_io) ensure decrypt_io.close unless decrypt_io.closed? end end # Creates a new instance of this class using _io_ as a data source. _io_ # must be readable and provide a _read_ method as an IO instance would or # errors will be raised when performing read operations. _password_ # should be the encryption key. _mtime_ must be the last modified time of # the entry to be encrypted/decrypted. # # This class has extremely limited seek capabilities. It is possible to # seek with an offset of 0 and a whence of IO::SEEK_CUR. # As a result, the _pos_ and _tell_ methods also work as expected. # # Due to certain optimizations within IO::Like#seek and if there is data # in the read buffer, the _seek_ method can be used to seek forward from # the current stream position up to the end of the buffer. Unless it is # known definitively how much data is in the buffer, it is best to avoid # relying on this behavior. # # If _io_ also responds to _rewind_, then the _rewind_ method of this # class can be used to reset the whole stream back to the beginning. Using # _seek_ of this class to seek directly to offset 0 using # IO::SEEK_SET for whence will also work in this case. # # Any other seeking attempts, will raise Errno::EINVAL exceptions. # # The _fill_size_ attribute is set to 0 by default under the # assumption that _io_ is already buffered. def initialize(io, password, mtime) # Keep track of the total number of bytes read. # Set this here so that the call to #initialize_keys caused by the call # to super below does not cause errors in #unbuffered_read due to this # attribute being uninitialized. @total_bytes_out = 0 super(io, password, mtime) # Assume that the delegate IO object is already buffered. self.fill_size = 0 end # Closes this object so that further write operations will fail. If # _close_delegate_ is +true+, the delegate object used as a data source # will also be closed using its close method. def close(close_delegate = true) super() io.close if close_delegate end private # Extend the inherited initialize_keys method to further initialize the # keys by encrypting and writing a 12 byte header to the delegate IO # object. def initialize_keys super # Load the 12 byte header taking care to ensure that all bytes are read. bytes_needed = 12 while bytes_needed > 0 do begin bytes_read = unbuffered_read(bytes_needed) bytes_needed -= bytes_read.size rescue Errno::EAGAIN, Errno::EINTR sleep(1) end end # Reset the total bytes read in order to disregard the header. @total_bytes_out = 0 nil end # Reads, decrypts, and returns at most _length_ bytes from the delegate IO # object. # # Raises EOFError if there is no data to read. def unbuffered_read(length) buffer = io.read(length) raise EOFError, 'end of file reached' if buffer.nil? @total_bytes_out += buffer.length 0.upto(buffer.length - 1) do |i| buffer[i] = (buffer[i].ord ^ decrypt_byte).chr update_keys(buffer[i].chr) end buffer end # Allows resetting this object and the delegate object back to the # beginning of the stream or reporting the current position in the stream. # # Raises Errno::EINVAL unless _offset_ is 0 and _whence_ is # either IO::SEEK_SET or IO::SEEK_CUR. Raises Errno::EINVAL if _whence_ # is IO::SEEK_SEK and the delegate object does not respond to the _rewind_ # method. def unbuffered_seek(offset, whence = IO::SEEK_SET) unless offset == 0 && ((whence == IO::SEEK_SET && @io.respond_to?(:rewind)) || whence == IO::SEEK_CUR) then raise Errno::EINVAL end case whence when IO::SEEK_SET io.rewind initialize_keys @total_bytes_out = 0 when IO::SEEK_CUR @total_bytes_out end end end # The last modified time of the entry to be processed. Set this before # calling #encryptor or #decryptor. attr_accessor :mtime # This method signature is part of the interface contract expected by # Archive::Zip::Entry for encryption codec objects. # # A convenience method for creating an # Archive::Zip::Codec::TraditionalEncryption::Encrypt object using that # class' open method. def encryptor(io, password, &b) Encrypt.open(io, password, mtime, &b) end # This method signature is part of the interface contract expected by # Archive::Zip::Entry for encryption codec objects. # # A convenience method for creating an # Archive::Zip::Codec::TraditionalEncryption::Decrypt object using that # class' open method. def decryptor(io, password, &b) Decrypt.open(io, password, mtime, &b) end # This method signature is part of the interface contract expected by # Archive::Zip::Entry for encryption codec objects. # # Returns an integer which indicates the version of the official ZIP # specification which introduced support for this encryption codec. def version_needed_to_extract 0x0014 end # This method signature is part of the interface contract expected by # Archive::Zip::Entry for encryption codec objects. # # Returns an integer representing the general purpose flags of a ZIP archive # entry using this encryption codec. def general_purpose_flags 0b0000000000000001 end # This method signature is part of the interface contract expected by # Archive::Zip::Entry for encryption codec objects. # # Returns the size of the encryption header in bytes. def header_size 12 end end end; end; end archive-zip-0.12.0/lib/archive/zip/codec/deflate.rb0000644000004100000410000002363013513326431022047 0ustar www-datawww-data# encoding: UTF-8 require 'archive/support/zlib' require 'archive/zip/codec' require 'archive/zip/data_descriptor' module Archive; class Zip; module Codec # Archive::Zip::Codec::Deflate is a handle for the deflate-inflate codec # as defined in Zlib which provides convenient interfaces for writing and # reading deflated streams. class Deflate # Archive::Zip::Codec::Deflate::Compress extends Zlib::ZWriter in order to # specify the standard Zlib options required by ZIP archives and to provide # a close method which can optionally close the delegate IO-like object. # In addition a convenience method is provided for generating DataDescriptor # objects based on the data which is passed through this object. # # Instances of this class should only be accessed via the # Archive::Zip::Codec::Deflate#compressor method. class Compress < Zlib::ZWriter # Creates a new instance of this class with the given arguments using #new # and then passes the instance to the given block. The #close method is # guaranteed to be called after the block completes. # # Equivalent to #new if no block is given. def self.open(io, compression_level) deflate_io = new(io, compression_level) return deflate_io unless block_given? begin yield(deflate_io) ensure deflate_io.close unless deflate_io.closed? end end # Creates a new instance of this class using _io_ as a data sink. _io_ # must be writable and must provide a _write_ method as IO does or errors # will be raised when performing write operations. _compression_level_ # must be one of Zlib::DEFAULT_COMPRESSION, Zlib::BEST_COMPRESSION, # Zlib::BEST_SPEED, or Zlib::NO_COMPRESSION and specifies the amount of # compression to be applied to the data stream. def initialize(io, compression_level) super(io, compression_level, -Zlib::MAX_WBITS) @crc32 = 0 end # The CRC32 checksum of the uncompressed data written using this object. # # NOTE: Anything still in the internal write buffer has not been # processed, so calling #flush prior to examining this attribute may be # necessary for an accurate computation. attr_reader :crc32 alias :checksum :crc32 # Closes this object so that further write operations will fail. If # _close_delegate_ is +true+, the delegate object used as a data sink will # also be closed using its close method. def close(close_delegate = true) super() delegate.close if close_delegate end # Returns an instance of Archive::Zip::DataDescriptor with information # regarding the data which has passed through this object to the delegate # object. The close or flush methods should be called before using this # method in order to ensure that any possibly buffered data is flushed to # the delegate object; otherwise, the contents of the data descriptor may # be inaccurate. def data_descriptor DataDescriptor.new(crc32, compressed_size, uncompressed_size) end private def unbuffered_seek(offset, whence = IO::SEEK_SET) result = super(offset, whence) @crc32 = 0 if whence == IO::SEEK_SET result end def unbuffered_write(string) result = super(string) @crc32 = Zlib.crc32(string, @crc32) result end end # Archive::Zip::Codec::Deflate::Decompress extends Zlib::ZReader in order to # specify the standard Zlib options required by ZIP archives and to provide # a close method which can optionally close the delegate IO-like object. # In addition a convenience method is provided for generating DataDescriptor # objects based on the data which is passed through this object. # # Instances of this class should only be accessed via the # Archive::Zip::Codec::Deflate#decompressor method. class Decompress < Zlib::ZReader # Creates a new instance of this class with the given arguments using #new # and then passes the instance to the given block. The #close method is # guaranteed to be called after the block completes. # # Equivalent to #new if no block is given. def self.open(io) inflate_io = new(io) return inflate_io unless block_given? begin yield(inflate_io) ensure inflate_io.close unless inflate_io.closed? end end # Creates a new instance of this class using _io_ as a data source. _io_ # must be readable and provide a _read_ method as IO does or errors will # be raised when performing read operations. If _io_ provides a _rewind_ # method, this class' _rewind_ method will be enabled. def initialize(io) super(io, -Zlib::MAX_WBITS) @crc32 = 0 end # The CRC32 checksum of the uncompressed data read using this object. # # NOTE: The contents of the internal read buffer are immediately # processed any time the internal buffer is filled, so this checksum is # only accurate if all data has been read out of this object. attr_reader :crc32 alias :checksum :crc32 # Closes this object so that further read operations will fail. If # _close_delegate_ is +true+, the delegate object used as a data source # will also be closed using its close method. def close(close_delegate = true) super() delegate.close if close_delegate end # Returns an instance of Archive::Zip::DataDescriptor with information # regarding the data which has passed through this object from the # delegate object. It is recommended to call the close method before # calling this in order to ensure that no further read operations change # the state of this object. def data_descriptor DataDescriptor.new(crc32, compressed_size, uncompressed_size) end private def unbuffered_read(length) result = super(length) @crc32 = Zlib.crc32(result, @crc32) result end def unbuffered_seek(offset, whence = IO::SEEK_SET) result = super(offset, whence) @crc32 = 0 if whence == IO::SEEK_SET result end end # The numeric identifier assigned to this compression codec by the ZIP # specification. ID = 8 # Register this compression codec. COMPRESSION_CODECS[ID] = self # A bit mask used to denote that Zlib's default compression level should be # used. NORMAL = 0b000 # A bit mask used to denote that Zlib's highest/slowest compression level # should be used. MAXIMUM = 0b010 # A bit mask used to denote that Zlib's lowest/fastest compression level # should be used. FAST = 0b100 # A bit mask used to denote that Zlib should not compress data at all. SUPER_FAST = 0b110 # This method signature is part of the interface contract expected by # Archive::Zip::Entry for compression codec objects. # # Creates a new instance of this class using bits 1 and 2 of # _general_purpose_flags_ to select a compression level to be used by # #compressor to set up a compression IO object. The constants NORMAL, # MAXIMUM, FAST, and SUPER_FAST can be used for _general_purpose_flags_ to # manually set the compression level. def initialize(general_purpose_flags = NORMAL) @compression_level = general_purpose_flags & 0b110 @zlib_compression_level = case @compression_level when NORMAL Zlib::DEFAULT_COMPRESSION when MAXIMUM Zlib::BEST_COMPRESSION when FAST Zlib::BEST_SPEED when SUPER_FAST Zlib::NO_COMPRESSION else raise Error, 'Invalid compression level' end end # This method signature is part of the interface contract expected by # Archive::Zip::Entry for compression codec objects. # # A convenience method for creating an # Archive::Zip::Codec::Deflate::Compress object using that class' open # method. The compression level for the open method is pulled from the # value of the _general_purpose_flags_ argument of new. def compressor(io, &b) Compress.open(io, @zlib_compression_level, &b) end # This method signature is part of the interface contract expected by # Archive::Zip::Entry for compression codec objects. # # A convenience method for creating an # Archive::Zip::Codec::Deflate::Decompress object using that class' open # method. def decompressor(io, &b) Decompress.open(io, &b) end # This method signature is part of the interface contract expected by # Archive::Zip::Entry for compression codec objects. # # Returns an integer which indicates the version of the official ZIP # specification which introduced support for this compression codec. def version_needed_to_extract 0x0014 end # This method signature is part of the interface contract expected by # Archive::Zip::Entry for compression codec objects. # # Returns an integer used to flag that this compression codec is used for a # particular ZIP archive entry. def compression_method ID end # This method signature is part of the interface contract expected by # Archive::Zip::Entry for compression codec objects. # # Returns an integer representing the general purpose flags of a ZIP archive # entry where bits 1 and 2 are set according to the compression level # selected for this object. All other bits are zero'd out. def general_purpose_flags @compression_level end end end; end; end archive-zip-0.12.0/lib/archive/zip/entry.rb0000644000004100000410000012561513513326431020535 0ustar www-datawww-data# encoding: UTF-8 require 'archive/support/ioextensions' require 'archive/support/binary_stringio' require 'archive/zip/codec/deflate' require 'archive/zip/codec/null_encryption' require 'archive/zip/codec/store' require 'archive/zip/codec/traditional_encryption' require 'archive/zip/data_descriptor' require 'archive/zip/error' require 'archive/zip/extra_field' module Archive; class Zip # The Archive::Zip::Entry mixin provides classes with methods implementing # many of the common features of all entry types. Some of these methods, such # as _dump_local_file_record_ and _dump_central_file_record_, are required by # Archive::Zip in order to store the entry into an archive. Those should be # left alone. Others, such as _ftype_ and mode=, are expected to be # overridden to provide sensible information for the new entry type. # # A class using this mixin must provide 2 methods: _extract_ and # _dump_file_data_. _extract_ should be a public method with the following # signature: # # def extract(options = {}) # ... # end # # This method should extract the contents of the entry to the filesystem. # _options_ should be an optional Hash containing a mapping of option names to # option values. Please refer to Archive::Zip::Entry::File#extract, # Archive::Zip::Entry::Symlink#extract, and # Archive::Zip::Entry::Directory#extract for examples of the options currently # supported. # # _dump_file_data_ should be a private method with the following signature: # # def dump_file_data(io) # ... # end # # This method should use the _write_ method of _io_ to write all file data. # _io_ will be a writable, IO-like object. # # The class methods from_file and parse are factories for creating the 3 kinds # of concrete entries currently implemented: File, Directory, and Symlink. # While it is possible to create new archives using custom entry # implementations, it is not possible to load those same entries from the # archive since the parse factory method does not know about them. Patches # to support new entry types are welcome. module Entry CFHRecord = Struct.new( :made_by_version, :extraction_version, :general_purpose_flags, :compression_method, :mtime, :crc32, :compressed_size, :uncompressed_size, :disk_number_start, :internal_file_attributes, :external_file_attributes, :local_header_position, :zip_path, :extra_fields, :comment ) LFHRecord = Struct.new( :extraction_version, :general_purpose_flags, :compression_method, :mtime, :crc32, :compressed_size, :uncompressed_size, :zip_path, :extra_fields, :compressed_data ) # When this flag is set in the general purpose flags, it indicates that the # entry's file data is encrypted using the original (weak) algorithm. FLAG_ENCRYPTED = 0b0001 # When this flag is set in the general purpose flags, it indicates that the # read data descriptor record for a local file record is located after the # entry's file data. FLAG_DATA_DESCRIPTOR_FOLLOWS = 0b1000 # Cleans up and returns _zip_path_ by eliminating . and .. references, # leading and trailing /'s, and runs of /'s. def self.expand_path(zip_path) result = [] source = zip_path.split('/') source.each do |e| next if e.empty? || e == '.' if e == '..' && ! (result.last.nil? || result.last == '..') then result.pop else result.push(e) end end result.shift while result.first == '..' result.join('/') end # Creates a new Entry based upon a file, symlink, or directory. _file_path_ # points to the source item. _options_ is a Hash optionally containing the # following: # :zip_path:: # The path for the entry in the archive where `/' is the file separator # character. This defaults to the basename of _file_path_ if unspecified. # :follow_symlinks:: # When set to +true+ (the default), symlinks are treated as the files or # directories to which they point. # :compression_codec:: # Specifies a proc, lambda, or class. If a proc or lambda is used, it # must take a single argument containing a zip entry and return a # compression codec class to be instantiated and used with the entry. # Otherwise, a compression codec class must be specified directly. When # unset, the default compression codec for each entry type is used. # :encryption_codec:: # Specifies a proc, lambda, or class. If a proc or lambda is used, it # must take a single argument containing a zip entry and return an # encryption codec class to be instantiated and used with the entry. # Otherwise, an encryption codec class must be specified directly. When # unset, the default encryption codec for each entry type is used. # # Raises Archive::Zip::EntryError if processing the given file path results # in a file not found error. def self.from_file(file_path, options = {}) zip_path = options.has_key?(:zip_path) ? expand_path(options[:zip_path]) : ::File.basename(file_path) follow_symlinks = options.has_key?(:follow_symlinks) ? options[:follow_symlinks] : true # Avoid repeatedly stat'ing the file by storing the stat structure once. begin stat = follow_symlinks ? ::File.stat(file_path) : ::File.lstat(file_path) rescue Errno::ENOENT if ::File.symlink?(file_path) then raise Zip::EntryError, "symlink at `#{file_path}' points to a non-existent file `#{::File.readlink(file_path)}'" else raise Zip::EntryError, "no such file or directory `#{file_path}'" end end # Ensure that zip paths for directories end with '/'. if stat.directory? then zip_path += '/' end # Instantiate the entry. if stat.symlink? then entry = Entry::Symlink.new(zip_path) entry.link_target = ::File.readlink(file_path) elsif stat.file? then entry = Entry::File.new(zip_path) entry.file_path = file_path elsif stat.directory? then entry = Entry::Directory.new(zip_path) else raise Zip::EntryError, "unsupported file type `#{stat.ftype}' for file `#{file_path}'" end # Set the compression and encryption codecs. unless options[:compression_codec].nil? then if options[:compression_codec].kind_of?(Proc) then entry.compression_codec = options[:compression_codec][entry].new else entry.compression_codec = options[:compression_codec].new end end unless options[:encryption_codec].nil? then if options[:encryption_codec].kind_of?(Proc) then entry.encryption_codec = options[:encryption_codec][entry].new else entry.encryption_codec = options[:encryption_codec].new end end # Set the entry's metadata. entry.uid = stat.uid entry.gid = stat.gid entry.mtime = stat.mtime entry.atime = stat.atime entry.mode = stat.mode entry end # Creates and returns a new entry object by parsing from the current # position of _io_. _io_ must be a readable, IO-like object which is # positioned at the start of a central file record following the signature # for that record. # # NOTE: For now _io_ MUST be seekable. # # Currently, the only entry objects returned are instances of # Archive::Zip::Entry::File, Archive::Zip::Entry::Directory, and # Archive::Zip::Entry::Symlink. Any other kind of entry will be mapped into # an instance of Archive::Zip::Entry::File. # # Raises Archive::Zip::EntryError for any other errors related to processing # the entry. def self.parse(io) # Parse the central file record and then use the information found there # to locate and parse the corresponding local file record. cfr = parse_central_file_record(io) next_record_position = io.pos io.seek(cfr.local_header_position) unless IOExtensions.read_exactly(io, 4) == LFH_SIGNATURE then raise Zip::EntryError, 'bad local file header signature' end lfr = parse_local_file_record(io, cfr.compressed_size) # Check to ensure that the contents of the central file record and the # local file record which are supposed to be duplicated are in fact the # same. compare_file_records(lfr, cfr) begin # Load the correct compression codec. compression_codec = Codec.create_compression_codec( cfr.compression_method, cfr.general_purpose_flags ) rescue Zip::Error => e raise Zip::EntryError, "`#{cfr.zip_path}': #{e.message}" end begin # Load the correct encryption codec. encryption_codec = Codec.create_encryption_codec( cfr.general_purpose_flags ) rescue Zip::Error => e raise Zip::EntryError, "`#{cfr.zip_path}': #{e.message}" end # Set up a data descriptor with expected values for later comparison. expected_data_descriptor = DataDescriptor.new( cfr.crc32, cfr.compressed_size, cfr.uncompressed_size ) # Create the entry. expanded_path = expand_path(cfr.zip_path) io_window = IOWindow.new(io, io.pos, cfr.compressed_size) if cfr.zip_path[-1..-1] == '/' then # This is a directory entry. entry = Entry::Directory.new(expanded_path, io_window) elsif (cfr.external_file_attributes >> 16) & 0770000 == 0120000 then # This is a symlink entry. entry = Entry::Symlink.new(expanded_path, io_window) else # Anything else is a file entry. entry = Entry::File.new(expanded_path, io_window) end # Set the expected data descriptor so that extraction can be verified. entry.expected_data_descriptor = expected_data_descriptor # Record the compression codec. entry.compression_codec = compression_codec # Record the encryption codec. entry.encryption_codec = encryption_codec # Set some entry metadata. entry.mtime = cfr.mtime # Only set mode bits for the entry if the external file attributes are # Unix-compatible. if cfr.made_by_version & 0xFF00 == 0x0300 then entry.mode = cfr.external_file_attributes >> 16 end entry.comment = cfr.comment cfr.extra_fields.each { |ef| entry.add_extra_field(ef) } lfr.extra_fields.each { |ef| entry.add_extra_field(ef) } # Return to the beginning of the next central directory record. io.seek(next_record_position) entry end private # Parses a central file record and returns a CFHRecord instance containing # the parsed data. _io_ must be a readable, IO-like object which is # positioned at the start of a central file record following the signature # for that record. def self.parse_central_file_record(io) cfr = CFHRecord.new cfr.made_by_version, cfr.extraction_version, cfr.general_purpose_flags, cfr.compression_method, dos_mtime, cfr.crc32, cfr.compressed_size, cfr.uncompressed_size, file_name_length, extra_fields_length, comment_length, cfr.disk_number_start, cfr.internal_file_attributes, cfr.external_file_attributes, cfr.local_header_position = IOExtensions.read_exactly(io, 42).unpack('vvvvVVVVvvvvvVV') cfr.zip_path = IOExtensions.read_exactly(io, file_name_length) cfr.extra_fields = parse_central_extra_fields( IOExtensions.read_exactly(io, extra_fields_length) ) cfr.comment = IOExtensions.read_exactly(io, comment_length) # Convert from MSDOS time to Unix time. cfr.mtime = DOSTime.new(dos_mtime).to_time cfr rescue EOFError raise Zip::EntryError, 'unexpected end of file' end # Parses a local file record and returns a LFHRecord instance containing the # parsed data. _io_ must be a readable, IO-like object which is positioned # at the start of a local file record following the signature for that # record. # # If the record to be parsed is flagged to have a trailing data descriptor # record, _expected_compressed_size_ must be set to an integer counting the # number of bytes of compressed data to skip in order to find the trailing # data descriptor record, and _io_ must be seekable by providing _pos_ and # pos= methods. def self.parse_local_file_record(io, expected_compressed_size = nil) lfr = LFHRecord.new lfr.extraction_version, lfr.general_purpose_flags, lfr.compression_method, dos_mtime, lfr.crc32, lfr.compressed_size, lfr.uncompressed_size, file_name_length, extra_fields_length = IOExtensions.read_exactly(io, 26).unpack('vvvVVVVvv') lfr.zip_path = IOExtensions.read_exactly(io, file_name_length) lfr.extra_fields = parse_local_extra_fields( IOExtensions.read_exactly(io, extra_fields_length) ) # Convert from MSDOS time to Unix time. lfr.mtime = DOSTime.new(dos_mtime).to_time if lfr.general_purpose_flags & FLAG_DATA_DESCRIPTOR_FOLLOWS > 0 then saved_pos = io.pos io.pos += expected_compressed_size # Because the ZIP specification has a history of murkiness, some # libraries create trailing data descriptor records with a preceding # signature while others do not. # This handles both cases. possible_signature = IOExtensions.read_exactly(io, 4) if possible_signature == DD_SIGNATURE then lfr.crc32, lfr.compressed_size, lfr.uncompressed_size = IOExtensions.read_exactly(io, 12).unpack('VVV') else lfr.crc32 = possible_signature.unpack('V')[0] lfr.compressed_size, lfr.uncompressed_size = IOExtensions.read_exactly(io, 8).unpack('VV') end io.pos = saved_pos end lfr rescue EOFError raise Zip::EntryError, 'unexpected end of file' end # Parses the extra fields for central file records and returns an array of # extra field objects. _bytes_ must be a String containing all of the extra # field data to be parsed. def self.parse_central_extra_fields(bytes) BinaryStringIO.open(bytes) do |io| extra_fields = [] while ! io.eof? do begin header_id, data_size = IOExtensions.read_exactly(io, 4).unpack('vv') data = IOExtensions.read_exactly(io, data_size) rescue ::EOFError raise EntryError, 'insufficient data available' end extra_fields << ExtraField.parse_central(header_id, data) end extra_fields end end # Parses the extra fields for local file records and returns an array of # extra field objects. _bytes_ must be a String containing all of the extra # field data to be parsed. def self.parse_local_extra_fields(bytes) BinaryStringIO.open(bytes) do |io| extra_fields = [] while ! io.eof? do begin header_id, data_size = IOExtensions.read_exactly(io, 4).unpack('vv') data = IOExtensions.read_exactly(io, data_size) rescue ::EOFError raise EntryError, 'insufficient data available' end extra_fields << ExtraField.parse_local(header_id, data) end extra_fields end end # Compares the local and the central file records found in _lfr_ and _cfr # respectively. Raises Archive::Zip::EntryError if the comparison fails. def self.compare_file_records(lfr, cfr) # Exclude the extra fields from the comparison since some implementations, # such as InfoZip, are known to have differences in the extra fields used # in local file records vs. central file records. if lfr.zip_path != cfr.zip_path then raise Zip::EntryError, "zip path differs between local and central file records: `#{lfr.zip_path}' != `#{cfr.zip_path}'" end if lfr.extraction_version != cfr.extraction_version then raise Zip::EntryError, "`#{cfr.zip_path}': extraction version differs between local and central file records" end if lfr.crc32 != cfr.crc32 then raise Zip::EntryError, "`#{cfr.zip_path}': CRC32 differs between local and central file records" end if lfr.compressed_size != cfr.compressed_size then raise Zip::EntryError, "`#{cfr.zip_path}': compressed size differs between local and central file records" end if lfr.uncompressed_size != cfr.uncompressed_size then raise Zip::EntryError, "`#{cfr.zip_path}': uncompressed size differs between local and central file records" end if lfr.general_purpose_flags != cfr.general_purpose_flags then raise Zip::EntryError, "`#{cfr.zip_path}': general purpose flag differs between local and central file records" end if lfr.compression_method != cfr.compression_method then raise Zip::EntryError, "`#{cfr.zip_path}': compression method differs between local and central file records" end if lfr.mtime != cfr.mtime then raise Zip::EntryError, "`#{cfr.zip_path}': last modified time differs between local and central file records" end end public # Creates a new, uninitialized Entry instance using the Store compression # method. The zip path is initialized to _zip_path_. _raw_data_, if # specified, must be a readable, IO-like object containing possibly # compressed/encrypted file data for the entry. It is intended to be used # primarily by the parse class method. def initialize(zip_path, raw_data = nil) self.zip_path = zip_path self.mtime = Time.now self.atime = @mtime self.uid = nil self.gid = nil self.mode = 0777 self.comment = '' self.expected_data_descriptor = nil self.compression_codec = Zip::Codec::Store.new self.encryption_codec = Zip::Codec::NullEncryption.new @raw_data = raw_data self.password = nil @extra_fields = [] end # The path for this entry in the ZIP archive. attr_reader :zip_path # The last accessed time. attr_accessor :atime # The last modified time. attr_accessor :mtime # The user ID of the owner of this entry. attr_accessor :uid # The group ID of the owner of this entry. attr_accessor :gid # The file mode/permission bits for this entry. attr_accessor :mode # The comment associated with this entry. attr_accessor :comment # An Archive::Zip::DataDescriptor instance which should contain the expected # CRC32 checksum, compressed size, and uncompressed size for the file data. # When not +nil+, this is used by #extract to confirm that the data # extraction was successful. attr_accessor :expected_data_descriptor # The selected compression codec. attr_accessor :compression_codec # The selected encryption codec. attr_accessor :encryption_codec # The password used with the encryption codec to encrypt or decrypt the file # data for an entry. attr_accessor :password # The raw, possibly compressed and/or encrypted file data for an entry. attr_accessor :raw_data # Sets the path in the archive for this entry to _zip_path_ after passing it # through Archive::Zip::Entry.expand_path and ensuring that the result is # not empty. def zip_path=(zip_path) @zip_path = Archive::Zip::Entry.expand_path(zip_path) if @zip_path.empty? then raise ArgumentError, "zip path expands to empty string" end end # Returns the file type of this entry as the symbol :unknown. # # Override this in concrete subclasses to return an appropriate symbol. def ftype :unknown end # Returns false. def file? false end # Returns false. def symlink? false end # Returns false. def directory? false end # Adds _extra_field_ as an extra field specification to *both* the central # file record and the local file record of this entry. # # If _extra_field_ is an instance of # Archive::Zip::Entry::ExtraField::ExtendedTimestamp, the values of that # field are used to set mtime and atime for this entry. If _extra_field_ is # an instance of Archive::Zip::Entry::ExtraField::Unix, the values of that # field are used to set mtime, atime, uid, and gid for this entry. def add_extra_field(extra_field) # Try to find an extra field with the same header ID already in the list # and merge the new one with that if one exists; otherwise, add the new # one to the list. existing_extra_field = @extra_fields.find do |ef| ef.header_id == extra_field.header_id end if existing_extra_field.nil? then @extra_fields << extra_field else extra_field = existing_extra_field.merge(extra_field) end # Set some attributes of this entry based on the settings in select types # of extra fields. if extra_field.kind_of?(ExtraField::ExtendedTimestamp) then self.mtime = extra_field.mtime unless extra_field.mtime.nil? self.atime = extra_field.atime unless extra_field.atime.nil? elsif extra_field.kind_of?(ExtraField::Unix) then self.mtime = extra_field.mtime unless extra_field.mtime.nil? self.atime = extra_field.atime unless extra_field.atime.nil? self.uid = extra_field.uid unless extra_field.uid.nil? self.gid = extra_field.gid unless extra_field.uid.nil? end self end # Writes the local file record for this entry to _io_, a writable, IO-like # object which provides a _write_ method. _local_file_record_position_ is # the offset within _io_ at which writing will begin. This is used so that # when writing to a non-seekable IO object it is possible to avoid calling # the _pos_ method of _io_. Returns the number of bytes written. # # NOTE: This method should only be called by Archive::Zip. def dump_local_file_record(io, local_file_record_position) @local_file_record_position = local_file_record_position bytes_written = 0 # Assume that no trailing data descriptor will be necessary. need_trailing_data_descriptor = false begin io.pos rescue Errno::ESPIPE # A trailing data descriptor is required for non-seekable IO. need_trailing_data_descriptor = true end if encryption_codec.class == Codec::TraditionalEncryption then # HACK: # According to the ZIP specification, a trailing data descriptor should # only be required when writing to non-seekable IO , but InfoZIP # *always* does this when using traditional encryption even though it # will also write the data descriptor in the usual place if possible. # Failure to emulate InfoZIP in this behavior will prevent InfoZIP # compatibility with traditionally encrypted entries. need_trailing_data_descriptor = true # HACK: # The InfoZIP implementation of traditional encryption requires that the # the last modified file time be used as part of the encryption header. # This is a deviation from the ZIP specification. encryption_codec.mtime = mtime end # Set the general purpose flags. general_purpose_flags = compression_codec.general_purpose_flags general_purpose_flags |= encryption_codec.general_purpose_flags if need_trailing_data_descriptor then general_purpose_flags |= FLAG_DATA_DESCRIPTOR_FOLLOWS end # Select the minimum ZIP specification version needed to extract this # entry. version_needed_to_extract = compression_codec.version_needed_to_extract if encryption_codec.version_needed_to_extract > version_needed_to_extract then version_needed_to_extract = encryption_codec.version_needed_to_extract end # Write the data. bytes_written += io.write(LFH_SIGNATURE) extra_field_data = local_extra_field_data bytes_written += io.write( [ version_needed_to_extract, general_purpose_flags, compression_codec.compression_method, mtime.to_dos_time.to_i, 0, 0, 0, zip_path.bytesize, extra_field_data.length ].pack('vvvVVVVvv') ) bytes_written += io.write(zip_path) bytes_written += io.write(extra_field_data) # Pipeline a compressor into an encryptor, write all the file data to the # compressor, and get a data descriptor from it. encryption_codec.encryptor(io, password) do |e| compression_codec.compressor(e) do |c| dump_file_data(c) c.close(false) @data_descriptor = DataDescriptor.new( c.data_descriptor.crc32, c.data_descriptor.compressed_size + encryption_codec.header_size, c.data_descriptor.uncompressed_size ) end e.close(false) end bytes_written += @data_descriptor.compressed_size # Write the trailing data descriptor if necessary. if need_trailing_data_descriptor then bytes_written += io.write(DD_SIGNATURE) bytes_written += @data_descriptor.dump(io) end begin # Update the data descriptor located before the compressed data for the # entry. saved_position = io.pos io.pos = @local_file_record_position + 14 @data_descriptor.dump(io) io.pos = saved_position rescue Errno::ESPIPE # Ignore a failed attempt to update the data descriptor. end bytes_written end # Writes the central file record for this entry to _io_, a writable, IO-like # object which provides a _write_ method. Returns the number of bytes # written. # # NOTE: This method should only be called by Archive::Zip. def dump_central_file_record(io) bytes_written = 0 # Assume that no trailing data descriptor will be necessary. need_trailing_data_descriptor = false begin io.pos rescue Errno::ESPIPE # A trailing data descriptor is required for non-seekable IO. need_trailing_data_descriptor = true end if encryption_codec.class == Codec::TraditionalEncryption then # HACK: # According to the ZIP specification, a trailing data descriptor should # only be required when writing to non-seekable IO , but InfoZIP # *always* does this when using traditional encryption even though it # will also write the data descriptor in the usual place if possible. # Failure to emulate InfoZIP in this behavior will prevent InfoZIP # compatibility with traditionally encrypted entries. need_trailing_data_descriptor = true end # Set the general purpose flags. general_purpose_flags = compression_codec.general_purpose_flags general_purpose_flags |= encryption_codec.general_purpose_flags if need_trailing_data_descriptor then general_purpose_flags |= FLAG_DATA_DESCRIPTOR_FOLLOWS end # Select the minimum ZIP specification version needed to extract this # entry. version_needed_to_extract = compression_codec.version_needed_to_extract if encryption_codec.version_needed_to_extract > version_needed_to_extract then version_needed_to_extract = encryption_codec.version_needed_to_extract end # Write the data. bytes_written += io.write(CFH_SIGNATURE) bytes_written += io.write( [ version_made_by, version_needed_to_extract, general_purpose_flags, compression_codec.compression_method, mtime.to_dos_time.to_i ].pack('vvvvV') ) bytes_written += @data_descriptor.dump(io) extra_field_data = central_extra_field_data bytes_written += io.write( [ zip_path.bytesize, extra_field_data.length, comment.length, 0, internal_file_attributes, external_file_attributes, @local_file_record_position ].pack('vvvvvVV') ) bytes_written += io.write(zip_path) bytes_written += io.write(extra_field_data) bytes_written += io.write(comment) bytes_written end private def version_made_by 0x0314 end def central_extra_field_data @central_extra_field_data = @extra_fields.collect do |extra_field| extra_field.dump_central end.join end def dummy # Add fields for time data if available. unless mtime.nil? && atime.nil? then @central_extra_field_data += ExtraField::ExtendedTimestamp.new(mtime, atime, nil).dump_central end # Add fields for user and group ownerships if available. unless uid.nil? || gid.nil? || mtime.nil? || atime.nil? then @central_extra_field_data += ExtraField::Unix.new( mtime, atime, uid, gid ).dump_central end end def local_extra_field_data @local_extra_field_data = @extra_fields.collect do |extra_field| extra_field.dump_local end.join end def internal_file_attributes 0x0000 end def external_file_attributes # Put Unix attributes into the high word and DOS attributes into the low # word. (mode << 16) + (directory? ? 0x10 : 0) end end end; end module Archive; class Zip; module Entry # Archive::Zip::Entry::Directory represents a directory entry within a Zip # archive. class Directory include Archive::Zip::Entry # Inherits the behavior of Archive::Zip::Entry#zip_path= but ensures that # there is a trailing slash (/) on the end of the path. def zip_path=(zip_path) super(zip_path) @zip_path += '/' end # Returns the file type of this entry as the symbol :directory. def ftype :directory end # Returns +true+. def directory? true end # Overridden in order to ensure that the proper mode bits are set for a # directory. def mode=(mode) super(040000 | (mode & 07777)) end # Extracts this entry. # # _options_ is a Hash optionally containing the following: # :file_path:: # Specifies the path to which this entry will be extracted. Defaults to # the zip path of this entry. # :permissions:: # When set to +false+ (the default), POSIX mode/permission bits will be # ignored. Otherwise, they will be restored if possible. # :ownerships:: # When set to +false+ (the default), user and group ownerships will be # ignored. On most systems, only a superuser is able to change # ownerships, so setting this option to +true+ as a regular user may have # no effect. # :times:: # When set to +false+ (the default), last accessed and last modified times # will be ignored. Otherwise, they will be restored if possible. def extract(options = {}) # Ensure that unspecified options have default values. file_path = options.has_key?(:file_path) ? options[:file_path].to_s : @zip_path restore_permissions = options.has_key?(:permissions) ? options[:permissions] : false restore_ownerships = options.has_key?(:ownerships) ? options[:ownerships] : false restore_times = options.has_key?(:times) ? options[:times] : false # Make the directory. FileUtils.mkdir_p(file_path) # Restore the metadata. ::File.chmod(mode, file_path) if restore_permissions ::File.chown(uid, gid, file_path) if restore_ownerships ::File.utime(atime, mtime, file_path) if restore_times nil end private # Directory entries do not have file data to write, so do nothing. def dump_file_data(io) end end end; end; end module Archive; class Zip; module Entry # Archive::Zip::Entry::Symlink represents a symlink entry withing a Zip # archive. class Symlink include Archive::Zip::Entry # Returns the file type of this entry as the symbol :symlink. def ftype :symlink end # Returns +true+. def symlink? true end # Overridden in order to ensure that the proper mode bits are set for a # symlink. def mode=(mode) super(0120000 | (mode & 07777)) end # Returns the link target for this entry. # # Raises Archive::Zip::EntryError if decoding the link target from an # archive is required but fails. def link_target return @link_target unless @link_target.nil? raw_data.rewind encryption_codec.decryptor(raw_data, password) do |decryptor| compression_codec.decompressor(decryptor) do |decompressor| @link_target = decompressor.read # Verify that the extracted data is good. begin unless expected_data_descriptor.nil? then expected_data_descriptor.verify(decompressor.data_descriptor) end rescue => e raise Zip::EntryError, "`#{zip_path}': #{e.message}" end end end @link_target end # Sets the link target for this entry. As a side effect, the raw_data # attribute is set to +nil+. def link_target=(link_target) self.raw_data = nil @link_target = link_target end # Extracts this entry. # # _options_ is a Hash optionally containing the following: # :file_path:: # Specifies the path to which this entry will be extracted. Defaults to # the zip path of this entry. # :permissions:: # When set to +false+ (the default), POSIX mode/permission bits will be # ignored. Otherwise, they will be restored if possible. Not supported # on all platforms. # :ownerships:: # When set to +false+ (the default), user and group ownerships will be # ignored. On most systems, only a superuser is able to change # ownerships, so setting this option to +true+ as a regular user may have # no effect. Not supported on all platforms. # # Raises Archive::Zip::EntryError if the link_target attribute is not # specified. def extract(options = {}) raise Zip::EntryError, 'link_target is nil' if link_target.nil? # Ensure that unspecified options have default values. file_path = options.has_key?(:file_path) ? options[:file_path].to_s : @zip_path restore_permissions = options.has_key?(:permissions) ? options[:permissions] : false restore_ownerships = options.has_key?(:ownerships) ? options[:ownerships] : false # Create the containing directory tree if necessary. parent_dir = ::File.dirname(file_path) FileUtils.mkdir_p(parent_dir) unless ::File.exist?(parent_dir) # Create the symlink. ::File.symlink(link_target, file_path) # Restore the metadata. # NOTE: Ruby does not have the ability to restore atime and mtime on # symlinks at this time (version 1.8.6). begin ::File.lchmod(mode, file_path) if restore_permissions rescue NotImplementedError # Ignore on platforms that do not support lchmod. end begin ::File.lchown(uid, gid, file_path) if restore_ownerships rescue NotImplementedError # Ignore on platforms that do not support lchown. end nil end private # Write the link target to _io_ as the file data for the entry. def dump_file_data(io) io.write(@link_target) end end end; end; end module Archive; class Zip; module Entry # Archive::Zip::Entry::File represents a file entry within a Zip archive. class File include Archive::Zip::Entry # Creates a new file entry where _zip_path_ is the path to the entry in the # ZIP archive. The Archive::Zip::Codec::Deflate codec with the default # compression level set (NORMAL) is used by default for compression. # _raw_data_, if specified, must be a readable, IO-like object containing # possibly compressed/encrypted file data for the entry. It is intended to # be used primarily by the Archive::Zip::Entry.parse class method. def initialize(zip_path, raw_data = nil) super(zip_path, raw_data) @file_path = nil @file_data = nil @compression_codec = Zip::Codec::Deflate.new end # Returns the file type of this entry as the symbol :file. def ftype :file end # Returns +true+. def file? true end # Overridden in order to ensure that the proper mode bits are set for a # file. def mode=(mode) super(0100000 | (mode & 07777)) end # Sets the decryption password. def password=(password) unless @raw_data.nil? then @file_data = nil end @password = password end # The path to a file whose contents are to be used for uncompressed file # data. This will be +nil+ if the +file_data+ attribute is set directly. attr_reader :file_path # Sets the +file_path+ attribute to _file_path_ which should be a String # usable with File#new to open a file for reading which will provide the # IO-like object for the +file_data+ attribute. def file_path=(file_path) @file_data = nil @raw_data = nil @file_path = file_path end # Returns a readable, IO-like object containing uncompressed file data. # # NOTE: It is the responsibility of the user of this attribute to # ensure that the #close method of the returned IO-like object is called # when the object is no longer needed. def file_data return @file_data unless @file_data.nil? || @file_data.closed? unless raw_data.nil? then raw_data.rewind @file_data = compression_codec.decompressor( encryption_codec.decryptor(raw_data, password) ) else if @file_path.nil? then simulated_raw_data = BinaryStringIO.new else simulated_raw_data = ::File.new(@file_path, 'rb') end # Ensure that the IO-like object can return a data descriptor so that # it's possible to verify extraction later if desired. @file_data = Zip::Codec::Store.new.decompressor(simulated_raw_data) end @file_data end # Sets the +file_data+ attribute of this object to _file_data_. _file_data_ # must be a readable, IO-like object. # # NOTE: As a side effect, the +file_path+ and +raw_data+ attributes # for this object will be set to +nil+. def file_data=(file_data) @file_path = nil self.raw_data = nil @file_data = file_data # Ensure that the IO-like object can return CRC32 and data size # information so that it's possible to verify extraction later if desired. unless @file_data.respond_to?(:data_descriptor) then @file_data = Zip::Codec::Store.new.decompressor(@file_data) end @file_data end # Extracts this entry. # # _options_ is a Hash optionally containing the following: # :file_path:: # Specifies the path to which this entry will be extracted. Defaults to # the zip path of this entry. # :permissions:: # When set to +false+ (the default), POSIX mode/permission bits will be # ignored. Otherwise, they will be restored if possible. # :ownerships:: # When set to +false+ (the default), user and group ownerships will be # ignored. On most systems, only a superuser is able to change # ownerships, so setting this option to +true+ as a regular user may have # no effect. # :times:: # When set to +false+ (the default), last accessed and last modified times # will be ignored. Otherwise, they will be restored if possible. # # Raises Archive::Zip::EntryError if the extracted file data appears # corrupt. def extract(options = {}) # Ensure that unspecified options have default values. file_path = options.has_key?(:file_path) ? options[:file_path].to_s : @zip_path restore_permissions = options.has_key?(:permissions) ? options[:permissions] : false restore_ownerships = options.has_key?(:ownerships) ? options[:ownerships] : false restore_times = options.has_key?(:times) ? options[:times] : false # Create the containing directory tree if necessary. parent_dir = ::File.dirname(file_path) FileUtils.mkdir_p(parent_dir) unless ::File.exist?(parent_dir) # Dump the file contents. ::File.open(file_path, 'wb') do |f| while buffer = file_data.read(4096) do f.write(buffer) end end # Verify that the extracted data is good. begin unless expected_data_descriptor.nil? then expected_data_descriptor.verify(file_data.data_descriptor) end rescue => e raise Zip::EntryError, "`#{zip_path}': #{e.message}" end # Restore the metadata. ::File.chmod(mode, file_path) if restore_permissions ::File.chown(uid, gid, file_path) if restore_ownerships ::File.utime(atime, mtime, file_path) if restore_times # Attempt to rewind the file data back to the beginning, but ignore # errors. begin file_data.rewind rescue # Ignore. end nil end private # Write the file data to _io_. def dump_file_data(io) while buffer = file_data.read(4096) do io.write(buffer) end # Attempt to ensure that the file data will still be in a readable state # at the beginning of the data for the next user, but close it if possible # in order to conserve resources. if file_path.nil? then # When the file_path attribute is not set, the file_data method cannot # reinitialize the IO object it returns, so attempt to rewind the file # data back to the beginning, but ignore errors. begin file_data.rewind rescue # Ignore. end else # Since the file_path attribute is set, the file_data method will # reinitialize the IO object it returns if we close the object here. file_data.close end end end end; end; end archive-zip-0.12.0/lib/archive/zip/error.rb0000644000004100000410000000161013513326431020511 0ustar www-datawww-data# encoding: UTF-8 module Archive; class Zip; # Archive::Zip::Error is the base class of all archive-related errors raised # by the Archive::Zip library. class Error < StandardError; end # Archive::Zip::EntryError is raised when there is an error while processing # ZIP archive entries. class EntryError < Error; end # Archive::Zip::ExtraFieldError is raised when there is an error while # processing an extra field in a ZIP archive entry. class ExtraFieldError < Error; end # Archive::Zip::IOError is raised in various places where either an IOError is # raised or an operation on an Archive::Zip object is disallowed because of # the state of the object. class IOError < Error; end # Archive::Zip::UnzipError is raised when attempting to extract an archive # fails but no more exact error class exists for reporting the error. class UnzipError < Error; end end; end archive-zip-0.12.0/lib/archive/zip/extra_field.rb0000644000004100000410000000257613513326431021662 0ustar www-datawww-data# encoding: UTF-8 module Archive; class Zip module ExtraField # A Hash used to map extra field header identifiers to extra field classes. EXTRA_FIELDS = {} # Returns an instance of an extra field class by selecting the class using # _header_id_ and passing _data_ to the class' _parse_central_ method. If # there is no mapping from a given value of _header_id_ to an extra field # class, an instance of Archive::Zip::Entry::ExtraField::Raw is returned. def self.parse_central(header_id, data) if EXTRA_FIELDS.has_key?(header_id) then EXTRA_FIELDS[header_id].parse_central(data) else Raw.parse_central(header_id, data) end end # Returns an instance of an extra field class by selecting the class using # _header_id_ and passing _data_ to the class' _parse_local_ method. If # there is no mapping from a given value of _header_id_ to an extra field # class, an instance of Archive::Zip::Entry::ExtraField::Raw is returned. def self.parse_local(header_id, data) if EXTRA_FIELDS.has_key?(header_id) then EXTRA_FIELDS[header_id].parse_local(data) else Raw.parse_local(header_id, data) end end end end; end # Load the standard extra field classes. require 'archive/zip/extra_field/extended_timestamp' require 'archive/zip/extra_field/raw' require 'archive/zip/extra_field/unix' archive-zip-0.12.0/lib/archive/zip.rb0000644000004100000410000006712413513326431017374 0ustar www-datawww-data# encoding: UTF-8 require 'fileutils' require 'set' require 'tempfile' require 'archive/support/ioextensions' require 'archive/support/iowindow' require 'archive/support/time' require 'archive/support/zlib' require 'archive/zip/codec' require 'archive/zip/entry' require 'archive/zip/error' module Archive # :nodoc: # Archive::Zip represents a ZIP archive compatible with InfoZip tools and the # archives they generate. It currently supports both stored and deflated ZIP # entries, directory entries, file entries, and symlink entries. File and # directory accessed and modified times, POSIX permissions, and ownerships can # be archived and restored as well depending on platform support for such # metadata. Traditional (weak) encryption is also supported. # # Zip64, digital signatures, and strong encryption are not supported. ZIP # archives can only be read from seekable kinds of IO, such as files; reading # archives from pipes or any other non-seekable kind of IO is not supported. # However, writing to such IO objects IS supported. class Zip include Enumerable # The lead-in marker for the end of central directory record. EOCD_SIGNATURE = "PK\x5\x6" # 0x06054b50 # The lead-in marker for the digital signature record. DS_SIGNATURE = "PK\x5\x5" # 0x05054b50 # The lead-in marker for the ZIP64 end of central directory record. Z64EOCD_SIGNATURE = "PK\x6\x6" # 0x06064b50 # The lead-in marker for the ZIP64 end of central directory locator record. Z64EOCDL_SIGNATURE = "PK\x6\x7" # 0x07064b50 # The lead-in marker for a central file record. CFH_SIGNATURE = "PK\x1\x2" # 0x02014b50 # The lead-in marker for a local file record. LFH_SIGNATURE = "PK\x3\x4" # 0x04034b50 # The lead-in marker for data descriptor record. DD_SIGNATURE = "PK\x7\x8" # 0x08074b50 # Creates or possibly updates an archive using _paths_ for new contents. # # If _archive_ is a String, it is treated as a file path which will receive # the archive contents. If the file already exists, it is assumed to be an # archive and will be updated "in place". Otherwise, a new archive # is created. The archive will be closed once written. # # If _archive_ has any other kind of value, it is treated as a writable # IO-like object which will be left open after the completion of this # method. # # NOTE: No attempt is made to prevent adding multiple entries with # the same archive path. # # See the instance method #archive for more information about _paths_ and # _options_. def self.archive(archive, paths, options = {}) if archive.kind_of?(String) && File.exist?(archive) then # Update the archive "in place". tmp_archive_path = nil File.open(archive) do |archive_in| Tempfile.open(*File.split(archive_in.path).reverse) do |archive_out| # Save off the path so that the temporary file can be renamed to the # archive file later. tmp_archive_path = archive_out.path # Ensure the file is in binary mode for Windows. archive_out.binmode # Update the archive. open(archive_in, :r) do |z_in| open(archive_out, :w) do |z_out| z_in.each { |entry| z_out << entry } z_out.archive(paths, options) end end end end # Set more reasonable permissions than those set by Tempfile. File.chmod(0666 & ~File.umask, tmp_archive_path) # Replace the input archive with the output archive. File.rename(tmp_archive_path, archive) else open(archive, :w) { |z| z.archive(paths, options) } end end # Extracts the entries from an archive to _destination_. # # If _archive_ is a String, it is treated as a file path pointing to an # existing archive file. Otherwise, it is treated as a seekable and # readable IO-like object. # # See the instance method #extract for more information about _destination_ # and _options_. def self.extract(archive, destination, options = {}) open(archive, :r) { |z| z.extract(destination, options) } end # Calls #new with the given arguments and yields the resulting Zip instance # to the given block. Returns the result of the block and ensures that the # Zip instance is closed. # # This is a synonym for #new if no block is given. def self.open(archive, mode = :r) zf = new(archive, mode) return zf unless block_given? begin yield(zf) ensure zf.close unless zf.closed? end end # Opens an existing archive and/or creates a new archive. # # If _archive_ is a String, it will be treated as a file path; otherwise, it # is assumed to be an IO-like object with the necessary read or write # support depending on the setting of _mode_. IO-like objects are not # closed when the archive is closed, but files opened from file paths are. # Set _mode_ to :r or "r" to read the archive, and set it # to :w or "w" to write the archive. # # NOTE: The #close method must be called in order to save any # modifications to the archive. Due to limitations in the Ruby finalization # capabilities, the #close method is _not_ automatically called when this # object is garbage collected. Make sure to call #close when finished with # this object. def initialize(archive, mode = :r) @archive = archive mode = mode.to_sym if mode == :r || mode == :w then @mode = mode else raise ArgumentError, "illegal access mode #{mode}" end @close_delegate = false if @archive.kind_of?(String) then @close_delegate = true if mode == :r then @archive = File.open(@archive, 'rb') else @archive = File.open(@archive, 'wb') end end @entries = [] @comment = '' @closed = false @parse_complete = false end # A comment string for the ZIP archive. attr_accessor :comment # Closes the archive. # # Failure to close the archive by calling this method may result in a loss # of data for writable archives. # # NOTE: The underlying stream is only closed if the archive was # opened with a String for the _archive_ parameter. # # Raises Archive::Zip::IOError if called more than once. def close raise IOError, 'closed archive' if closed? if writable? then # Write the new archive contents. dump(@archive) end # Note that we only close delegate streams which are opened by us so that # the user may do so for other delegate streams at his/her discretion. @archive.close if @close_delegate @closed = true nil end # Returns +true+ if the ZIP archive is closed, +false+ otherwise. def closed? @closed end # Returns +true+ if the ZIP archive is readable, +false+ otherwise. def readable? @mode == :r end # Returns +true+ if the ZIP archive is writable, +false+ otherwise. def writable? @mode == :w end # Iterates through each entry of a readable ZIP archive in turn yielding # each one to the given block. # # Raises Archive::Zip::IOError if called on a non-readable archive or after # the archive is closed. def each(&b) raise IOError, 'non-readable archive' unless readable? raise IOError, 'closed archive' if closed? unless @parse_complete then parse(@archive) @parse_complete = true end @entries.each(&b) end # Adds _entry_ into a writable ZIP archive. # # NOTE: No attempt is made to prevent adding multiple entries with # the same archive path. # # Raises Archive::Zip::IOError if called on a non-writable archive or after # the archive is closed. def add_entry(entry) raise IOError, 'non-writable archive' unless writable? raise IOError, 'closed archive' if closed? unless entry.kind_of?(Entry) then raise ArgumentError, 'Archive::Zip::Entry instance required' end @entries << entry self end alias :<< :add_entry # Adds _paths_ to the archive. _paths_ may be either a single path or an # Array of paths. The files and directories referenced by _paths_ are added # using their respective basenames as their zip paths. The exception to # this is when the basename for a path is either "." or # "..". In this case, the path is replaced with the paths to the # contents of the directory it references. # # _options_ is a Hash optionally containing the following: # :path_prefix:: # Specifies a prefix to be added to the zip_path attribute of each entry # where `/' is the file separator character. This defaults to the empty # string. All values are passed through Archive::Zip::Entry.expand_path # before use. # :recursion:: # When set to +true+ (the default), the contents of directories are # recursively added to the archive. # :directories:: # When set to +true+ (the default), entries are added to the archive for # directories. Otherwise, the entries for directories will not be added; # however, the contents of the directories will still be considered if the # :recursion option is +true+. # :symlinks:: # When set to +false+ (the default), entries for symlinks are excluded # from the archive. Otherwise, they are included. NOTE: Unless # :follow_symlinks is explicitly set, it will be set to the logical # NOT of this option in calls to Archive::Zip::Entry.from_file. If # symlinks should be completely ignored, set both this option and # :follow_symlinks to +false+. See Archive::Zip::Entry.from_file # for details regarding :follow_symlinks. # :flatten:: # When set to +false+ (the default), the directory paths containing # archived files will be included in the zip paths of entries representing # the files. When set to +true+, files are archived without any # containing directory structure in the zip paths. Setting to +true+ # implies that :directories is +false+ and :path_prefix is # empty. # :exclude:: # Specifies a proc or lambda which takes a single argument containing a # prospective zip entry and returns +true+ if the entry should be excluded # from the archive and +false+ if it should be included. NOTE: If # a directory is excluded in this way, the :recursion option has no # effect for it. # :password:: # Specifies a proc, lambda, or a String. If a proc or lambda is used, it # must take a single argument containing a zip entry and return a String # to be used as an encryption key for the entry. If a String is used, it # will be used as an encryption key for all encrypted entries. # :on_error:: # Specifies a proc or lambda which is called when an exception is raised # during the archival of an entry. It takes two arguments, a file path # and an exception object generated while attempting to archive the entry. # If :retry is returned, archival of the entry is attempted # again. If :skip is returned, the entry is skipped. Otherwise, # the exception is raised. # Any other options which are supported by Archive::Zip::Entry.from_file are # also supported. # # NOTE: No attempt is made to prevent adding multiple entries with # the same archive path. # # Raises Archive::Zip::IOError if called on a non-writable archive or after # the archive is closed. Raises Archive::Zip::EntryError if the # :on_error option is either unset or indicates that the error should # be raised and Archive::Zip::Entry.from_file raises an error. # # == Example # # A directory contains: # zip-test # +- dir1 # | +- file2.txt # +- dir2 # +- file1.txt # # Create some archives: # Archive::Zip.open('zip-test1.zip') do |z| # z.archive('zip-test') # end # # Archive::Zip.open('zip-test2.zip') do |z| # z.archive('zip-test/.', :path_prefix => 'a/b/c/d') # end # # Archive::Zip.open('zip-test3.zip') do |z| # z.archive('zip-test', :directories => false) # end # # Archive::Zip.open('zip-test4.zip') do |z| # z.archive('zip-test', :exclude => lambda { |e| e.file? }) # end # # The archives contain: # zip-test1.zip -> zip-test/ # zip-test/dir1/ # zip-test/dir1/file2.txt # zip-test/dir2/ # zip-test/file1.txt # # zip-test2.zip -> a/b/c/d/dir1/ # a/b/c/d/dir1/file2.txt # a/b/c/d/dir2/ # a/b/c/d/file1.txt # # zip-test3.zip -> zip-test/dir1/file2.txt # zip-test/file1.txt # # zip-test4.zip -> zip-test/ # zip-test/dir1/ # zip-test/dir2/ def archive(paths, options = {}) raise IOError, 'non-writable archive' unless writable? raise IOError, 'closed archive' if closed? # Ensure that paths is an enumerable. paths = [paths] unless paths.kind_of?(Enumerable) # If the basename of a path is '.' or '..', replace the path with the # paths of all the entries contained within the directory referenced by # the original path. paths = paths.collect do |path| basename = File.basename(path) if basename == '.' || basename == '..' then Dir.entries(path).reject do |e| e == '.' || e == '..' end.collect do |e| File.join(path, e) end else path end end.flatten.uniq # Ensure that unspecified options have default values. options[:path_prefix] = '' unless options.has_key?(:path_prefix) options[:recursion] = true unless options.has_key?(:recursion) options[:directories] = true unless options.has_key?(:directories) options[:symlinks] = false unless options.has_key?(:symlinks) options[:flatten] = false unless options.has_key?(:flatten) # Flattening the directory structure implies that directories are skipped # and that the path prefix should be ignored. if options[:flatten] then options[:path_prefix] = '' options[:directories] = false end # Clean up the path prefix. options[:path_prefix] = Entry.expand_path(options[:path_prefix].to_s) paths.each do |path| # Generate the zip path. zip_entry_path = File.basename(path) zip_entry_path += '/' if File.directory?(path) unless options[:path_prefix].empty? then zip_entry_path = "#{options[:path_prefix]}/#{zip_entry_path}" end begin # Create the entry, but do not add it to the archive yet. zip_entry = Zip::Entry.from_file( path, options.merge( :zip_path => zip_entry_path, :follow_symlinks => options.has_key?(:follow_symlinks) ? options[:follow_symlinks] : ! options[:symlinks] ) ) rescue StandardError => error unless options[:on_error].nil? then case options[:on_error][path, error] when :retry retry when :skip next else raise end else raise end end # Skip this entry if so directed. if (zip_entry.symlink? && ! options[:symlinks]) || (! options[:exclude].nil? && options[:exclude][zip_entry]) then next end # Set the encryption key for the entry. if options[:password].kind_of?(String) then zip_entry.password = options[:password] elsif ! options[:password].nil? then zip_entry.password = options[:password][zip_entry] end # Add entries for directories (if requested) and files/symlinks. if (! zip_entry.directory? || options[:directories]) then add_entry(zip_entry) end # Recurse into subdirectories (if requested). if zip_entry.directory? && options[:recursion] then archive( Dir.entries(path).reject do |e| e == '.' || e == '..' end.collect do |e| File.join(path, e) end, options.merge(:path_prefix => zip_entry_path) ) end end nil end # Extracts the contents of the archive to _destination_, where _destination_ # is a path to a directory which will contain the contents of the archive. # The destination path will be created if it does not already exist. # # _options_ is a Hash optionally containing the following: # :directories:: # When set to +true+ (the default), entries representing directories in # the archive are extracted. This happens after all non-directory entries # are extracted so that directory metadata can be properly updated. # :symlinks:: # When set to +false+ (the default), entries representing symlinks in the # archive are skipped. When set to +true+, such entries are extracted. # Exceptions may be raised on plaforms/file systems which do not support # symlinks. # :overwrite:: # When set to :all (the default), files which already exist will # be replaced. When set to :older, such files will only be # replaced if they are older according to their last modified times than # the zip entry which would replace them. When set to :none, # such files will never be replaced. Any other value is the same as # :all. # :create:: # When set to +true+ (the default), files and directories which do not # already exist will be extracted. When set to +false+, only files and # directories which already exist will be extracted (depending on the # setting of :overwrite). # :flatten:: # When set to +false+ (the default), the directory paths containing # extracted files will be created within +destination+ in order to contain # the files. When set to +true+, files are extracted directly to # +destination+ and directory entries are skipped. # :exclude:: # Specifies a proc or lambda which takes a single argument containing a # zip entry and returns +true+ if the entry should be skipped during # extraction and +false+ if it should be extracted. # :password:: # Specifies a proc, lambda, or a String. If a proc or lambda is used, it # must take a single argument containing a zip entry and return a String # to be used as a decryption key for the entry. If a String is used, it # will be used as a decryption key for all encrypted entries. # :on_error:: # Specifies a proc or lambda which is called when an exception is raised # during the extraction of an entry. It takes two arguments, a zip entry # and an exception object generated while attempting to extract the entry. # If :retry is returned, extraction of the entry is attempted # again. If :skip is returned, the entry is skipped. Otherwise, # the exception is raised. # Any other options which are supported by Archive::Zip::Entry#extract are # also supported. # # Raises Archive::Zip::IOError if called on a non-readable archive or after # the archive is closed. # # == Example # # An archive, archive.zip, contains: # zip-test/ # zip-test/dir1/ # zip-test/dir1/file2.txt # zip-test/dir2/ # zip-test/file1.txt # # A directory, extract4, contains: # zip-test # +- dir1 # +- file1.txt # # Extract the archive: # Archive::Zip.open('archive.zip') do |z| # z.extract('extract1') # end # # Archive::Zip.open('archive.zip') do |z| # z.extract('extract2', :flatten => true) # end # # Archive::Zip.open('archive.zip') do |z| # z.extract('extract3', :create => false) # end # # Archive::Zip.open('archive.zip') do |z| # z.extract('extract3', :create => true) # end # # Archive::Zip.open('archive.zip') do |z| # z.extract( 'extract5', :exclude => lambda { |e| e.file? }) # end # # The directories contain: # extract1 -> zip-test # +- dir1 # | +- file2.txt # +- dir2 # +- file1.txt # # extract2 -> file2.txt # file1.txt # # extract3 -> # # extract4 -> zip-test # +- dir2 # +- file1.txt <- from archive contents # # extract5 -> zip-test # +- dir1 # +- dir2 def extract(destination, options = {}) raise IOError, 'non-readable archive' unless readable? raise IOError, 'closed archive' if closed? # Ensure that unspecified options have default values. options[:directories] = true unless options.has_key?(:directories) options[:symlinks] = false unless options.has_key?(:symlinks) options[:overwrite] = :all unless options[:overwrite] == :older || options[:overwrite] == :never options[:create] = true unless options.has_key?(:create) options[:flatten] = false unless options.has_key?(:flatten) # Flattening the archive structure implies that directory entries are # skipped. options[:directories] = false if options[:flatten] # First extract all non-directory entries. directories = [] each do |entry| # Compute the target file path. file_path = entry.zip_path file_path = File.basename(file_path) if options[:flatten] file_path = File.join(destination, file_path) # Cache some information about the file path. file_exists = File.exist?(file_path) file_mtime = File.mtime(file_path) if file_exists begin # Skip this entry if so directed. if (! file_exists && ! options[:create]) || (file_exists && (options[:overwrite] == :never || options[:overwrite] == :older && entry.mtime <= file_mtime)) || (! options[:exclude].nil? && options[:exclude][entry]) then next end # Set the decryption key for the entry. if options[:password].kind_of?(String) then entry.password = options[:password] elsif ! options[:password].nil? then entry.password = options[:password][entry] end if entry.directory? then # Record the directories as they are encountered. directories << entry elsif entry.file? || (entry.symlink? && options[:symlinks]) then # Extract files and symlinks. entry.extract( options.merge(:file_path => file_path) ) end rescue StandardError => error unless options[:on_error].nil? then case options[:on_error][entry, error] when :retry retry when :skip else raise end else raise end end end if options[:directories] then # Then extract the directory entries in depth first order so that time # stamps, ownerships, and permissions can be properly restored. directories.sort { |a, b| b.zip_path <=> a.zip_path }.each do |entry| begin entry.extract( options.merge( :file_path => File.join(destination, entry.zip_path) ) ) rescue StandardError => error unless options[:on_error].nil? then case options[:on_error][entry, error] when :retry retry when :skip else raise end else raise end end end end nil end private # NOTE: For now _io_ MUST be seekable. def parse(io) socd_pos = find_central_directory(io) io.seek(socd_pos) # Parse each entry in the central directory. loop do signature = IOExtensions.read_exactly(io, 4) break unless signature == CFH_SIGNATURE @entries << Zip::Entry.parse(io) end # Maybe add support for digital signatures and ZIP64 records... Later nil end # Returns the file offset of the first record in the central directory. # _io_ must be a seekable, readable, IO-like object. # # Raises Archive::Zip::UnzipError if the end of central directory signature # is not found where expected or at all. def find_central_directory(io) # First find the offset to the end of central directory record. # It is expected that the variable length comment field will usually be # empty and as a result the initial value of eocd_offset is all that is # necessary. # # NOTE: A cleverly crafted comment could throw this thing off if the # comment itself looks like a valid end of central directory record. eocd_offset = -22 loop do io.seek(eocd_offset, IO::SEEK_END) if IOExtensions.read_exactly(io, 4) == EOCD_SIGNATURE then io.seek(16, IO::SEEK_CUR) if IOExtensions.read_exactly(io, 2).unpack('v')[0] == (eocd_offset + 22).abs then break end end eocd_offset -= 1 end # At this point, eocd_offset should point to the location of the end of # central directory record relative to the end of the archive. # Now, jump into the location in the record which contains a pointer to # the start of the central directory record and return the value. io.seek(eocd_offset + 16, IO::SEEK_END) return IOExtensions.read_exactly(io, 4).unpack('V')[0] rescue Errno::EINVAL raise Zip::UnzipError, 'unable to locate end-of-central-directory record' end # Writes all the entries of this archive to _io_. _io_ must be a writable, # IO-like object providing a _write_ method. Returns the total number of # bytes written. def dump(io) bytes_written = 0 @entries.each do |entry| bytes_written += entry.dump_local_file_record(io, bytes_written) end central_directory_offset = bytes_written @entries.each do |entry| bytes_written += entry.dump_central_file_record(io) end central_directory_length = bytes_written - central_directory_offset bytes_written += io.write(EOCD_SIGNATURE) bytes_written += io.write( [ 0, 0, @entries.length, @entries.length, central_directory_length, central_directory_offset, comment.bytesize ].pack('vvvvVVv') ) bytes_written += io.write(comment) bytes_written end end end archive-zip-0.12.0/lib/archive/support/0000755000004100000410000000000013513326431017747 5ustar www-datawww-dataarchive-zip-0.12.0/lib/archive/support/ioextensions.rb0000644000004100000410000000106313513326431023023 0ustar www-datawww-data# encoding: UTF-8 # IOExtensions provides convenience wrappers for certain IO functionality. module IOExtensions # Reads and returns exactly _length_ bytes from _io_ using the read method on # _io_. If there is insufficient data available, an EOFError is raised. def self.read_exactly(io, length, buffer = '') buffer.slice!(0..-1) unless buffer.empty? while buffer.size < length do internal = io.read(length - buffer.size) raise EOFError, 'unexpected end of file' if internal.nil? buffer << internal end buffer end end archive-zip-0.12.0/lib/archive/support/zlib.rb0000644000004100000410000004556313513326431021251 0ustar www-datawww-data# encoding: UTF-8 require 'zlib' require 'archive/support/io-like' module Zlib # :nodoc: # The maximum size of the zlib history buffer. Note that zlib allows larger # values to enable different inflate modes. See Zlib::Inflate.new for details. # Provided here only for Ruby versions that do not provide it. MAX_WBITS = Deflate::MAX_WBITS unless const_defined?(:MAX_WBITS) # A deflate strategy which limits match distances to 1, also known as # run-length encoding. Provided here only for Ruby versions that do not # provide it. RLE = 3 unless const_defined?(:RLE) # A deflate strategy which does not use dynamic Huffman codes, allowing for a # simpler decoder to be used to inflate. Provided here only for Ruby versions # that do not provide it. FIXED = 4 unless const_defined?(:FIXED) # Zlib::ZWriter is a writable, IO-like object (includes IO::Like) which wraps # other writable, IO-like objects in order to facilitate writing data to those # objects using the deflate method of compression. class ZWriter include IO::Like # Creates a new instance of this class with the given arguments using #new # and then passes the instance to the given block. The #close method is # guaranteed to be called after the block completes. # # Equivalent to #new if no block is given. def self.open(delegate, level = nil, window_bits = nil, mem_level = nil, strategy = nil) zw = new(delegate, level, window_bits, mem_level, strategy) return zw unless block_given? begin yield(zw) ensure zw.close unless zw.closed? end end # Creates a new instance of this class. _delegate_ must respond to the # _write_ method as an instance of IO would. _level_, _window_bits_, # _mem_level_, and _strategy_ are all passed directly to # Zlib::Deflate.new(). # # # The following descriptions of _level_, _window_bits_, _mem_level_, and # _strategy_ are based upon or pulled largely verbatim from descriptions # found in zlib.h version 1.2.3 with changes made to account for different # parameter names and to improve readability. Some of the statements # concerning default settings or value ranges may not be accurate depending # on the version of the zlib library used by a given Ruby interpreter. # # # The _level_ parameter must be +nil+, Zlib::DEFAULT_COMPRESSION, or between # 0 and 9: 1 gives best speed, 9 gives # best compression, 0 gives no compression at all (the input data # is simply copied a block at a time). Zlib::DEFAULT_COMPRESSION requests a # default compromise between speed and compression (currently equivalent to # level 6). If unspecified or +nil+, _level_ defaults to # Zlib::DEFAULT_COMPRESSION. # # The _window_bits_ parameter specifies the size of the history buffer, the # format of the compressed stream, and the kind of checksum returned by the # checksum method. The size of the history buffer is specified by setting # the value of _window_bits_ in the range of 8..15, # inclusive. A value of 8 indicates a small window which reduces # memory usage but lowers the compression ratio while a value of 15 # indicates a larger window which increases memory usage but raises the # compression ratio. Modification of this base value for _window_bits_ as # noted below dictates what kind of compressed stream and checksum will be # produced while preserving the setting for the history buffer. # # If nothing else is done to the base value of _window_bits_, a zlib stream # is to be produced with an appropriate header and trailer. In this case # the checksum method of this object will be an adler32. # # Adding 16 to the base value of _window_bits_ indicates that a # gzip stream is to be produced with an appropriate header and trailer. The # gzip header will have no file name, no extra data, no comment, no # modification time (set to zero), no header crc, and the operating system # will be set to 255 (unknown). In this case the checksum # attribute of this object will be a crc32. # # Finally, negating the base value of _window_bits_ indicates that a raw # zlib stream is to be produced without any header or trailer. In this case # the checksum method of this object will always return nil. This is # for use with other formats that use the deflate compressed data format # such as zip. Such formats should provide their own check values. # # If unspecified or +nil+, _window_bits_ defaults to 15. # # The _mem_level_ parameter specifies how much memory should be allocated # for the internal compression state. A value of 1 uses minimum # memory but is slow and reduces compression ratio; a value of 9 # uses maximum memory for optimal speed. The default value is 8 if # unspecified or +nil+. # # The _strategy_ parameter is used to tune the compression algorithm. It # only affects the compression ratio but not the correctness of the # compressed output even if it is not set appropriately. The default value # is Zlib::DEFAULT_STRATEGY if unspecified or +nil+. # # Use the value Zlib::DEFAULT_STRATEGY for normal data, Zlib::FILTERED for # data produced by a filter (or predictor), Zlib::HUFFMAN_ONLY to force # Huffman encoding only (no string match), Zlib::RLE to limit match # distances to 1 (run-length encoding), or Zlib::FIXED to simplify decoder # requirements. # # The effect of Zlib::FILTERED is to force more Huffman coding and less # string matching; it is somewhat intermediate between # Zlib::DEFAULT_STRATEGY and Zlib::HUFFMAN_ONLY. Filtered data consists # mostly of small values with a somewhat random distribution. In this case, # the compression algorithm is tuned to compress them better. # # Zlib::RLE is designed to be almost as fast as Zlib::HUFFMAN_ONLY, but give # better compression for PNG image data. # # Zlib::FIXED prevents the use of dynamic Huffman codes, allowing for a # simpler decoder for special applications. # # This class has extremely limited seek capabilities. It is possible to # seek with an offset of 0 and a whence of IO::SEEK_CUR. # As a result, the _pos_ and _tell_ methods also work as expected. # # If _delegate_ also responds to _rewind_, then the _rewind_ method of this # class can be used to reset the whole stream back to the beginning. Using # _seek_ of this class to seek directly to offset 0 using # IO::SEEK_SET for whence will also work in this case. # # NOTE: Due to limitations in Ruby's finalization capabilities, the # #close method is _not_ automatically called when this object is garbage # collected. Make sure to call #close when finished with this object. def initialize(delegate, level = nil, window_bits = nil, mem_level = nil, strategy = nil) @delegate = delegate @level = level @window_bits = window_bits @mem_level = mem_level @strategy = strategy @deflater = Zlib::Deflate.new(@level, @window_bits, @mem_level, @strategy) @deflate_buffer = '' @checksum = nil @compressed_size = nil @uncompressed_size = nil end protected # The delegate object to which compressed data is written. attr_reader :delegate public # Returns the checksum computed over the data written to this stream so far. # # NOTE: Refer to the documentation of #new concerning _window_bits_ # to learn what kind of checksum will be returned. # # NOTE: Anything still in the internal write buffer has not been # processed, so calling #flush prior to calling this method may be necessary # for an accurate checksum. def checksum return nil if @window_bits < 0 @deflater.closed? ? @checksum : @deflater.adler end # Closes the writer by finishing the compressed data and flushing it to the # delegate. # # Raises IOError if called more than once. def close flush() @deflate_buffer << @deflater.finish unless @deflater.finished? begin until @deflate_buffer.empty? do @deflate_buffer.slice!(0, delegate.write(@deflate_buffer)) end rescue Errno::EAGAIN, Errno::EINTR retry if write_ready? end @checksum = @deflater.adler @compressed_size = @deflater.total_out @uncompressed_size = @deflater.total_in @deflater.close super() nil end # Returns the number of bytes of compressed data produced so far. # # NOTE: This value is only updated when both the internal write # buffer is flushed and there is enough data to produce a compressed block. # It does not necessarily reflect the amount of data written to the # delegate until this stream is closed however. Until then the only # guarantee is that the value will be greater than or equal to 0. def compressed_size @deflater.closed? ? @compressed_size : @deflater.total_out end # Returns the number of bytes sent to be compressed so far. # # NOTE: This value is only updated when the internal write buffer is # flushed. def uncompressed_size @deflater.closed? ? @uncompressed_size : @deflater.total_in end private # Allows resetting this object and the delegate object back to the beginning # of the stream or reporting the current position in the stream. # # Raises Errno::EINVAL unless _offset_ is 0 and _whence_ is either # IO::SEEK_SET or IO::SEEK_CUR. Raises Errno::EINVAL if _whence_ is # IO::SEEK_SEK and the delegate object does not respond to the _rewind_ # method. def unbuffered_seek(offset, whence = IO::SEEK_SET) unless offset == 0 && ((whence == IO::SEEK_SET && delegate.respond_to?(:rewind)) || whence == IO::SEEK_CUR) then raise Errno::EINVAL end case whence when IO::SEEK_SET delegate.rewind @deflater.finish @deflater.close @deflater = Zlib::Deflate.new( @level, @window_bits, @mem_level, @strategy ) @deflate_buffer = '' 0 when IO::SEEK_CUR @deflater.total_in end end def unbuffered_write(string) # First try to write out the contents of the deflate buffer because if # that raises a failure we can let that pass up the call stack without # having polluted the deflater instance. until @deflate_buffer.empty? do @deflate_buffer.slice!(0, delegate.write(@deflate_buffer)) end # At this point we can deflate the given string into a new buffer and # behave as if it was written. @deflate_buffer = @deflater.deflate(string) string.length end end # Zlib::ZReader is a readable, IO-like object (includes IO::Like) which wraps # other readable, IO-like objects in order to facilitate reading data from # those objects using the inflate method of decompression. class ZReader include IO::Like # The number of bytes to read from the delegate object each time the # internal read buffer is filled. DEFAULT_DELEGATE_READ_SIZE = 4096 # Creates a new instance of this class with the given arguments using #new # and then passes the instance to the given block. The #close method is # guaranteed to be called after the block completes. # # Equivalent to #new if no block is given. def self.open(delegate, window_bits = nil) zr = new(delegate, window_bits) return zr unless block_given? begin yield(zr) ensure zr.close unless zr.closed? end end # Creates a new instance of this class. _delegate_ must respond to the # _read_ method as an IO instance would. _window_bits_ is passed directly # to Zlib::Inflate.new(). # # # The following description of _window_bits_ is based on the description # found in zlib.h version 1.2.3. Some of the statements concerning default # settings or value ranges may not be accurate depending on the version of # the zlib library used by a given Ruby interpreter. # # # The _window_bits_ parameter specifies the size of the history buffer, the # format of the compressed stream, and the kind of checksum returned by the # checksum method. The size of the history buffer is specified by setting # the value of _window_bits_ in the range of 8..15, # inclusive. It must be at least as large as the setting used to create the # stream or a Zlib::DataError will be raised. Modification of this base # value for _window_bits_ as noted below dictates what kind of compressed # stream is expected and what kind of checksum will be produced while # preserving the setting for the history buffer. # # If nothing else is done to the base value of _window_bits_, a zlib stream # is expected with an appropriate header and trailer. In this case the # checksum method of this object will be an adler32. # # Adding 16 to the base value of _window_bits_ indicates that a # gzip stream is expected with an appropriate header and trailer. In this # case the checksum method of this object will be a crc32. # # Adding 32 to the base value of _window_bits_ indicates that an # automatic detection of the stream format should be made based on the # header in the stream. In this case the checksum method of this object # will depend on whether a zlib or a gzip stream is detected. # # Finally, negating the base value of _window_bits_ indicates that a raw # zlib stream is expected without any header or trailer. In this case the # checksum method of this object will always return nil. This is for # use with other formats that use the deflate compressed data format such as # zip. Such formats should provide their own check values. # # If unspecified or +nil+, _window_bits_ defaults to 15. # # In all cases, Zlib::DataError is raised if the wrong stream format is # found when reading. # # This class has extremely limited seek capabilities. It is possible to # seek with an offset of 0 and a whence of IO::SEEK_CUR. # As a result, the _pos_ and _tell_ methods also work as expected. # # Due to certain optimizations within IO::Like#seek and if there is data in # the read buffer, the _seek_ method can be used to seek forward from the # current stream position up to the end of the buffer. Unless it is known # definitively how much data is in the buffer, it is best to avoid relying # on this behavior. # # If _delegate_ also responds to _rewind_, then the _rewind_ method of this # class can be used to reset the whole stream back to the beginning. Using # _seek_ of this class to seek directly to offset 0 using # IO::SEEK_SET for whence will also work in this case. # # Any other seeking attempts, will raise Errno::EINVAL exceptions. # # NOTE: Due to limitations in Ruby's finalization capabilities, the # #close method is _not_ automatically called when this object is garbage # collected. Make sure to call #close when finished with this object. def initialize(delegate, window_bits = nil) @delegate = delegate @delegate_read_size = DEFAULT_DELEGATE_READ_SIZE @window_bits = window_bits @inflater = Zlib::Inflate.new(@window_bits) @inflate_buffer = '' @checksum = nil @compressed_size = nil @uncompressed_size = nil end # The number of bytes to read from the delegate object each time the # internal read buffer is filled. attr_accessor :delegate_read_size protected # The delegate object from which compressed data is read. attr_reader :delegate public # Returns the checksum computed over the data read from this stream. # # NOTE: Refer to the documentation of #new concerning _window_bits_ # to learn what kind of checksum will be returned. # # NOTE: The contents of the internal read buffer are immediately # processed any time the internal buffer is filled, so this checksum is only # accurate if all data has been read out of this object. def checksum return nil if @window_bits < 0 @inflater.closed? ? @checksum : @inflater.adler end # Closes the reader. # # Raises IOError if called more than once. def close super() @checksum = @inflater.adler @compressed_size = @inflater.total_in @uncompressed_size = @inflater.total_out @inflater.close nil end # Returns the number of bytes sent to be compressed so far. # # NOTE: This value is updated whenever the internal read buffer needs # to be filled, not when data is read out of this stream. def compressed_size @inflater.closed? ? @compressed_size : @inflater.total_in end # Returns the number of bytes of decompressed data produced so far. # # NOTE: This value is updated whenever the internal read buffer needs # to be filled, not when data is read out of this stream. def uncompressed_size @inflater.closed? ? @uncompressed_size : @inflater.total_out end private def unbuffered_read(length) if @inflate_buffer.empty? && @inflater.finished? then raise EOFError, 'end of file reached' end begin while @inflate_buffer.length < length && ! @inflater.finished? do @inflate_buffer << @inflater.inflate(delegate.read(@delegate_read_size)) end rescue Errno::EINTR, Errno::EAGAIN raise if @inflate_buffer.empty? end @inflate_buffer.slice!(0, length) end # Allows resetting this object and the delegate object back to the beginning # of the stream or reporting the current position in the stream. # # Raises Errno::EINVAL unless _offset_ is 0 and _whence_ is either # IO::SEEK_SET or IO::SEEK_CUR. Raises Errno::EINVAL if _whence_ is # IO::SEEK_SEK and the delegate object does not respond to the _rewind_ # method. def unbuffered_seek(offset, whence = IO::SEEK_SET) unless offset == 0 && ((whence == IO::SEEK_SET && delegate.respond_to?(:rewind)) || whence == IO::SEEK_CUR) then raise Errno::EINVAL end case whence when IO::SEEK_SET delegate.rewind @inflater.close @inflater = Zlib::Inflate.new(@window_bits) @inflate_buffer = '' 0 when IO::SEEK_CUR @inflater.total_out - @inflate_buffer.length end end end end archive-zip-0.12.0/lib/archive/support/binary_stringio.rb0000644000004100000410000000150713513326431023501 0ustar www-datawww-data# encoding: UTF-8 require 'stringio' # This class is a version of StringIO that always uses the binary encoding on # any Ruby platform that has a notion of encodings. On Ruby platforms without # encoding support, this class is equivalent to StringIO. class BinaryStringIO < StringIO # Creates a new instance of this class. # # This takes all the arguments of StringIO.new. def initialize(*args) super # Force a binary encoding when possible. if respond_to?(:set_encoding, true) @encoding_locked = false set_encoding('binary') end end if instance_methods.include?(:set_encoding) # Raise an exception on attempts to change the encoding. def set_encoding(*args) raise 'Changing encoding is not allowed' if @encoding_locked @encoding_locked = true super end end end archive-zip-0.12.0/lib/archive/support/integer.rb0000644000004100000410000000042213513326431021727 0ustar www-datawww-data# encoding: UTF-8 class Integer unless public_method_defined?('ord') then # Returns the int itself. # # This method is defined here only if not already defined elsewhere, such as # versions of Ruby prior to 1.8.7. def ord self end end end archive-zip-0.12.0/lib/archive/support/io-like.rb0000644000004100000410000000052113513326431021623 0ustar www-datawww-data# encoding: UTF-8 # Try to first load io-like from rubygems and then fall back to assuming a # standard installation. begin require 'rubygems' gem 'io-like', '>= 0.3.0' rescue LoadError # Failed to load via rubygems. end # This will work for the gem and standard install assuming io-like is available # at all. require 'io/like' archive-zip-0.12.0/lib/archive/support/time.rb0000644000004100000410000000731313513326431021236 0ustar www-datawww-data# encoding: UTF-8 class Time # Returns a DOSTime representing this time object as a DOS date-time # structure. Times are bracketed by the limits of the ability of the DOS # date-time structure to represent them. Accuracy is 2 seconds and years # range from 1980 to 2099. The returned structure represents as closely as # possible the time of this object. # # See DOSTime#new for a description of this structure. def to_dos_time dos_sec = sec/2 dos_year = year - 1980 dos_year = 0 if dos_year < 0 dos_year = 119 if dos_year > 119 Archive::DOSTime.new( (dos_sec ) | (min << 5) | (hour << 11) | (day << 16) | (month << 21) | (dos_year << 25) ) end end module Archive # A representation of the DOS time structure which can be converted into # instances of Time. class DOSTime include Comparable # Creates a new instance of DOSTime. _dos_time_ is a 4 byte String or # unsigned number (Integer) representing an MS-DOS time structure where: # Bits 0-4:: 2 second increments (0-29) # Bits 5-10:: minutes (0-59) # Bits 11-15:: hours (0-24) # Bits 16-20:: day (1-31) # Bits 21-24:: month (1-12) # Bits 25-31:: four digit year minus 1980 (0-119) # # If _dos_time_ is ommitted or +nil+, a new instance is created based on the # current time. def initialize(dos_time = nil) case dos_time when nil @dos_time = Time.now.to_dos_time.to_i when Integer @dos_time = dos_time else unless dos_time.length == 4 then raise ArgumentError, 'length of DOS time structure is not 4' end @dos_time = dos_time.unpack('V')[0] end validate end # Returns -1 if _other_ is a time earlier than this one, 0 if _other_ is the # same time, and 1 if _other_ is a later time. def cmp(other) to_i <=> other.to_i end alias :<=> :cmp # Returns the time value of this object as an integer representing the DOS # time structure. def to_i @dos_time end # Returns the 32 bit integer that backs this object packed into a String in # little endian format. This is suitable for use with #new. def pack [to_i].pack('V') end # Returns a Time instance which is equivalent to the time represented by # this object. def to_time second = ((0b11111 & @dos_time) ) * 2 minute = ((0b111111 << 5 & @dos_time) >> 5) hour = ((0b11111 << 11 & @dos_time) >> 11) day = ((0b11111 << 16 & @dos_time) >> 16) month = ((0b1111 << 21 & @dos_time) >> 21) year = ((0b1111111 << 25 & @dos_time) >> 25) + 1980 return Time.local(year, month, day, hour, minute, second) end private def validate second = (0b11111 & @dos_time) minute = (0b111111 << 5 & @dos_time) >> 5 hour = (0b11111 << 11 & @dos_time) >> 11 day = (0b11111 << 16 & @dos_time) >> 16 month = (0b1111 << 21 & @dos_time) >> 21 year = (0b1111111 << 25 & @dos_time) >> 25 if second > 29 raise ArgumentError, 'second must not be greater than 29' elsif minute > 59 raise ArgumentError, 'minute must not be greater than 59' elsif hour > 24 raise ArgumentError, 'hour must not be greater than 24' elsif day < 1 raise ArgumentError, 'day must not be less than 1' elsif month < 1 raise ArgumentError, 'month must not be less than 1' elsif month > 12 raise ArgumentError, 'month must not be greater than 12' elsif year > 119 raise ArgumentError, 'year must not be greater than 119' end end end end archive-zip-0.12.0/lib/archive/support/iowindow.rb0000644000004100000410000000673313513326431022144 0ustar www-datawww-data# encoding: UTF-8 require 'archive/support/io-like' # IOWindow represents an IO object which wraps another one allowing read and/or # write access to a subset of the data within the stream. # # NOTE: This object is NOT thread safe. class IOWindow include IO::Like # Creates a new instance of this class using _io_ as the data source # and where _window_position_ and _window_size_ define the location and size # of data window respectively. # # _io_ must be opened for reading and must be seekable. _window_position_ # must be an integer greater than or equal to 0. _window_size_ must be an # integer greater than or equal to 0. def initialize(io, window_position, window_size) @io = io @unbuffered_pos = 0 self.window_position = window_position self.window_size = window_size end # The file position at which this window begins. attr_reader :window_position # Set the file position at which this window begins. # _window_position_ must be an integer greater than or equal to 0. def window_position=(window_position) unless window_position.respond_to?(:to_int) then raise TypeError, "can't convert #{window_position.class} into Integer" end window_position = window_position.to_int if window_position < 0 then raise ArgumentError, 'non-positive window position given' end @window_position = window_position end # The size of the window. attr_reader :window_size # Set the size of the window. # _window_size_ must be an integer greater than or equal to 0. def window_size=(window_size) unless window_size.respond_to?(:to_int) then raise TypeError, "can't convert #{window_size.class} into Integer" end window_size = window_size.to_int raise ArgumentError, 'non-positive window size given' if window_size < 0 @window_size = window_size end private def unbuffered_read(length) restore_self # Error out if the end of the window is reached. raise EOFError, 'end of file reached' if @unbuffered_pos >= @window_size # Limit the read operation to the window. length = @window_size - @unbuffered_pos if @unbuffered_pos + length > @window_size # Fill a buffer with the data from the delegate. buffer = @io.read(length) # Error out if the end of the delegate is reached. raise EOFError, 'end of file reached' if buffer.nil? # Update the position. @unbuffered_pos += buffer.length buffer ensure restore_delegate end def unbuffered_seek(offset, whence = IO::SEEK_SET) # Convert the offset and whence into an absolute position. case whence when IO::SEEK_SET new_pos = offset when IO::SEEK_CUR new_pos = @unbuffered_pos + offset when IO::SEEK_END new_pos = @window_size + offset end # Error out if the position is outside the window. raise Errno::EINVAL, 'Invalid argument' if new_pos < 0 or new_pos > @window_size # Set the new position. @unbuffered_pos = new_pos end # Restores the state of the delegate IO object to that saved by a prior call # to #restore_self. def restore_delegate @io.pos = @delegate_pos @io.lineno = @delegate_lineno end # Saves the state of the delegate IO object so that it can be restored later # using #restore_delegate and then configures the delegate so as to restore # the state of this object. def restore_self @delegate_pos = @io.pos @delegate_lineno = @io.lineno @io.pos = @window_position + @unbuffered_pos end end archive-zip-0.12.0/.yardopts0000644000004100000410000000010513513326431015706 0ustar www-datawww-data--protected --private --main README.md lib/**/*.rb - NEWS.md LICENSE archive-zip-0.12.0/NEWS.md0000644000004100000410000001041113513326431015137 0ustar www-datawww-data# News and Notifications by Version This file lists noteworthy changes which may affect users of this project. More detailed information is available in the rest of the documentation. **NOTE:** Date stamps in the following entries are in YYYY/MM/DD format. ## v0.12.0 (2019/02/28) ### Fixes * Check for codec availability before attempting to initialize a codec instance during extraction. (Kouhei Sutou) ## v0.11.0 (2018/01/28) ### Fixes * Upgrade gems required for development. ## v0.10.0 (2017/09/04) ### Fixes * Avoid Ruby warnings for uninitialized attributes. (Tatsuya Sato) ## v0.9.0 (2016/12/18) ### Fixes * Initialize DOSTime correctly when not given a struct to parse. ## v0.8.0 (2015/01/05) ### Fixes * Avoid Ruby warnings. (Akira Matsuda) ## v0.7.0 (2014/08/18) ### Fixes * Avoid corrupting the archive when storing entries that have multibyte names. ### Notes * Ruby 1.8.6 support has been dropped. * This may come back if demand warrants it. * Switched to the MIT license. * Now using minitest instead of mspec for tests. ## v0.6.0 (2013/03/24) ### Fixes * Only define Zlib constants when they are not already defined. * Fixes constant redefinition warnings under MRI 2.0.0-p0. * Force Zlib::ZWriter and Zlib::ZReader #checksum methods to return nil for raw streams. * The behavior of the underlying Zlib::Deflate and Zlib::Inflate classes' #adler methods appear inconsistent for raw streams and really aren't necessary in this case anyway. ### Notes * Broke backward compatibility with the behavior of Zlib::ZWriter#checksum and Zlib::ZReader#checksum when working with raw streams. * This should not affect direct users of Archive::Zip because the checksum methods of those classes are never used. ## v0.5.0 (2012/03/01) ### Fixes * Avoid timezone discrepancies in encryption tests. * Moved the DOSTime class to the Archive namespace (Chris Schneider). ### Notes * Broke backward compatibility of the DOSTime class. ## v0.4.0 (2011/08/29) ### Features * Added Ruby 1.9 support. * Simplified arguments for Archive::Zip.new. * Archives cannot be directly opened for modification. * Archive::Zip.archive can still emulate modifying an archive in place. * Added a bunch of tests (many more still needed). * Updated and simplified rake tasks. * Created a standalone gemspec file. ### Fixes * Fixed a potential data loss bug in Zlib::ZReader. * Archives larger than the maximum Fixnum for the platform don't falsely raise a "non-integer windows position given" error. ### Notes * Broke backward compatibility for Archive::Zip.new. * Wrapper class methods continue to work as before. * Broke backward compatibility for Archive::Zip::ExtraField. * Allows separate handling of extra fields in central and local records. ## v0.3.0 (2009/01/23) * Made a significant performance improvement for the extraction of compressed entries for performance on par with InfoZIP's unzip. Parsing archives with many entries is still a bit subpar however. ## v0.2.0 (2008/08/06) * Traditional (weak) encryption is now supported. * Adding new encryption methods should be easier now. * Fixed a bug where the compression codec for an entry loaded from an archive was not being recorded. * The _compression_codec_ attribute for Entry instances is now used instead of the _codec_ attribute to access the compression codec of the entry. ## v0.1.1 (2008/07/11) * Archive files are now closed when the Archive::Zip object is closed even when no changes were made to the archive, a problem on Windows if you want to delete the archive after extracting it within the same script. ## v0.1.0 (2008/07/10) * Initial release. * Archive creation and extraction is supported with only a few lines of code. (See README) * Archives can be updated "in place" or dumped out to other files or pipes. * Files, symlinks, and directories are supported within archives. * Unix permission/mode bits are supported. * Unix user and group ownerships are supported. * Unix last accessed and last modified times are supported. * Entry extension (AKA extra field) implementations can be added on the fly. * Unknown entry extension types are preserved during archive processing. * Deflate and Store compression codecs are supported out of the box. * More compression codecs can be added on the fly. archive-zip-0.12.0/archive-zip.gemspec0000644000004100000410000003740213513326431017640 0ustar www-datawww-data######################################################### # This file has been automatically generated by gem2tgz # ######################################################### # -*- encoding: utf-8 -*- # stub: archive-zip 0.12.0 ruby lib Gem::Specification.new do |s| s.name = "archive-zip".freeze s.version = "0.12.0" s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.require_paths = ["lib".freeze] s.authors = ["Jeremy Bopp".freeze, "Akira Matsuda".freeze, "Tatsuya Sato".freeze, "Kouhei Sutou".freeze] s.date = "2019-03-01" s.description = "Archive::Zip provides a simple Ruby-esque interface to creating, extracting, and\nupdating ZIP archives. This implementation is 100% Ruby and loosely modeled on\nthe archive creation and extraction capabilities of InfoZip's zip and unzip\ntools.\n".freeze s.email = ["jeremy@bopp.net".freeze, "ronnie@dio.jp".freeze, "tatsuya.b.sato@rakuten.com".freeze, "kou@clear-code.com".freeze] s.files = [".yardopts".freeze, "LICENSE".freeze, "NEWS.md".freeze, "README.md".freeze, "Rakefile".freeze, "lib/archive/support/binary_stringio.rb".freeze, "lib/archive/support/integer.rb".freeze, "lib/archive/support/io-like.rb".freeze, "lib/archive/support/ioextensions.rb".freeze, "lib/archive/support/iowindow.rb".freeze, "lib/archive/support/time.rb".freeze, "lib/archive/support/zlib.rb".freeze, "lib/archive/zip.rb".freeze, "lib/archive/zip/codec.rb".freeze, "lib/archive/zip/codec/deflate.rb".freeze, "lib/archive/zip/codec/null_encryption.rb".freeze, "lib/archive/zip/codec/store.rb".freeze, "lib/archive/zip/codec/traditional_encryption.rb".freeze, "lib/archive/zip/data_descriptor.rb".freeze, "lib/archive/zip/entry.rb".freeze, "lib/archive/zip/error.rb".freeze, "lib/archive/zip/extra_field.rb".freeze, "lib/archive/zip/extra_field/extended_timestamp.rb".freeze, "lib/archive/zip/extra_field/raw.rb".freeze, "lib/archive/zip/extra_field/unix.rb".freeze, "lib/archive/zip/version.rb".freeze, "spec/archive/dos_time_spec.rb".freeze, "spec/archive/zip/archive_spec.rb".freeze, "spec/archive/zip/codec/deflate/compress/checksum_spec.rb".freeze, "spec/archive/zip/codec/deflate/compress/close_spec.rb".freeze, "spec/archive/zip/codec/deflate/compress/crc32_spec.rb".freeze, "spec/archive/zip/codec/deflate/compress/data_descriptor_spec.rb".freeze, "spec/archive/zip/codec/deflate/compress/new_spec.rb".freeze, "spec/archive/zip/codec/deflate/compress/open_spec.rb".freeze, "spec/archive/zip/codec/deflate/compress/write_spec.rb".freeze, "spec/archive/zip/codec/deflate/decompress/checksum_spec.rb".freeze, "spec/archive/zip/codec/deflate/decompress/close_spec.rb".freeze, "spec/archive/zip/codec/deflate/decompress/crc32_spec.rb".freeze, "spec/archive/zip/codec/deflate/decompress/data_descriptor_spec.rb".freeze, "spec/archive/zip/codec/deflate/decompress/new_spec.rb".freeze, "spec/archive/zip/codec/deflate/decompress/open_spec.rb".freeze, "spec/archive/zip/codec/deflate/fixtures/classes.rb".freeze, "spec/archive/zip/codec/deflate/fixtures/compressed_file.bin".freeze, "spec/archive/zip/codec/deflate/fixtures/compressed_file_nocomp.bin".freeze, "spec/archive/zip/codec/deflate/fixtures/raw_file.txt".freeze, "spec/archive/zip/codec/null_encryption/decrypt/close_spec.rb".freeze, "spec/archive/zip/codec/null_encryption/decrypt/new_spec.rb".freeze, "spec/archive/zip/codec/null_encryption/decrypt/open_spec.rb".freeze, "spec/archive/zip/codec/null_encryption/decrypt/read_spec.rb".freeze, "spec/archive/zip/codec/null_encryption/decrypt/rewind_spec.rb".freeze, "spec/archive/zip/codec/null_encryption/decrypt/seek_spec.rb".freeze, "spec/archive/zip/codec/null_encryption/decrypt/tell_spec.rb".freeze, "spec/archive/zip/codec/null_encryption/encrypt/close_spec.rb".freeze, "spec/archive/zip/codec/null_encryption/encrypt/new_spec.rb".freeze, "spec/archive/zip/codec/null_encryption/encrypt/open_spec.rb".freeze, "spec/archive/zip/codec/null_encryption/encrypt/rewind_spec.rb".freeze, "spec/archive/zip/codec/null_encryption/encrypt/seek_spec.rb".freeze, "spec/archive/zip/codec/null_encryption/encrypt/tell_spec.rb".freeze, "spec/archive/zip/codec/null_encryption/encrypt/write_spec.rb".freeze, "spec/archive/zip/codec/null_encryption/fixtures/classes.rb".freeze, "spec/archive/zip/codec/null_encryption/fixtures/raw_file.txt".freeze, "spec/archive/zip/codec/store/compress/close_spec.rb".freeze, "spec/archive/zip/codec/store/compress/data_descriptor_spec.rb".freeze, "spec/archive/zip/codec/store/compress/new_spec.rb".freeze, "spec/archive/zip/codec/store/compress/open_spec.rb".freeze, "spec/archive/zip/codec/store/compress/rewind_spec.rb".freeze, "spec/archive/zip/codec/store/compress/seek_spec.rb".freeze, "spec/archive/zip/codec/store/compress/tell_spec.rb".freeze, "spec/archive/zip/codec/store/compress/write_spec.rb".freeze, "spec/archive/zip/codec/store/decompress/close_spec.rb".freeze, "spec/archive/zip/codec/store/decompress/data_descriptor_spec.rb".freeze, "spec/archive/zip/codec/store/decompress/new_spec.rb".freeze, "spec/archive/zip/codec/store/decompress/open_spec.rb".freeze, "spec/archive/zip/codec/store/decompress/read_spec.rb".freeze, "spec/archive/zip/codec/store/decompress/rewind_spec.rb".freeze, "spec/archive/zip/codec/store/decompress/seek_spec.rb".freeze, "spec/archive/zip/codec/store/decompress/tell_spec.rb".freeze, "spec/archive/zip/codec/store/fixtures/classes.rb".freeze, "spec/archive/zip/codec/store/fixtures/raw_file.txt".freeze, "spec/archive/zip/codec/traditional_encryption/decrypt/close_spec.rb".freeze, "spec/archive/zip/codec/traditional_encryption/decrypt/new_spec.rb".freeze, "spec/archive/zip/codec/traditional_encryption/decrypt/open_spec.rb".freeze, "spec/archive/zip/codec/traditional_encryption/decrypt/read_spec.rb".freeze, "spec/archive/zip/codec/traditional_encryption/decrypt/rewind_spec.rb".freeze, "spec/archive/zip/codec/traditional_encryption/decrypt/seek_spec.rb".freeze, "spec/archive/zip/codec/traditional_encryption/decrypt/tell_spec.rb".freeze, "spec/archive/zip/codec/traditional_encryption/encrypt/close_spec.rb".freeze, "spec/archive/zip/codec/traditional_encryption/encrypt/new_spec.rb".freeze, "spec/archive/zip/codec/traditional_encryption/encrypt/open_spec.rb".freeze, "spec/archive/zip/codec/traditional_encryption/encrypt/rewind_spec.rb".freeze, "spec/archive/zip/codec/traditional_encryption/encrypt/seek_spec.rb".freeze, "spec/archive/zip/codec/traditional_encryption/encrypt/tell_spec.rb".freeze, "spec/archive/zip/codec/traditional_encryption/encrypt/write_spec.rb".freeze, "spec/archive/zip/codec/traditional_encryption/fixtures/classes.rb".freeze, "spec/archive/zip/codec/traditional_encryption/fixtures/encrypted_file.bin".freeze, "spec/archive/zip/codec/traditional_encryption/fixtures/raw_file.txt".freeze, "spec/binary_stringio/new_spec.rb".freeze, "spec/binary_stringio/set_encoding_spec.rb".freeze, "spec/ioextensions/read_exactly_spec.rb".freeze, "spec/zlib/fixtures/classes.rb".freeze, "spec/zlib/fixtures/compressed_file.bin".freeze, "spec/zlib/fixtures/compressed_file_gzip.bin".freeze, "spec/zlib/fixtures/compressed_file_huffman.bin".freeze, "spec/zlib/fixtures/compressed_file_minmem.bin".freeze, "spec/zlib/fixtures/compressed_file_minwin.bin".freeze, "spec/zlib/fixtures/compressed_file_nocomp.bin".freeze, "spec/zlib/fixtures/compressed_file_raw.bin".freeze, "spec/zlib/fixtures/raw_file.txt".freeze, "spec/zlib/zreader/checksum_spec.rb".freeze, "spec/zlib/zreader/close_spec.rb".freeze, "spec/zlib/zreader/compressed_size_spec.rb".freeze, "spec/zlib/zreader/new_spec.rb".freeze, "spec/zlib/zreader/open_spec.rb".freeze, "spec/zlib/zreader/read_spec.rb".freeze, "spec/zlib/zreader/rewind_spec.rb".freeze, "spec/zlib/zreader/seek_spec.rb".freeze, "spec/zlib/zreader/tell_spec.rb".freeze, "spec/zlib/zreader/uncompressed_size_spec.rb".freeze, "spec/zlib/zwriter/checksum_spec.rb".freeze, "spec/zlib/zwriter/close_spec.rb".freeze, "spec/zlib/zwriter/compressed_size_spec.rb".freeze, "spec/zlib/zwriter/new_spec.rb".freeze, "spec/zlib/zwriter/open_spec.rb".freeze, "spec/zlib/zwriter/rewind_spec.rb".freeze, "spec/zlib/zwriter/seek_spec.rb".freeze, "spec/zlib/zwriter/tell_spec.rb".freeze, "spec/zlib/zwriter/uncompressed_size_spec.rb".freeze, "spec/zlib/zwriter/write_spec.rb".freeze] s.homepage = "http://github.com/javanthropus/archive-zip".freeze s.licenses = ["MIT".freeze] s.rubyforge_project = "archive-zip".freeze s.rubygems_version = "2.5.2.1".freeze s.summary = "Simple, extensible, pure Ruby ZIP archive support.".freeze s.test_files = ["spec/archive/dos_time_spec.rb".freeze, "spec/archive/zip/archive_spec.rb".freeze, "spec/archive/zip/codec/deflate/compress/checksum_spec.rb".freeze, "spec/archive/zip/codec/deflate/compress/close_spec.rb".freeze, "spec/archive/zip/codec/deflate/compress/crc32_spec.rb".freeze, "spec/archive/zip/codec/deflate/compress/data_descriptor_spec.rb".freeze, "spec/archive/zip/codec/deflate/compress/new_spec.rb".freeze, "spec/archive/zip/codec/deflate/compress/open_spec.rb".freeze, "spec/archive/zip/codec/deflate/compress/write_spec.rb".freeze, "spec/archive/zip/codec/deflate/decompress/checksum_spec.rb".freeze, "spec/archive/zip/codec/deflate/decompress/close_spec.rb".freeze, "spec/archive/zip/codec/deflate/decompress/crc32_spec.rb".freeze, "spec/archive/zip/codec/deflate/decompress/data_descriptor_spec.rb".freeze, "spec/archive/zip/codec/deflate/decompress/new_spec.rb".freeze, "spec/archive/zip/codec/deflate/decompress/open_spec.rb".freeze, "spec/archive/zip/codec/deflate/fixtures/classes.rb".freeze, "spec/archive/zip/codec/deflate/fixtures/compressed_file.bin".freeze, "spec/archive/zip/codec/deflate/fixtures/compressed_file_nocomp.bin".freeze, "spec/archive/zip/codec/deflate/fixtures/raw_file.txt".freeze, "spec/archive/zip/codec/null_encryption/decrypt/close_spec.rb".freeze, "spec/archive/zip/codec/null_encryption/decrypt/new_spec.rb".freeze, "spec/archive/zip/codec/null_encryption/decrypt/open_spec.rb".freeze, "spec/archive/zip/codec/null_encryption/decrypt/read_spec.rb".freeze, "spec/archive/zip/codec/null_encryption/decrypt/rewind_spec.rb".freeze, "spec/archive/zip/codec/null_encryption/decrypt/seek_spec.rb".freeze, "spec/archive/zip/codec/null_encryption/decrypt/tell_spec.rb".freeze, "spec/archive/zip/codec/null_encryption/encrypt/close_spec.rb".freeze, "spec/archive/zip/codec/null_encryption/encrypt/new_spec.rb".freeze, "spec/archive/zip/codec/null_encryption/encrypt/open_spec.rb".freeze, "spec/archive/zip/codec/null_encryption/encrypt/rewind_spec.rb".freeze, "spec/archive/zip/codec/null_encryption/encrypt/seek_spec.rb".freeze, "spec/archive/zip/codec/null_encryption/encrypt/tell_spec.rb".freeze, "spec/archive/zip/codec/null_encryption/encrypt/write_spec.rb".freeze, "spec/archive/zip/codec/null_encryption/fixtures/classes.rb".freeze, "spec/archive/zip/codec/null_encryption/fixtures/raw_file.txt".freeze, "spec/archive/zip/codec/store/compress/close_spec.rb".freeze, "spec/archive/zip/codec/store/compress/data_descriptor_spec.rb".freeze, "spec/archive/zip/codec/store/compress/new_spec.rb".freeze, "spec/archive/zip/codec/store/compress/open_spec.rb".freeze, "spec/archive/zip/codec/store/compress/rewind_spec.rb".freeze, "spec/archive/zip/codec/store/compress/seek_spec.rb".freeze, "spec/archive/zip/codec/store/compress/tell_spec.rb".freeze, "spec/archive/zip/codec/store/compress/write_spec.rb".freeze, "spec/archive/zip/codec/store/decompress/close_spec.rb".freeze, "spec/archive/zip/codec/store/decompress/data_descriptor_spec.rb".freeze, "spec/archive/zip/codec/store/decompress/new_spec.rb".freeze, "spec/archive/zip/codec/store/decompress/open_spec.rb".freeze, "spec/archive/zip/codec/store/decompress/read_spec.rb".freeze, "spec/archive/zip/codec/store/decompress/rewind_spec.rb".freeze, "spec/archive/zip/codec/store/decompress/seek_spec.rb".freeze, "spec/archive/zip/codec/store/decompress/tell_spec.rb".freeze, "spec/archive/zip/codec/store/fixtures/classes.rb".freeze, "spec/archive/zip/codec/store/fixtures/raw_file.txt".freeze, "spec/archive/zip/codec/traditional_encryption/decrypt/close_spec.rb".freeze, "spec/archive/zip/codec/traditional_encryption/decrypt/new_spec.rb".freeze, "spec/archive/zip/codec/traditional_encryption/decrypt/open_spec.rb".freeze, "spec/archive/zip/codec/traditional_encryption/decrypt/read_spec.rb".freeze, "spec/archive/zip/codec/traditional_encryption/decrypt/rewind_spec.rb".freeze, "spec/archive/zip/codec/traditional_encryption/decrypt/seek_spec.rb".freeze, "spec/archive/zip/codec/traditional_encryption/decrypt/tell_spec.rb".freeze, "spec/archive/zip/codec/traditional_encryption/encrypt/close_spec.rb".freeze, "spec/archive/zip/codec/traditional_encryption/encrypt/new_spec.rb".freeze, "spec/archive/zip/codec/traditional_encryption/encrypt/open_spec.rb".freeze, "spec/archive/zip/codec/traditional_encryption/encrypt/rewind_spec.rb".freeze, "spec/archive/zip/codec/traditional_encryption/encrypt/seek_spec.rb".freeze, "spec/archive/zip/codec/traditional_encryption/encrypt/tell_spec.rb".freeze, "spec/archive/zip/codec/traditional_encryption/encrypt/write_spec.rb".freeze, "spec/archive/zip/codec/traditional_encryption/fixtures/classes.rb".freeze, "spec/archive/zip/codec/traditional_encryption/fixtures/encrypted_file.bin".freeze, "spec/archive/zip/codec/traditional_encryption/fixtures/raw_file.txt".freeze, "spec/binary_stringio/new_spec.rb".freeze, "spec/binary_stringio/set_encoding_spec.rb".freeze, "spec/ioextensions/read_exactly_spec.rb".freeze, "spec/zlib/fixtures/classes.rb".freeze, "spec/zlib/fixtures/compressed_file.bin".freeze, "spec/zlib/fixtures/compressed_file_gzip.bin".freeze, "spec/zlib/fixtures/compressed_file_huffman.bin".freeze, "spec/zlib/fixtures/compressed_file_minmem.bin".freeze, "spec/zlib/fixtures/compressed_file_minwin.bin".freeze, "spec/zlib/fixtures/compressed_file_nocomp.bin".freeze, "spec/zlib/fixtures/compressed_file_raw.bin".freeze, "spec/zlib/fixtures/raw_file.txt".freeze, "spec/zlib/zreader/checksum_spec.rb".freeze, "spec/zlib/zreader/close_spec.rb".freeze, "spec/zlib/zreader/compressed_size_spec.rb".freeze, "spec/zlib/zreader/new_spec.rb".freeze, "spec/zlib/zreader/open_spec.rb".freeze, "spec/zlib/zreader/read_spec.rb".freeze, "spec/zlib/zreader/rewind_spec.rb".freeze, "spec/zlib/zreader/seek_spec.rb".freeze, "spec/zlib/zreader/tell_spec.rb".freeze, "spec/zlib/zreader/uncompressed_size_spec.rb".freeze, "spec/zlib/zwriter/checksum_spec.rb".freeze, "spec/zlib/zwriter/close_spec.rb".freeze, "spec/zlib/zwriter/compressed_size_spec.rb".freeze, "spec/zlib/zwriter/new_spec.rb".freeze, "spec/zlib/zwriter/open_spec.rb".freeze, "spec/zlib/zwriter/rewind_spec.rb".freeze, "spec/zlib/zwriter/seek_spec.rb".freeze, "spec/zlib/zwriter/tell_spec.rb".freeze, "spec/zlib/zwriter/uncompressed_size_spec.rb".freeze, "spec/zlib/zwriter/write_spec.rb".freeze] if s.respond_to? :specification_version then s.specification_version = 4 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then s.add_development_dependency(%q.freeze, ["~> 1.6"]) s.add_runtime_dependency(%q.freeze, ["~> 0.3.0"]) s.add_development_dependency(%q.freeze, ["~> 5.11"]) s.add_development_dependency(%q.freeze, ["~> 12.3"]) s.add_development_dependency(%q.freeze, ["~> 3.4"]) s.add_development_dependency(%q.freeze, ["~> 0.9.12"]) else s.add_dependency(%q.freeze, ["~> 1.6"]) s.add_dependency(%q.freeze, ["~> 0.3.0"]) s.add_dependency(%q.freeze, ["~> 5.11"]) s.add_dependency(%q.freeze, ["~> 12.3"]) s.add_dependency(%q.freeze, ["~> 3.4"]) s.add_dependency(%q.freeze, ["~> 0.9.12"]) end else s.add_dependency(%q.freeze, ["~> 1.6"]) s.add_dependency(%q.freeze, ["~> 0.3.0"]) s.add_dependency(%q.freeze, ["~> 5.11"]) s.add_dependency(%q.freeze, ["~> 12.3"]) s.add_dependency(%q.freeze, ["~> 3.4"]) s.add_dependency(%q.freeze, ["~> 0.9.12"]) end end