rubyzip 1.1.6 → 1.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of rubyzip might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +66 -13
- data/lib/zip.rb +5 -1
- data/lib/zip/central_directory.rb +1 -1
- data/lib/zip/crypto/encryption.rb +11 -0
- data/lib/zip/crypto/null_encryption.rb +45 -0
- data/lib/zip/crypto/traditional_encryption.rb +99 -0
- data/lib/zip/deflater.rb +6 -3
- data/lib/zip/entry.rb +11 -5
- data/lib/zip/entry_set.rb +5 -6
- data/lib/zip/extra_field.rb +1 -0
- data/lib/zip/extra_field/ntfs.rb +92 -0
- data/lib/zip/file.rb +11 -1
- data/lib/zip/filesystem.rb +4 -0
- data/lib/zip/inflater.rb +4 -3
- data/lib/zip/input_stream.rb +10 -4
- data/lib/zip/output_stream.rb +12 -7
- data/lib/zip/version.rb +1 -1
- data/test/basic_zip_file_test.rb +1 -1
- data/test/central_directory_entry_test.rb +1 -1
- data/test/central_directory_test.rb +5 -1
- data/test/crypto/null_encryption_test.rb +53 -0
- data/test/crypto/traditional_encryption_test.rb +80 -0
- data/test/data/WarnInvalidDate.zip +0 -0
- data/test/data/ntfs.zip +0 -0
- data/test/data/zipWithEncryption.zip +0 -0
- data/test/deflater_test.rb +14 -9
- data/test/encryption_test.rb +42 -0
- data/test/entry_set_test.rb +14 -1
- data/test/entry_test.rb +2 -2
- data/test/errors_test.rb +1 -1
- data/test/extra_field_test.rb +10 -1
- data/test/file_extract_directory_test.rb +3 -2
- data/test/file_extract_test.rb +2 -2
- data/test/file_split_test.rb +2 -2
- data/test/file_test.rb +16 -13
- data/test/filesystem/dir_iterator_test.rb +1 -1
- data/test/filesystem/directory_test.rb +1 -1
- data/test/filesystem/file_mutating_test.rb +1 -1
- data/test/filesystem/file_nonmutating_test.rb +10 -1
- data/test/filesystem/file_stat_test.rb +1 -1
- data/test/inflater_test.rb +1 -1
- data/test/input_stream_test.rb +1 -1
- data/test/ioextras/abstract_input_stream_test.rb +1 -1
- data/test/ioextras/abstract_output_stream_test.rb +1 -1
- data/test/ioextras/fake_io_test.rb +1 -1
- data/test/local_entry_test.rb +14 -11
- data/test/output_stream_test.rb +18 -3
- data/test/pass_thru_compressor_test.rb +2 -2
- data/test/pass_thru_decompressor_test.rb +1 -1
- data/test/settings_test.rb +23 -2
- data/test/test_helper.rb +1 -1
- data/test/unicode_file_names_and_comments_test.rb +16 -4
- data/test/zip64_full_test.rb +5 -1
- data/test/zip64_support_test.rb +1 -1
- metadata +104 -6
- data/test/dummy.txt +0 -1
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 21b11d72a619a00f577f3ac15a65d309d07ab57b
         | 
| 4 | 
            +
              data.tar.gz: d2c28c4157e692118e26478c34205094eb2ca055
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 935e9064b3fc98693e8e0eef68f147b9a62ba4dcf25c9f42cf9828a544665d749fd8ff5866b52183a569171fd3aecddb09613d526da1bc43bf34c0b556bcea02
         | 
| 7 | 
            +
              data.tar.gz: b9e36842ed0830befe3ca556b954757975291f8c523be419213c5974015742e41c4bb4a4b44c88a746ad22f7656f23162cf5e5d5e7591d917927ae9830ad8dae
         | 
    
        data/README.md
    CHANGED
    
    | @@ -1,7 +1,8 @@ | |
| 1 1 | 
             
            # rubyzip
         | 
