diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index f6e1112..8ecc67d 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -6,11 +6,6 @@ # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. -# Offense count: 76 -# Cop supports --auto-correct. -Layout/EmptyLineAfterGuardClause: - Enabled: false - # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. diff --git a/lib/zip/central_directory.rb b/lib/zip/central_directory.rb index 0b6874e..54ada42 100644 --- a/lib/zip/central_directory.rb +++ b/lib/zip/central_directory.rb @@ -141,6 +141,7 @@ module Zip def get_e_o_c_d(buf) #:nodoc: sig_index = buf.rindex([END_OF_CDS].pack('V')) raise Error, 'Zip end of central directory signature not found' unless sig_index + buf = buf.slice!((sig_index + 4)..(buf.bytesize)) def buf.read(count) @@ -166,8 +167,10 @@ module Zip def get_64_e_o_c_d(buf) #:nodoc: zip_64_start = buf.rindex([ZIP64_END_OF_CDS].pack('V')) raise Error, 'Zip64 end of central directory signature not found' unless zip_64_start + zip_64_locator = buf.rindex([ZIP64_EOCD_LOCATOR].pack('V')) raise Error, 'Zip64 end of central directory signature locator not found' unless zip_64_locator + buf = buf.slice!((zip_64_start + 4)..zip_64_locator) def buf.read(count) @@ -198,6 +201,7 @@ module Zip def ==(other) #:nodoc: return false unless other.kind_of?(CentralDirectory) + @entry_set.entries.sort == other.entries.sort && comment == other.comment end end diff --git a/lib/zip/crypto/decrypted_io.rb b/lib/zip/crypto/decrypted_io.rb index 1dab17c..cddce8a 100644 --- a/lib/zip/crypto/decrypted_io.rb +++ b/lib/zip/crypto/decrypted_io.rb @@ -12,6 +12,7 @@ module Zip while length.nil? || (buffer.bytesize < length) break if input_finished? + buffer << produce_input end diff --git a/lib/zip/entry.rb b/lib/zip/entry.rb index 155b136..bed82f8 100644 --- a/lib/zip/entry.rb +++ b/lib/zip/entry.rb @@ -48,6 +48,7 @@ module Zip def check_name(name) return unless name.start_with?('/') + raise ::Zip::EntryNameError, "Illegal ZipEntry name '#{name}', name must not start with /" end @@ -104,6 +105,7 @@ module Zip def file_type_is?(type) raise InternalError, "current filetype is unknown: #{inspect}" unless @ftype + @ftype == type end @@ -124,6 +126,7 @@ module Zip def name_safe? cleanpath = Pathname.new(@name).cleanpath return false unless cleanpath.relative? + root = ::File::SEPARATOR naive_expanded_path = ::File.join(root, cleanpath.to_s) ::File.absolute_path(cleanpath.to_s, root) == naive_expanded_path @@ -153,6 +156,7 @@ module Zip # that we didn't change the header size (and thus clobber file data or something) def verify_local_header_size! return if @local_header_size.nil? + new_size = calculate_local_header_size raise Error, "local header size changed (#{@local_header_size} -> #{new_size})" if @local_header_size != new_size end @@ -255,6 +259,7 @@ module Zip unless @header_signature == ::Zip::LOCAL_ENTRY_SIGNATURE raise ::Zip::Error, "Zip local header magic not found at location '#{local_header_offset}'" end + set_time(@last_mod_date, @last_mod_time) @name = io.read(@name_length) @@ -274,6 +279,7 @@ module Zip @extra = ::Zip::ExtraField.new(extra) end end + parse_zip64_extra(true) @local_header_size = calculate_local_header_size end @@ -360,16 +366,19 @@ module Zip def check_c_dir_entry_static_header_length(buf) return if buf.bytesize == ::Zip::CDIR_ENTRY_STATIC_HEADER_LENGTH + raise Error, 'Premature end of file. Not enough data for zip cdir entry header' end def check_c_dir_entry_signature return if header_signature == ::Zip::CENTRAL_DIRECTORY_ENTRY_SIGNATURE + raise Error, "Zip local header magic not found at location '#{local_header_offset}'" end def check_c_dir_entry_comment_size return if @comment && @comment.bytesize == @comment_length + raise ::Zip::Error, 'Truncated cdir zip entry header' end @@ -408,6 +417,7 @@ module Zip def get_extra_attributes_from_path(path) # :nodoc: return if Zip::RUNNING_ON_WINDOWS + stat = file_stat(path) @unix_uid = stat.uid @unix_gid = stat.gid @@ -494,6 +504,7 @@ module Zip def ==(other) return false unless other.class == self.class + # Compares contents of local entry and exposed fields keys_equal = %w[compression_method crc compressed_size size name extra filepath].all? do |k| other.__send__(k.to_sym) == __send__(k.to_sym) @@ -635,6 +646,7 @@ module Zip def create_directory(dest_path) return if ::File.directory?(dest_path) + if ::File.exist?(dest_path) if block_given? && yield(self, dest_path) ::FileUtils.rm_f dest_path @@ -659,6 +671,7 @@ module Zip # (required when file sizes exceed 2**32, but can be used for all files) def parse_zip64_extra(for_local_header) #:nodoc:all return if @extra['Zip64'].nil? + if for_local_header @size, @compressed_size = @extra['Zip64'].parse(@size, @compressed_size) else @@ -673,6 +686,7 @@ module Zip # create a zip64 extra information field if we need one def prep_zip64_extra(for_local_header) #:nodoc:all return unless ::Zip.write_zip64_support + need_zip64 = @size >= 0xFFFFFFFF || @compressed_size >= 0xFFFFFFFF need_zip64 ||= @local_header_offset >= 0xFFFFFFFF unless for_local_header if need_zip64 diff --git a/lib/zip/entry_set.rb b/lib/zip/entry_set.rb index 3272b2a..9c50378 100644 --- a/lib/zip/entry_set.rb +++ b/lib/zip/entry_set.rb @@ -50,6 +50,7 @@ module Zip def ==(other) return false unless other.kind_of?(EntrySet) + @entry_set.values == other.entry_set.values end @@ -60,6 +61,7 @@ module Zip def glob(pattern, flags = ::File::FNM_PATHNAME | ::File::FNM_DOTMATCH | ::File::FNM_EXTGLOB) entries.map do |entry| next nil unless ::File.fnmatch(pattern, entry.name.chomp('/'), flags) + yield(entry) if block_given? entry end.compact diff --git a/lib/zip/extra_field.rb b/lib/zip/extra_field.rb index 0dcf0d5..b8bb8a5 100644 --- a/lib/zip/extra_field.rb +++ b/lib/zip/extra_field.rb @@ -36,6 +36,7 @@ module Zip def merge(binstr) return if binstr.empty? + i = 0 while i < binstr.bytesize id = binstr[i, 2] @@ -54,6 +55,7 @@ module Zip unless (field_class = ID_MAP.values.find { |k| k.name == name }) raise Error, "Unknown extra field '#{name}'" end + self[name] = field_class.new end diff --git a/lib/zip/extra_field/generic.rb b/lib/zip/extra_field/generic.rb index d61137f..0bb000b 100644 --- a/lib/zip/extra_field/generic.rb +++ b/lib/zip/extra_field/generic.rb @@ -19,11 +19,13 @@ module Zip warn 'WARNING: weird extra field header ID. Skip parsing it.' return false end + [binstr[2, 2].unpack('v')[0], binstr[4..-1]] end def ==(other) return false if self.class != other.class + each do |k, v| return false if v != other[k] end diff --git a/lib/zip/extra_field/ntfs.rb b/lib/zip/extra_field/ntfs.rb index 687704d..f4f11b2 100644 --- a/lib/zip/extra_field/ntfs.rb +++ b/lib/zip/extra_field/ntfs.rb @@ -19,6 +19,7 @@ module Zip def merge(binstr) return if binstr.empty? + size, content = initial_parse(binstr) (size && content) || return @@ -27,6 +28,7 @@ module Zip tag1 = tags[1] return unless tag1 + ntfs_mtime, ntfs_atime, ntfs_ctime = tag1.unpack('Q= 5 # how many times should we retry? + retried += 1 retry end diff --git a/lib/zip/input_stream.rb b/lib/zip/input_stream.rb index 0bf2485..8d86897 100644 --- a/lib/zip/input_stream.rb +++ b/lib/zip/input_stream.rb @@ -73,6 +73,7 @@ module Zip # Rewinds the stream to the beginning of the current entry def rewind return if @current_entry.nil? + @lineno = 0 @pos = 0 @archive_io.seek(@current_entry.local_header_offset, IO::SEEK_SET) @@ -91,6 +92,7 @@ module Zip def open(filename_or_io, offset = 0, decrypter = nil) zio = new(filename_or_io, offset, decrypter) return zio unless block_given? + begin yield zio ensure @@ -123,6 +125,7 @@ module Zip if @current_entry && @current_entry.encrypted? && @decrypter.is_a?(NullEncrypter) raise Error, 'password required to decode zip file' end + if @current_entry && @current_entry.incomplete? && @current_entry.crc == 0 \ && @current_entry.compressed_size == 0 \ && @current_entry.size == 0 && !@complete_entry diff --git a/lib/zip/ioextras/abstract_input_stream.rb b/lib/zip/ioextras/abstract_input_stream.rb index d2c0db3..e3cf983 100644 --- a/lib/zip/ioextras/abstract_input_stream.rb +++ b/lib/zip/ioextras/abstract_input_stream.rb @@ -35,6 +35,7 @@ module Zip if tbuf.nil? || tbuf.empty? return nil if number_of_bytes + return '' end @@ -69,6 +70,7 @@ module Zip end return read(number_of_bytes) if a_sep_string.nil? + a_sep_string = "#{$/}#{$/}" if a_sep_string.empty? buffer_index = 0 @@ -76,6 +78,7 @@ module Zip while (match_index = @output_buffer.index(a_sep_string, buffer_index)).nil? && !over_limit buffer_index = [buffer_index, @output_buffer.bytesize - a_sep_string.bytesize].max return @output_buffer.empty? ? nil : flush if input_finished? + @output_buffer << produce_input over_limit = (number_of_bytes && @output_buffer.bytesize >= number_of_bytes) end @@ -97,6 +100,7 @@ module Zip def readline(a_sep_string = $/) ret_val = gets(a_sep_string) raise EOFError unless ret_val + ret_val end diff --git a/lib/zip/output_stream.rb b/lib/zip/output_stream.rb index d9bbc4d..6ab4a48 100644 --- a/lib/zip/output_stream.rb +++ b/lib/zip/output_stream.rb @@ -49,6 +49,7 @@ module Zip class << self def open(file_name, encrypter = nil) return new(file_name) unless block_given? + zos = new(file_name, false, encrypter) yield zos ensure @@ -66,6 +67,7 @@ module Zip # Closes the stream and writes the central directory to the zip file def close return if @closed + finalize_current_entry update_local_headers write_central_directory @@ -76,6 +78,7 @@ module Zip # Closes the stream and writes the central directory to the zip file def close_buffer return @output_stream if @closed + finalize_current_entry update_local_headers write_central_directory @@ -87,6 +90,7 @@ module Zip # +entry+ can be a ZipEntry object or a string. def put_next_entry(entry_name, comment = nil, extra = nil, compression_method = Entry::DEFLATED, level = Zip.default_compression) raise Error, 'zip stream is closed' if @closed + new_entry = if entry_name.kind_of?(Entry) entry_name else @@ -105,6 +109,7 @@ module Zip entry = entry.dup raise Error, 'zip stream is closed' if @closed raise Error, 'entry is not a ZipEntry' unless entry.is_a?(Entry) + finalize_current_entry @entry_set << entry src_pos = entry.local_header_offset @@ -123,6 +128,7 @@ module Zip def finalize_current_entry return unless @current_entry + finish @current_entry.compressed_size = @output_stream.tell - @current_entry.local_header_offset - @current_entry.calculate_local_header_size @current_entry.size = @compressor.size diff --git a/lib/zip/streamable_stream.rb b/lib/zip/streamable_stream.rb index 642ddae..90a4444 100644 --- a/lib/zip/streamable_stream.rb +++ b/lib/zip/streamable_stream.rb @@ -22,6 +22,7 @@ module Zip unless @temp_file.closed? raise StandardError, "cannot open entry for reading while its open for writing - #{name}" end + @temp_file.open # reopens tempfile from top @temp_file.binmode if block_given? diff --git a/samples/zipfind.rb b/samples/zipfind.rb index 400e0a6..cd76c26 100755 --- a/samples/zipfind.rb +++ b/samples/zipfind.rb @@ -13,6 +13,7 @@ module Zip Find.find(path) do |fileName| yield(fileName) next unless zipFilePattern.match(fileName) && File.file?(fileName) + begin Zip::File.foreach(fileName) do |zipEntry| yield(fileName + File::SEPARATOR + zipEntry.to_s) diff --git a/test/file_split_test.rb b/test/file_split_test.rb index dfea837..c488f18 100644 --- a/test/file_split_test.rb +++ b/test/file_split_test.rb @@ -28,6 +28,7 @@ class ZipFileSplitTest < MiniTest::Test result = ::Zip::File.split(TEST_ZIP.zip_name, 65_536, false) return if result.nil? + Dir["#{TEST_ZIP.zip_name}.*"].sort.each_with_index do |zip_file_name, index| File.open(zip_file_name, 'rb') do |zip_file| zip_file.read([::Zip::File::SPLIT_SIGNATURE].pack('V').size) if index == 0 diff --git a/test/file_test.rb b/test/file_test.rb index c08bbdb..fb7ec5c 100644 --- a/test/file_test.rb +++ b/test/file_test.rb @@ -623,6 +623,7 @@ class ZipFileTest < MiniTest::Test Zip::File.open_buffer(f) do |zipfile| zipfile.each do |entry| next unless entry.name =~ /README.md/ + data = zipfile.read(entry) end end diff --git a/test/gentestfiles.rb b/test/gentestfiles.rb index 3e76e7d..bb803b9 100755 --- a/test/gentestfiles.rb +++ b/test/gentestfiles.rb @@ -52,6 +52,7 @@ class TestFiles def ensure_dir(name) if File.exist?(name) return if File.stat(name).directory? + File.delete(name) end Dir.mkdir(name)