class Glima::Zip
Attributes
password[RW]
Public Class Methods
new(zip_string, password = "")
click to toggle source
# File lib/glima/zip.rb, line 12 def initialize(zip_string, password = "") @zip_string = zip_string @password = password end
read(zip_filename, password = "")
click to toggle source
# File lib/glima/zip.rb, line 8 def self.read(zip_filename, password = "") new(File.open(File.expand_path(zip_filename)).read, password) end
Public Instance Methods
correct_password?(password)
click to toggle source
# File lib/glima/zip.rb, line 17 def correct_password?(password) with_input_stream(password) do |zip| begin # Looking the first entry is not enough, because # some zip files have directory entry which size is zero # and no error is emitted even with wrong password. while entry = zip.get_next_entry size = zip.read.size # Exception if invalid password return false if size != entry.size return true if size > 0 # short cut end rescue Zlib::DataError => e puts "*** #{e} ***" if $DEBUG return false end # False-positive if all files are emtpy. return true end end
encrypted?()
click to toggle source
# File lib/glima/zip.rb, line 55 def encrypted? correct_password?("") end
to_base64()
click to toggle source
# File lib/glima/zip.rb, line 71 def to_base64 Base64.encode64(@zip_string) end
to_decrypted_unicode_zip()
click to toggle source
# File lib/glima/zip.rb, line 75 def to_decrypted_unicode_zip() ::Zip.unicode_names = true out = ::Zip::OutputStream.write_buffer(StringIO.new) do |zos| with_input_stream(@password) do |zis| while entry = zis.get_next_entry name = cp932_path_to_utf8_path(entry.name) # Two types of Exception will occur on encrypted zip: # 1) "invalid block type (Zlib::DataError)" if password is not specified. # 2) "invalid stored block lengths (Zlib::DataError)" if password is wrong. content = zis.read raise Zlib::DataError if content.size != entry.size zos.put_next_entry(name) zos.write(content) end end end Zip.new(out.string) end
to_s()
click to toggle source
# File lib/glima/zip.rb, line 67 def to_s @zip_string end
unlock_password!(password_candidates, logger = nil)
click to toggle source
# File lib/glima/zip.rb, line 38 def unlock_password!(password_candidates, logger = nil) list = sort_by_password_strength(password_candidates.uniq).unshift("") list.each_with_index do |password, i| msg = "Try password(#{i}):'#{password}' (#{password_strength(password)})..." if correct_password?(password) logger.info(msg + " OK.") if logger @password = password return password # Found password else logger.info(msg + " NG.") if logger end end return nil # No luck end
write_to_file(file)
click to toggle source
# File lib/glima/zip.rb, line 59 def write_to_file(file) return file.write(@zip_string) if file.respond_to?(:write) File.open(file, "w") do |f| f.write(@zip_string) end end
Private Instance Methods
cp932_path_to_utf8_path(cp932_path_string)
click to toggle source
1) Convert CP932 (SJIS) to UTF8. 2) Replace path-separators from backslash () to slash (/).
Example:
path = io.get_next_entry.name # rubyzip returns ASCII-8BIT string as name. path is: + ASCII-8BIT + Every backslash is replaced to '/' even in second-byte of CP932.
See also:
https://github.com/rubyzip/rubyzip/blob/master/lib/zip/entry.rb#L223 Zip::Entry#read_local_entry does gsub('\\', '/')
# File lib/glima/zip.rb, line 148 def cp932_path_to_utf8_path(cp932_path_string) # Replace-back all '/' to '\' name = cp932_path_string.force_encoding("BINARY").gsub('/', '\\') # Change endoding to CP932 (SJIS) and replace all '\' to '/' # In this replacement, '\' in second-byte of CP932 will be preserved. name = name.force_encoding("CP932").gsub('\\', '/') # Convert CP932 to UTF-8 return name.encode("utf-8", "CP932", :invalid => :replace, :undef => :replace) end
decrypter(password = "")
click to toggle source
# File lib/glima/zip.rb, line 127 def decrypter(password = "") if password.empty? nil # return empty decrypter else ::Zip::TraditionalDecrypter.new(password) end end
password_strength(password)
click to toggle source
# File lib/glima/zip.rb, line 99 def password_strength(password) password = password.to_s score = Math.log2(password.length + 1) password.scan(/[A-Z]?[a-z]+|[A-Z]+|[-+\d]+|[!"#$%&'()*+,-.\/:;<=>?@\[\\\]^_`{|}~]+/) do |s| score += 1.0 end return score end
sort_by_password_strength(password_array)
click to toggle source
# File lib/glima/zip.rb, line 109 def sort_by_password_strength(password_array) password_array.sort{|a,b| password_strength(b) <=> password_strength(a) } end
with_input_stream(password = "") { |zis| ... }
click to toggle source
# File lib/glima/zip.rb, line 115 def with_input_stream(password = "", &block) # I have to read entire content of @zip_string to avoid the problems on # `general purpose flag Bit 3 is set` # zip_stringio = ::Zip::File.open_buffer(@zip_string).write_buffer zip_stringio.rewind ::Zip::InputStream.open(zip_stringio, 0, decrypter(password)) do |zis| yield zis end end