| 2 | 
            -
            [](http://badge.fury.io/rb/rubyzip)
         | 
| 3 | 
            +
            [](http://travis-ci.org/rubyzip/rubyzip)
         | 
| 4 | 
            +
            [](https://codeclimate.com/github/rubyzip/rubyzip)
         | 
| 5 | 
            +
            [](https://coveralls.io/r/rubyzip/rubyzip?branch=master)
         | 
| 5 6 |  | 
| 6 7 | 
             
            rubyzip is a ruby library for reading and writing zip files.
         | 
| 7 8 |  | 
| @@ -9,7 +10,7 @@ rubyzip is a ruby library for reading and writing zip files. | |
| 9 10 |  | 
| 10 11 | 
             
            Rubyzip interface changed!!! No need to do `require "zip/zip"` and `Zip` prefix in class names removed.
         | 
| 11 12 |  | 
| 12 | 
            -
            If you have issues with any third-party gems  | 
| 13 | 
            +
            If you have issues with any third-party gems that require an old version of rubyzip, you can use this workaround:
         | 
| 13 14 |  | 
| 14 15 | 
             
            ```ruby
         | 
| 15 16 | 
             
            gem 'rubyzip', '>= 1.0.0' # will load new rubyzip version
         | 
| @@ -58,18 +59,52 @@ end | |
| 58 59 | 
             
            ```
         | 
| 59 60 |  | 
| 60 61 | 
             
            ### Zipping a directory recursively
         | 
| 62 | 
            +
            Copy from [here](https://github.com/rubyzip/rubyzip/blob/05916bf89181e1955118fd3ea059f18acac28cc8/samples/example_recursive.rb )
         | 
| 61 63 |  | 
| 62 64 | 
             
            ```ruby
         | 
| 63 65 | 
             
            require 'rubygems'
         | 
| 64 66 | 
             
            require 'zip'
         | 
| 65 | 
            -
             | 
| 66 | 
            -
             | 
| 67 | 
            -
             | 
| 68 | 
            -
             | 
| 69 | 
            -
             | 
| 70 | 
            -
             | 
| 71 | 
            -
             | 
| 72 | 
            -
             | 
| 67 | 
            +
            # This is a simple example which uses rubyzip to
         | 
| 68 | 
            +
            # recursively generate a zip file from the contents of
         | 
| 69 | 
            +
            # a specified directory. The directory itself is not
         | 
| 70 | 
            +
            # included in the archive, rather just its contents.
         | 
| 71 | 
            +
            #
         | 
| 72 | 
            +
            # Usage:
         | 
| 73 | 
            +
            # require /path/to/the/ZipFileGenerator/Class
         | 
| 74 | 
            +
            # directoryToZip = "/tmp/input"
         | 
| 75 | 
            +
            # outputFile = "/tmp/out.zip"
         | 
| 76 | 
            +
            # zf = ZipFileGenerator.new(directoryToZip, outputFile)
         | 
| 77 | 
            +
            # zf.write()
         | 
| 78 | 
            +
             | 
| 79 | 
            +
            class ZipFileGenerator
         | 
| 80 | 
            +
              # Initialize with the directory to zip and the location of the output archive.
         | 
| 81 | 
            +
              def initialize(inputDir, outputFile)
         | 
| 82 | 
            +
                @inputDir = inputDir
         | 
| 83 | 
            +
                @outputFile = outputFile
         | 
| 84 | 
            +
              end
         | 
| 85 | 
            +
              # Zip the input directory.
         | 
| 86 | 
            +
              def write()
         | 
| 87 | 
            +
                entries = Dir.entries(@inputDir); entries.delete("."); entries.delete("..")
         | 
| 88 | 
            +
                io = Zip::File.open(@outputFile, Zip::File::CREATE);
         | 
| 89 | 
            +
                writeEntries(entries, "", io)
         | 
| 90 | 
            +
                io.close();
         | 
| 91 | 
            +
              end
         | 
| 92 | 
            +
              # A helper method to make the recursion work.
         | 
| 93 | 
            +
              private
         | 
| 94 | 
            +
              def writeEntries(entries, path, io)
         | 
| 95 | 
            +
                entries.each { |e|
         | 
| 96 | 
            +
                  zipFilePath = path == "" ? e : File.join(path, e)
         | 
| 97 | 
            +
                  diskFilePath = File.join(@inputDir, zipFilePath)
         | 
| 98 | 
            +
                  puts "Deflating " + diskFilePath
         | 
| 99 | 
            +
                  if File.directory?(diskFilePath)
         | 
| 100 | 
            +
                    io.mkdir(zipFilePath)
         | 
| 101 | 
            +
                    subdir =Dir.entries(diskFilePath); subdir.delete("."); subdir.delete("..")
         | 
| 102 | 
            +
                    writeEntries(subdir, zipFilePath, io)
         | 
| 103 | 
            +
                  else
         | 
| 104 | 
            +
                    io.get_output_stream(zipFilePath) { |f| f.print(File.open(diskFilePath, "rb").read())}
         | 
| 105 | 
            +
                  end
         | 
| 106 | 
            +
                }
         | 
| 107 | 
            +
              end
         | 
| 73 108 | 
             
            end
         | 
| 74 109 | 
             
            ```
         | 
| 75 110 |  | 
| @@ -111,6 +146,19 @@ Zip::File.open('foo.zip') do |zip_file| | |
| 111 146 | 
             
            end
         | 
| 112 147 | 
             
            ```
         | 
| 113 148 |  | 
| 149 | 
            +
            ### Password Protection (Experimental)
         | 
| 150 | 
            +
             | 
| 151 | 
            +
            RubyZip supports reading/writing zip files with traditional zip encryption (a.k.a. "ZipCrypto"). AES encryption is not yet supported. It can be used with buffer streams, e.g.:
         | 
| 152 | 
            +
             | 
| 153 | 
            +
            ```ruby
         | 
| 154 | 
            +
            Zip::OutputStream.write_buffer(::StringIO.new(''), Zip::TraditionalEncrypter.new('password')) do |out|
         | 
| 155 | 
            +
              out.put_next_entry("my_file.txt")
         | 
| 156 | 
            +
              out.write my_data
         | 
| 157 | 
            +
            end.string
         | 
| 158 | 
            +
            ```
         | 
| 159 | 
            +
             | 
| 160 | 
            +
            This is an experimental feature and the interface for encryption may change in future versions.
         | 
| 161 | 
            +
             | 
| 114 162 | 
             
            ## Known issues
         | 
| 115 163 |  | 
| 116 164 | 
             
            ### Modify docx file with rubyzip
         | 
| @@ -158,12 +206,17 @@ If you want to store non english names and want to open properly file on Windows | |
| 158 206 | 
             
            Zip.unicode_names = true
         | 
| 159 207 | 
             
            ```
         | 
| 160 208 |  | 
| 209 | 
            +
            In some zip date of files stored in incorrect format. You can hide warning about it by using:
         | 
| 210 | 
            +
             | 
| 211 | 
            +
            ```ruby
         | 
| 212 | 
            +
            Zip.warn_invalid_date = false
         | 
| 213 | 
            +
            ```
         | 
| 214 | 
            +
             | 
| 161 215 | 
             
            You can set the default compression level like so:
         | 
| 162 216 |  | 
| 163 217 | 
             
            ```ruby
         | 
| 164 218 | 
             
            Zip.default_compression = Zlib::DEFAULT_COMPRESSION
         | 
| 165 219 | 
             
            ```
         | 
| 166 | 
            -
             | 
| 167 220 | 
             
            It defaults to `Zlib::DEFAULT_COMPRESSION`. Possible values are `Zlib::BEST_COMPRESSION`, `Zlib::DEFAULT_COMPRESSION` and `Zlib::NO_COMPRESSION`
         | 
| 168 221 |  | 
| 169 222 | 
             
            All settings in same time
         | 
    
        data/lib/zip.rb
    CHANGED
    
    | @@ -21,6 +21,9 @@ require 'zip/null_compressor' | |
| 21 21 | 
             
            require 'zip/null_input_stream'
         | 
| 22 22 | 
             
            require 'zip/pass_thru_compressor'
         | 
| 23 23 | 
             
            require 'zip/pass_thru_decompressor'
         | 
| 24 | 
            +
            require 'zip/crypto/encryption'
         | 
| 25 | 
            +
            require 'zip/crypto/null_encryption'
         | 
| 26 | 
            +
            require 'zip/crypto/traditional_encryption'
         | 
| 24 27 | 
             
            require 'zip/inflater'
         | 
| 25 28 | 
             
            require 'zip/deflater'
         | 
| 26 29 | 
             
            require 'zip/streamable_stream'
         | 
| @@ -34,7 +37,7 @@ end | |
| 34 37 |  | 
| 35 38 | 
             
            module Zip
         | 
| 36 39 | 
             
              extend self
         | 
| 37 | 
            -
              attr_accessor :unicode_names, :on_exists_proc, :continue_on_exists_proc, :sort_entries, :default_compression, :write_zip64_support
         | 
| 40 | 
            +
              attr_accessor :unicode_names, :on_exists_proc, :continue_on_exists_proc, :sort_entries, :default_compression, :write_zip64_support, :warn_invalid_date
         | 
| 38 41 |  | 
| 39 42 | 
             
              def reset!
         | 
| 40 43 | 
             
                @_ran_once = false
         | 
| @@ -44,6 +47,7 @@ module Zip | |
| 44 47 | 
             
                @sort_entries = false
         | 
| 45 48 | 
             
                @default_compression = ::Zlib::DEFAULT_COMPRESSION
         | 
| 46 49 | 
             
                @write_zip64_support = false
         | 
| 50 | 
            +
                @warn_invalid_date = true
         | 
| 47 51 | 
             
              end
         | 
| 48 52 |  | 
| 49 53 | 
             
              def setup
         | 
| @@ -0,0 +1,45 @@ | |
| 1 | 
            +
            module Zip
         | 
| 2 | 
            +
              module NullEncryption
         | 
| 3 | 
            +
                def header_bytesize
         | 
| 4 | 
            +
                  0
         | 
| 5 | 
            +
                end
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def gp_flags
         | 
| 8 | 
            +
                  0
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
              end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              class NullEncrypter < Encrypter
         | 
| 13 | 
            +
                include NullEncryption
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                def header(mtime)
         | 
| 16 | 
            +
                  ''
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                def encrypt(data)
         | 
| 20 | 
            +
                  data
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                def data_descriptor(crc32, compressed_size, uncomprssed_size)
         | 
| 24 | 
            +
                  ''
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                def reset!
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
              end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
              class NullDecrypter < Decrypter
         | 
| 32 | 
            +
                include NullEncryption
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                def decrypt(data)
         | 
| 35 | 
            +
                  data
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                def reset!(header)
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
            end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
            # Copyright (C) 2002, 2003 Thomas Sondergaard
         | 
| 44 | 
            +
            # rubyzip is free software; you can redistribute it and/or
         | 
| 45 | 
            +
            # modify it under the terms of the ruby license.
         | 
| @@ -0,0 +1,99 @@ | |
| 1 | 
            +
            module Zip
         | 
| 2 | 
            +
              module TraditionalEncryption
         | 
| 3 | 
            +
                def initialize(password)
         | 
| 4 | 
            +
                  @password = password
         | 
| 5 | 
            +
                  reset_keys!
         | 
| 6 | 
            +
                end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                def header_bytesize
         | 
| 9 | 
            +
                  12
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                def gp_flags
         | 
| 13 | 
            +
                  0x0001 | 0x0008
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                protected
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                def reset_keys!
         | 
| 19 | 
            +
                  @key0 = 0x12345678
         | 
| 20 | 
            +
                  @key1 = 0x23456789
         | 
| 21 | 
            +
                  @key2 = 0x34567890
         | 
| 22 | 
            +
                  @password.each_byte do |byte|
         | 
| 23 | 
            +
                    update_keys(byte.chr)
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                def update_keys(n)
         | 
| 28 | 
            +
                  @key0 = ~Zlib.crc32(n, ~@key0)
         | 
| 29 | 
            +
                  @key1 = ((@key1 + (@key0 & 0xff)) * 134775813 + 1) & 0xffffffff
         | 
| 30 | 
            +
                  @key2 = ~Zlib.crc32((@key1 >> 24).chr, ~@key2)
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                def decrypt_byte
         | 
| 34 | 
            +
                  temp = (@key2 & 0xffff) | 2
         | 
| 35 | 
            +
                  ((temp * (temp ^ 1)) >> 8) & 0xff
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
              end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
              class TraditionalEncrypter < Encrypter
         | 
| 40 | 
            +
                include TraditionalEncryption
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                def header(mtime)
         | 
| 43 | 
            +
                  [].tap do |header|
         | 
| 44 | 
            +
                    (header_bytesize - 2).times do
         | 
| 45 | 
            +
                      header << Random.rand(0..255)
         | 
| 46 | 
            +
                    end
         | 
| 47 | 
            +
                    header << (mtime.to_binary_dos_time & 0xff)
         | 
| 48 | 
            +
                    header << (mtime.to_binary_dos_time >> 8)
         | 
| 49 | 
            +
                  end.map{|x| encode x}.pack("C*")
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                def encrypt(data)
         | 
| 53 | 
            +
                  data.unpack("C*").map{|x| encode x}.pack("C*")
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                def data_descriptor(crc32, compressed_size, uncomprssed_size)
         | 
| 57 | 
            +
                  [0x08074b50, crc32, compressed_size, uncomprssed_size].pack("VVVV")
         | 
| 58 | 
            +
                end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                def reset!
         | 
| 61 | 
            +
                  reset_keys!
         | 
| 62 | 
            +
                end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                private
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                def encode(n)
         | 
| 67 | 
            +
                  t = decrypt_byte
         | 
| 68 | 
            +
                  update_keys(n.chr)
         | 
| 69 | 
            +
                  t ^ n
         | 
| 70 | 
            +
                end
         | 
| 71 | 
            +
              end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
              class TraditionalDecrypter < Decrypter
         | 
| 74 | 
            +
                include TraditionalEncryption
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                def decrypt(data)
         | 
| 77 | 
            +
                  data.unpack("C*").map{|x| decode x}.pack("C*")
         | 
| 78 | 
            +
                end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                def reset!(header)
         | 
| 81 | 
            +
                  reset_keys!
         | 
| 82 | 
            +
                  header.each_byte do |x|
         | 
| 83 | 
            +
                    decode x
         | 
| 84 | 
            +
                  end
         | 
| 85 | 
            +
                end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                private
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                def decode(n)
         | 
| 90 | 
            +
                  n ^= decrypt_byte
         | 
| 91 | 
            +
                  update_keys(n.chr)
         | 
| 92 | 
            +
                  n
         | 
| 93 | 
            +
                end
         | 
| 94 | 
            +
              end
         | 
| 95 | 
            +
            end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
            # Copyright (C) 2002, 2003 Thomas Sondergaard
         | 
| 98 | 
            +
            # rubyzip is free software; you can redistribute it and/or
         | 
| 99 | 
            +
            # modify it under the terms of the ruby license.
         | 
    
        data/lib/zip/deflater.rb
    CHANGED
    
    | @@ -1,23 +1,26 @@ | |
| 1 1 | 
             
            module Zip
         | 
| 2 2 | 
             
              class Deflater < Compressor #:nodoc:all
         | 
| 3 3 |  | 
| 4 | 
            -
                def initialize(output_stream, level = Zip.default_compression)
         | 
| 4 | 
            +
                def initialize(output_stream, level = Zip.default_compression, encrypter = NullEncrypter.new)
         | 
| 5 5 | 
             
                  super()
         | 
| 6 6 | 
             
                  @output_stream = output_stream
         | 
| 7 7 | 
             
                  @zlib_deflater = ::Zlib::Deflate.new(level, -::Zlib::MAX_WBITS)
         | 
| 8 8 | 
             
                  @size          = 0
         | 
| 9 9 | 
             
                  @crc           = ::Zlib.crc32
         | 
| 10 | 
            +
                  @encrypter     = encrypter
         | 
| 11 | 
            +
                  @buffer_stream = ::StringIO.new('')
         | 
| 10 12 | 
             
                end
         | 
| 11 13 |  | 
| 12 14 | 
             
                def << (data)
         | 
| 13 15 | 
             
                  val   = data.to_s
         | 
| 14 16 | 
             
                  @crc  = Zlib::crc32(val, @crc)
         | 
| 15 17 | 
             
                  @size += val.bytesize
         | 
| 16 | 
            -
                  @ | 
| 18 | 
            +
                  @buffer_stream << @zlib_deflater.deflate(data)
         | 
| 17 19 | 
             
                end
         | 
| 18 20 |  | 
| 19 21 | 
             
                def finish
         | 
| 20 | 
            -
                  @output_stream << @ | 
| 22 | 
            +
                  @output_stream << @encrypter.encrypt(@buffer_stream.string)
         | 
| 23 | 
            +
                  @output_stream << @encrypter.encrypt(@zlib_deflater.finish) until @zlib_deflater.finished?
         | 
| 21 24 | 
             
                end
         | 
| 22 25 |  | 
| 23 26 | 
             
                attr_reader :size, :crc
         | 
    
        data/lib/zip/entry.rb
    CHANGED
    
    | @@ -74,6 +74,8 @@ module Zip | |
| 74 74 | 
             
                def time
         | 
| 75 75 | 
             
                  if @extra['UniversalTime']
         | 
| 76 76 | 
             
                    @extra['UniversalTime'].mtime
         | 
| 77 | 
            +
                  elsif @extra['NTFS']
         | 
| 78 | 
            +
                    @extra['NTFS'].mtime
         | 
| 77 79 | 
             
                  else
         | 
| 78 80 | 
             
                    # Standard time field in central directory has local time
         | 
| 79 81 | 
             
                    # under archive creator. Then, we can't get timezone.
         | 
| @@ -84,10 +86,10 @@ module Zip | |
| 84 86 | 
             
                alias :mtime :time
         | 
| 85 87 |  | 
| 86 88 | 
             
                def time=(value)
         | 
| 87 | 
            -
                  unless @extra.member?('UniversalTime')
         | 
| 89 | 
            +
                  unless @extra.member?('UniversalTime') || @extra.member?('NTFS')
         | 
| 88 90 | 
             
                    @extra.create('UniversalTime')
         | 
| 89 91 | 
             
                  end
         | 
| 90 | 
            -
                  @extra['UniversalTime'].mtime = value
         | 
| 92 | 
            +
                  (@extra['UniversalTime'] || @extra['NTFS']).mtime = value
         | 
| 91 93 | 
             
                  @time                         = value
         | 
| 92 94 | 
             
                end
         | 
| 93 95 |  | 
| @@ -141,7 +143,7 @@ module Zip | |
| 141 143 | 
             
                end
         | 
| 142 144 |  | 
| 143 145 | 
             
                def next_header_offset #:nodoc:all
         | 
| 144 | 
            -
                  local_entry_offset + self.compressed_size
         | 
| 146 | 
            +
                  local_entry_offset + self.compressed_size + data_descriptor_size
         | 
| 145 147 | 
             
                end
         | 
| 146 148 |  | 
| 147 149 | 
             
                # Extracts entry to file dest_path (defaults to @name).
         | 
| @@ -362,7 +364,7 @@ module Zip | |
| 362 364 | 
             
                  unpack_c_dir_entry(static_sized_fields_buf)
         | 
| 363 365 | 
             
                  check_c_dir_entry_signature
         | 
| 364 366 | 
             
                  set_time(@last_mod_date, @last_mod_time)
         | 
| 365 | 
            -
                  @name = io.read(@name_length). | 
| 367 | 
            +
                  @name = io.read(@name_length).tr('\\', '/')
         | 
| 366 368 | 
             
                  read_c_dir_extra_field(io)
         | 
| 367 369 | 
             
                  @comment = io.read(@comment_length)
         | 
| 368 370 | 
             
                  check_c_dir_entry_comment_size
         | 
| @@ -569,7 +571,7 @@ module Zip | |
| 569 571 | 
             
                def set_time(binary_dos_date, binary_dos_time)
         | 
| 570 572 | 
             
                  @time = ::Zip::DOSTime.parse_binary_dos_format(binary_dos_date, binary_dos_time)
         | 
| 571 573 | 
             
                rescue ArgumentError
         | 
| 572 | 
            -
                  puts "Invalid date/time in zip entry"
         | 
| 574 | 
            +
                  puts "Invalid date/time in zip entry" if ::Zip.warn_invalid_date
         | 
| 573 575 | 
             
                end
         | 
| 574 576 |  | 
| 575 577 | 
             
                def create_file(dest_path, continue_on_exists_proc = proc { Zip.continue_on_exists_proc })
         | 
| @@ -646,6 +648,10 @@ module Zip | |
| 646 648 | 
             
                  end
         | 
| 647 649 | 
             
                end
         | 
| 648 650 |  | 
| 651 | 
            +
                def data_descriptor_size
         | 
| 652 | 
            +
                  (@gp_flags & 0x0008) > 0 ? 16 : 0
         | 
| 653 | 
            +
                end
         | 
| 654 | 
            +
             | 
| 649 655 | 
             
                # create a zip64 extra information field if we need one
         | 
| 650 656 | 
             
                def prep_zip64_extra(for_local_header) #:nodoc:all
         | 
| 651 657 | 
             
                  return unless ::Zip.write_zip64_support
         | 
    
        data/lib/zip/entry_set.rb
    CHANGED
    
    | @@ -38,17 +38,13 @@ module Zip | |
| 38 38 | 
             
                end
         | 
| 39 39 |  | 
| 40 40 | 
             
                def each(&block)
         | 
| 41 | 
            -
                  @entry_set =  | 
| 41 | 
            +
                  @entry_set = sorted_entries.dup.each do |_, value|
         | 
| 42 42 | 
             
                    block.call(value)
         | 
| 43 43 | 
             
                  end
         | 
| 44 44 | 
             
                end
         | 
| 45 45 |  | 
| 46 46 | 
             
                def entries
         | 
| 47 | 
            -
                   | 
| 48 | 
            -
                    @entry_set.values.sort_by{|x| x.name}
         | 
| 49 | 
            -
                  else
         | 
| 50 | 
            -
                    @entry_set.values
         | 
| 51 | 
            -
                  end
         | 
| 47 | 
            +
                  sorted_entries.values
         | 
| 52 48 | 
             
                end
         | 
| 53 49 |  | 
| 54 50 | 
             
                # deep clone
         | 
| @@ -74,6 +70,9 @@ module Zip | |
| 74 70 | 
             
                end
         | 
| 75 71 |  | 
| 76 72 | 
             
                protected
         | 
| 73 | 
            +
                def sorted_entries
         | 
| 74 | 
            +
                  ::Zip.sort_entries ? Hash[@entry_set.sort] : @entry_set
         | 
| 75 | 
            +
                end
         | 
| 77 76 |  | 
| 78 77 | 
             
                private
         | 
| 79 78 | 
             
                def to_key(entry)
         | 
    
        data/lib/zip/extra_field.rb
    CHANGED
    
    | @@ -94,6 +94,7 @@ require 'zip/extra_field/old_unix' | |
| 94 94 | 
             
            require 'zip/extra_field/unix'
         | 
| 95 95 | 
             
            require 'zip/extra_field/zip64'
         | 
| 96 96 | 
             
            require 'zip/extra_field/zip64_placeholder'
         | 
| 97 | 
            +
            require 'zip/extra_field/ntfs'
         | 
| 97 98 |  | 
| 98 99 | 
             
            # Copyright (C) 2002, 2003 Thomas Sondergaard
         | 
| 99 100 | 
             
            # rubyzip is free software; you can redistribute it and/or
         |