archive-zip-0.11.0/0000755000175000017500000000000013367560454016563 5ustar ruby_packagingruby_packagingarchive-zip-0.11.0/NEWS.md0000644000175000017500000001016013367560454017657 0ustar ruby_packagingruby_packaging# 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.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.11.0/spec/0000755000175000017500000000000013367560454017515 5ustar ruby_packagingruby_packagingarchive-zip-0.11.0/spec/archive/0000755000175000017500000000000013367560454021136 5ustar ruby_packagingruby_packagingarchive-zip-0.11.0/spec/archive/dos_time_spec.rb0000644000175000017500000000657313367560454024313 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/0000755000175000017500000000000013367560454021740 5ustar ruby_packagingruby_packagingarchive-zip-0.11.0/spec/archive/zip/codec/0000755000175000017500000000000013367560454023015 5ustar ruby_packagingruby_packagingarchive-zip-0.11.0/spec/archive/zip/codec/store/0000755000175000017500000000000013367560454024151 5ustar ruby_packagingruby_packagingarchive-zip-0.11.0/spec/archive/zip/codec/store/compress/0000755000175000017500000000000013367560454026004 5ustar ruby_packagingruby_packagingarchive-zip-0.11.0/spec/archive/zip/codec/store/compress/close_spec.rb0000644000175000017500000000160713367560454030454 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/store/compress/seek_spec.rb0000644000175000017500000000371313367560454030276 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/store/compress/new_spec.rb0000644000175000017500000000065513367560454030142 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/store/compress/open_spec.rb0000644000175000017500000000157313367560454030312 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/store/compress/data_descriptor_spec.rb0000644000175000017500000000456413367560454032523 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/store/compress/tell_spec.rb0000644000175000017500000000144113367560454030303 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/store/compress/write_spec.rb0000644000175000017500000000152013367560454030473 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/store/compress/rewind_spec.rb0000644000175000017500000000152713367560454030640 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/store/fixtures/0000755000175000017500000000000013367560454026022 5ustar ruby_packagingruby_packagingarchive-zip-0.11.0/spec/archive/zip/codec/store/fixtures/raw_file.txt0000644000175000017500000000035313367560454030354 0ustar ruby_packagingruby_packagingtest 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.11.0/spec/archive/zip/codec/store/fixtures/classes.rb0000644000175000017500000000040113367560454027777 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/store/decompress/0000755000175000017500000000000013367560454026315 5ustar ruby_packagingruby_packagingarchive-zip-0.11.0/spec/archive/zip/codec/store/decompress/close_spec.rb0000644000175000017500000000162113367560454030761 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/store/decompress/seek_spec.rb0000644000175000017500000000413013367560454030601 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/store/decompress/read_spec.rb0000644000175000017500000000123613367560454030571 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/store/decompress/new_spec.rb0000644000175000017500000000066313367560454030452 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/store/decompress/open_spec.rb0000644000175000017500000000161713367560454030622 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/store/decompress/data_descriptor_spec.rb0000644000175000017500000000437713367560454033036 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/store/decompress/tell_spec.rb0000644000175000017500000000104713367560454030616 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/store/decompress/rewind_spec.rb0000644000175000017500000000143413367560454031146 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/null_encryption/0000755000175000017500000000000013367560454026241 5ustar ruby_packagingruby_packagingarchive-zip-0.11.0/spec/archive/zip/codec/null_encryption/encrypt/0000755000175000017500000000000013367560454027725 5ustar ruby_packagingruby_packagingarchive-zip-0.11.0/spec/archive/zip/codec/null_encryption/encrypt/close_spec.rb0000644000175000017500000000167113367560454032376 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/null_encryption/encrypt/seek_spec.rb0000644000175000017500000000377513367560454032227 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/null_encryption/encrypt/new_spec.rb0000644000175000017500000000071713367560454032062 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/null_encryption/encrypt/open_spec.rb0000644000175000017500000000170113367560454032224 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/null_encryption/encrypt/tell_spec.rb0000644000175000017500000000150313367560454032223 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/null_encryption/encrypt/write_spec.rb0000644000175000017500000000164513367560454032424 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/null_encryption/encrypt/rewind_spec.rb0000644000175000017500000000165313367560454032561 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/null_encryption/fixtures/0000755000175000017500000000000013367560454030112 5ustar ruby_packagingruby_packagingarchive-zip-0.11.0/spec/archive/zip/codec/null_encryption/fixtures/raw_file.txt0000644000175000017500000000035313367560454032444 0ustar ruby_packagingruby_packagingtest 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.11.0/spec/archive/zip/codec/null_encryption/fixtures/classes.rb0000644000175000017500000000041113367560454032070 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/null_encryption/decrypt/0000755000175000017500000000000013367560454027713 5ustar ruby_packagingruby_packagingarchive-zip-0.11.0/spec/archive/zip/codec/null_encryption/decrypt/close_spec.rb0000644000175000017500000000167113367560454032364 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/null_encryption/decrypt/seek_spec.rb0000644000175000017500000000424613367560454032207 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/null_encryption/decrypt/read_spec.rb0000644000175000017500000000131313367560454032163 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/null_encryption/decrypt/new_spec.rb0000644000175000017500000000071713367560454032050 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/null_encryption/decrypt/open_spec.rb0000644000175000017500000000166113367560454032217 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/null_encryption/decrypt/tell_spec.rb0000644000175000017500000000110513367560454032207 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/null_encryption/decrypt/rewind_spec.rb0000644000175000017500000000151113367560454032540 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/traditional_encryption/0000755000175000017500000000000013367560454027601 5ustar ruby_packagingruby_packagingarchive-zip-0.11.0/spec/archive/zip/codec/traditional_encryption/encrypt/0000755000175000017500000000000013367560454031265 5ustar ruby_packagingruby_packagingarchive-zip-0.11.0/spec/archive/zip/codec/traditional_encryption/encrypt/close_spec.rb0000644000175000017500000000273513367560454033740 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/traditional_encryption/encrypt/seek_spec.rb0000644000175000017500000000506313367560454033557 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/traditional_encryption/encrypt/new_spec.rb0000644000175000017500000000111213367560454033410 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/traditional_encryption/encrypt/open_spec.rb0000644000175000017500000000237713367560454033576 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/traditional_encryption/encrypt/tell_spec.rb0000644000175000017500000000214513367560454033566 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/traditional_encryption/encrypt/write_spec.rb0000644000175000017500000000662513367560454033767 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/traditional_encryption/encrypt/rewind_spec.rb0000644000175000017500000000241713367560454034120 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/traditional_encryption/fixtures/0000755000175000017500000000000013367560454031452 5ustar ruby_packagingruby_packagingarchive-zip-0.11.0/spec/archive/zip/codec/traditional_encryption/fixtures/raw_file.txt0000644000175000017500000000035313367560454034004 0ustar ruby_packagingruby_packagingtest 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.11.0/spec/archive/zip/codec/traditional_encryption/fixtures/classes.rb0000644000175000017500000000101713367560454033433 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/traditional_encryption/fixtures/encrypted_file.bin0000644000175000017500000000036713367560454035146 0ustar ruby_packagingruby_packagingk•ÛÉ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.11.0/spec/archive/zip/codec/traditional_encryption/decrypt/0000755000175000017500000000000013367560454031253 5ustar ruby_packagingruby_packagingarchive-zip-0.11.0/spec/archive/zip/codec/traditional_encryption/decrypt/close_spec.rb0000644000175000017500000000300513367560454033715 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/traditional_encryption/decrypt/seek_spec.rb0000644000175000017500000000544113367560454033545 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/traditional_encryption/decrypt/read_spec.rb0000644000175000017500000000705013367560454033527 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/traditional_encryption/decrypt/new_spec.rb0000644000175000017500000000112713367560454033404 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/traditional_encryption/decrypt/open_spec.rb0000644000175000017500000000246613367560454033563 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/traditional_encryption/decrypt/tell_spec.rb0000644000175000017500000000131013367560454033545 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/traditional_encryption/decrypt/rewind_spec.rb0000644000175000017500000000215413367560454034104 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/deflate/0000755000175000017500000000000013367560454024421 5ustar ruby_packagingruby_packagingarchive-zip-0.11.0/spec/archive/zip/codec/deflate/compress/0000755000175000017500000000000013367560454026254 5ustar ruby_packagingruby_packagingarchive-zip-0.11.0/spec/archive/zip/codec/deflate/compress/close_spec.rb0000644000175000017500000000225113367560454030720 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/deflate/compress/crc32_spec.rb0000644000175000017500000000131213367560454030524 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/deflate/compress/checksum_spec.rb0000644000175000017500000000265713367560454031427 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/deflate/compress/new_spec.rb0000644000175000017500000000202713367560454030405 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/deflate/compress/open_spec.rb0000644000175000017500000000277513367560454030567 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/deflate/compress/data_descriptor_spec.rb0000644000175000017500000000466113367560454032771 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/deflate/compress/write_spec.rb0000644000175000017500000000625513367560454030755 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/deflate/fixtures/0000755000175000017500000000000013367560454026272 5ustar ruby_packagingruby_packagingarchive-zip-0.11.0/spec/archive/zip/codec/deflate/fixtures/compressed_file.bin0000644000175000017500000000024013367560454032123 0ustar ruby_packagingruby_packaging%Ž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.11.0/spec/archive/zip/codec/deflate/fixtures/raw_file.txt0000644000175000017500000000035313367560454030624 0ustar ruby_packagingruby_packagingtest 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.11.0/spec/archive/zip/codec/deflate/fixtures/classes.rb0000644000175000017500000000103613367560454030254 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/deflate/fixtures/compressed_file_nocomp.bin0000644000175000017500000000036013367560454033501 0ustar ruby_packagingruby_packagingëÿ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.11.0/spec/archive/zip/codec/deflate/decompress/0000755000175000017500000000000013367560454026565 5ustar ruby_packagingruby_packagingarchive-zip-0.11.0/spec/archive/zip/codec/deflate/decompress/close_spec.rb0000644000175000017500000000163513367560454031236 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/deflate/decompress/crc32_spec.rb0000644000175000017500000000112513367560454031037 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/deflate/decompress/checksum_spec.rb0000644000175000017500000000113613367560454031727 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/deflate/decompress/new_spec.rb0000644000175000017500000000067313367560454030723 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/deflate/decompress/open_spec.rb0000644000175000017500000000163513367560454031072 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/codec/deflate/decompress/data_descriptor_spec.rb0000644000175000017500000000437213367560454033301 0ustar ruby_packagingruby_packaging# 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.11.0/spec/archive/zip/archive_spec.rb0000644000175000017500000000303013367560454024714 0ustar ruby_packagingruby_packaging# 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.11.0/spec/ioextensions/0000755000175000017500000000000013367560454022244 5ustar ruby_packagingruby_packagingarchive-zip-0.11.0/spec/ioextensions/read_exactly_spec.rb0000644000175000017500000000300013367560454026240 0ustar ruby_packagingruby_packaging# 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.11.0/spec/binary_stringio/0000755000175000017500000000000013367560454022717 5ustar ruby_packagingruby_packagingarchive-zip-0.11.0/spec/binary_stringio/set_encoding_spec.rb0000644000175000017500000000064113367560454026720 0ustar ruby_packagingruby_packaging# 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.11.0/spec/binary_stringio/new_spec.rb0000644000175000017500000000216213367560454025050 0ustar ruby_packagingruby_packaging# 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.11.0/spec/zlib/0000755000175000017500000000000013367560454020455 5ustar ruby_packagingruby_packagingarchive-zip-0.11.0/spec/zlib/zreader/0000755000175000017500000000000013367560454022111 5ustar ruby_packagingruby_packagingarchive-zip-0.11.0/spec/zlib/zreader/close_spec.rb0000644000175000017500000000053213367560454024555 0ustar ruby_packagingruby_packaging# 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.11.0/spec/zlib/zreader/seek_spec.rb0000644000175000017500000000360213367560454024400 0ustar ruby_packagingruby_packaging# 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.11.0/spec/zlib/zreader/uncompressed_size_spec.rb0000644000175000017500000000100213367560454027202 0ustar ruby_packagingruby_packaging# 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.11.0/spec/zlib/zreader/checksum_spec.rb0000644000175000017500000000214313367560454025252 0ustar ruby_packagingruby_packaging# 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.11.0/spec/zlib/zreader/compressed_size_spec.rb0000644000175000017500000000077213367560454026654 0ustar ruby_packagingruby_packaging# 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.11.0/spec/zlib/zreader/read_spec.rb0000644000175000017500000000322413367560454024364 0ustar ruby_packagingruby_packaging# 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.11.0/spec/zlib/zreader/new_spec.rb0000644000175000017500000000204113367560454024236 0ustar ruby_packagingruby_packaging# 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.11.0/spec/zlib/zreader/open_spec.rb0000644000175000017500000000254313367560454024415 0ustar ruby_packagingruby_packaging# 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.11.0/spec/zlib/zreader/tell_spec.rb0000644000175000017500000000076513367560454024420 0ustar ruby_packagingruby_packaging# 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.11.0/spec/zlib/zreader/rewind_spec.rb0000644000175000017500000000121713367560454024741 0ustar ruby_packagingruby_packaging# 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.11.0/spec/zlib/zwriter/0000755000175000017500000000000013367560454022163 5ustar ruby_packagingruby_packagingarchive-zip-0.11.0/spec/zlib/zwriter/close_spec.rb0000644000175000017500000000053213367560454024627 0ustar ruby_packagingruby_packaging# 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.11.0/spec/zlib/zwriter/seek_spec.rb0000644000175000017500000000347513367560454024462 0ustar ruby_packagingruby_packaging# 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.11.0/spec/zlib/zwriter/uncompressed_size_spec.rb0000644000175000017500000000107413367560454027265 0ustar ruby_packagingruby_packaging# 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.11.0/spec/zlib/zwriter/checksum_spec.rb0000644000175000017500000000240213367560454025322 0ustar ruby_packagingruby_packaging# 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.11.0/spec/zlib/zwriter/compressed_size_spec.rb0000644000175000017500000000106413367560454026721 0ustar ruby_packagingruby_packaging# 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.11.0/spec/zlib/zwriter/new_spec.rb0000644000175000017500000000340013367560454024310 0ustar ruby_packagingruby_packaging# 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.11.0/spec/zlib/zwriter/open_spec.rb0000644000175000017500000000405213367560454024464 0ustar ruby_packagingruby_packaging# 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.11.0/spec/zlib/zwriter/tell_spec.rb0000644000175000017500000000134613367560454024466 0ustar ruby_packagingruby_packaging# 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.11.0/spec/zlib/zwriter/write_spec.rb0000644000175000017500000000132613367560454024656 0ustar ruby_packagingruby_packaging# 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.11.0/spec/zlib/zwriter/rewind_spec.rb0000644000175000017500000000143113367560454025011 0ustar ruby_packagingruby_packaging# 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.11.0/spec/zlib/fixtures/0000755000175000017500000000000013367560454022326 5ustar ruby_packagingruby_packagingarchive-zip-0.11.0/spec/zlib/fixtures/compressed_file.bin0000644000175000017500000000024613367560454026165 0ustar ruby_packagingruby_packagingxœ%Ž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.11.0/spec/zlib/fixtures/compressed_file_huffman.bin0000644000175000017500000000025113367560454027665 0ustar ruby_packagingruby_packagingxÁÁ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.11.0/spec/zlib/fixtures/compressed_file_gzip.bin0000644000175000017500000000026213367560454027214 0ustar ruby_packagingruby_packaging‹%Ž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.11.0/spec/zlib/fixtures/raw_file.txt0000644000175000017500000000035313367560454024660 0ustar ruby_packagingruby_packagingtest 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.11.0/spec/zlib/fixtures/compressed_file_raw.bin0000644000175000017500000000024013367560454027030 0ustar ruby_packagingruby_packaging%Ž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.11.0/spec/zlib/fixtures/classes.rb0000644000175000017500000000271713367560454024317 0ustar ruby_packagingruby_packaging# 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.11.0/spec/zlib/fixtures/compressed_file_minwin.bin0000644000175000017500000000024613367560454027546 0ustar ruby_packagingruby_packaging•%Ž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.11.0/spec/zlib/fixtures/compressed_file_nocomp.bin0000644000175000017500000000036613367560454027543 0ustar ruby_packagingruby_packagingxëÿ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.11.0/spec/zlib/fixtures/compressed_file_minmem.bin0000644000175000017500000000027313367560454027527 0ustar ruby_packagingruby_packagingxœÍÁ 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.11.0/LICENSE0000644000175000017500000000206213367560454017570 0ustar ruby_packagingruby_packaging(The MIT License) Copyright (c) 2018 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.11.0/README.md0000644000175000017500000001713613367560454020052 0ustar ruby_packagingruby_packaging# 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 ## 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) 2018 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.11.0/Rakefile0000644000175000017500000001767013367560454020243 0ustar ruby_packagingruby_packaging# 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.11.0/.yardopts0000644000175000017500000000010513367560454020425 0ustar ruby_packagingruby_packaging--protected --private --main README.md lib/**/*.rb - NEWS.md LICENSE archive-zip-0.11.0/lib/0000755000175000017500000000000013367560454017331 5ustar ruby_packagingruby_packagingarchive-zip-0.11.0/lib/archive/0000755000175000017500000000000013367560454020752 5ustar ruby_packagingruby_packagingarchive-zip-0.11.0/lib/archive/support/0000755000175000017500000000000013367560454022466 5ustar ruby_packagingruby_packagingarchive-zip-0.11.0/lib/archive/support/binary_stringio.rb0000644000175000017500000000150713367560454026220 0ustar ruby_packagingruby_packaging# 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.11.0/lib/archive/support/ioextensions.rb0000644000175000017500000000106313367560454025542 0ustar ruby_packagingruby_packaging# 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.11.0/lib/archive/support/integer.rb0000644000175000017500000000042213367560454024446 0ustar ruby_packagingruby_packaging# 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.11.0/lib/archive/support/zlib.rb0000644000175000017500000004556313367560454023770 0ustar ruby_packagingruby_packaging# 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.11.0/lib/archive/support/iowindow.rb0000644000175000017500000000673313367560454024663 0ustar ruby_packagingruby_packaging# 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.11.0/lib/archive/support/time.rb0000644000175000017500000000731313367560454023755 0ustar ruby_packagingruby_packaging# 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.11.0/lib/archive/support/io-like.rb0000644000175000017500000000052113367560454024342 0ustar ruby_packagingruby_packaging# 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.11.0/lib/archive/zip/0000755000175000017500000000000013367560454021554 5ustar ruby_packagingruby_packagingarchive-zip-0.11.0/lib/archive/zip/extra_field.rb0000644000175000017500000000257613367560454024401 0ustar ruby_packagingruby_packaging# 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.11.0/lib/archive/zip/extra_field/0000755000175000017500000000000013367560454024042 5ustar ruby_packagingruby_packagingarchive-zip-0.11.0/lib/archive/zip/extra_field/unix.rb0000644000175000017500000001135013367560454025352 0ustar ruby_packagingruby_packaging# 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.11.0/lib/archive/zip/extra_field/extended_timestamp.rb0000644000175000017500000001535713367560454030265 0ustar ruby_packagingruby_packaging# 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.11.0/lib/archive/zip/extra_field/raw.rb0000644000175000017500000000600213367560454025156 0ustar ruby_packagingruby_packaging# 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.11.0/lib/archive/zip/entry.rb0000644000175000017500000012561513367560454023254 0ustar ruby_packagingruby_packaging# 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.11.0/lib/archive/zip/codec/0000755000175000017500000000000013367560454022631 5ustar ruby_packagingruby_packagingarchive-zip-0.11.0/lib/archive/zip/codec/deflate.rb0000644000175000017500000002363013367560454024566 0ustar ruby_packagingruby_packaging# 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.11.0/lib/archive/zip/codec/null_encryption.rb0000644000175000017500000002051613367560454026406 0ustar ruby_packagingruby_packaging# 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.11.0/lib/archive/zip/codec/store.rb0000644000175000017500000002542313367560454024320 0ustar ruby_packagingruby_packaging# 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.11.0/lib/archive/zip/codec/traditional_encryption.rb0000644000175000017500000003602313367560454027746 0ustar ruby_packagingruby_packaging# 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.11.0/lib/archive/zip/data_descriptor.rb0000644000175000017500000000426313367560454025255 0ustar ruby_packagingruby_packaging# 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.11.0/lib/archive/zip/error.rb0000644000175000017500000000161013367560454023230 0ustar ruby_packagingruby_packaging# 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.11.0/lib/archive/zip/codec.rb0000644000175000017500000000443413367560454023163 0ustar ruby_packagingruby_packaging# 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].new(general_purpose_flags) raise Zip::Error, 'unsupported compression codec' if codec.nil? codec 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.11.0/lib/archive/zip/version.rb0000644000175000017500000000016013367560454023563 0ustar ruby_packagingruby_packaging# encoding: UTF-8 module Archive; class Zip # The current version of this gem. VERSION = '0.11.0' end; end archive-zip-0.11.0/lib/archive/zip.rb0000644000175000017500000006712413367560454022113 0ustar ruby_packagingruby_packaging# 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.11.0/archive-zip.gemspec0000644000175000017500000003731413367560454022361 0ustar ruby_packagingruby_packaging######################################################### # This file has been automatically generated by gem2tgz # ######################################################### # -*- encoding: utf-8 -*- # stub: archive-zip 0.11.0 ruby lib Gem::Specification.new do |s| s.name = "archive-zip".freeze s.version = "0.11.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] s.date = "2018-01-29" 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] 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.7.6".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