lib/symmetric_encryption/reader.rb in symmetric-encryption-2.0.0 vs lib/symmetric_encryption/reader.rb in symmetric-encryption-2.0.1

- old
+ new

@@ -20,12 +20,11 @@ # See File.open for open modes # Default: 'rb' # # :buffer_size # Amount of data to read at a time - # The buffer size must be at least large enough to hold the entire - # magic header if one is present + # Minimum Value 128 # Default: 4096 # # The following options are only used if the stream/file has no header # :compress [true|false] # Uses Zlib to decompress the data after it is decrypted @@ -83,22 +82,43 @@ compress = options.fetch(:compress, false) ios = filename_or_stream.is_a?(String) ? ::File.open(filename_or_stream, mode) : filename_or_stream begin file = self.new(ios, options) - file = Zlib::GzipReader.new(file) if file.compressed? || compress + file = Zlib::GzipReader.new(file) if !file.eof? && (file.compressed? || compress) block ? block.call(file) : file ensure file.close if block && file end end + # Returns [true|false] whether the file or stream contains any data + # excluding the header should it have one + def self.empty?(filename_or_stream) + open(filename_or_stream) {|file| file.eof? } + end + + # Returns [true|false] whether the file contains the encryption header + def self.header_present?(filename) + ::File.open(filename, 'rb') {|file| new(file).header_present?} + end + + # After opening a file Returns [true|false] whether the file being + # read has an encryption header + def header_present? + @header_present + end + # Decrypt data before reading from the supplied stream def initialize(ios,options={}) - @ios = ios - @buffer_size = options.fetch(:buffer_size, 4096).to_i - @version = options[:version] + @ios = ios + @buffer_size = options.fetch(:buffer_size, 4096).to_i + @version = options[:version] + @header_present = false + + raise "Buffer size cannot be smaller than 128" unless @buffer_size >= 128 + read_header end # Returns whether the stream being read is compressed # @@ -152,15 +172,17 @@ # See IOS#read # # Reads at most length bytes from the I/O stream, or to the end of file if # length is omitted or is nil. length must be a non-negative integer or nil. # - # At end of file, it returns nil or "" depending on length. + # At end of file, it returns nil if no more data is available, or the last + # remaining bytes def read(length=nil) data = nil if length return '' if length == 0 + return nil if @ios.eof? && (@read_buffer.length == 0) # Read length bytes while (@read_buffer.length < length) && !@ios.eof? read_block end if @read_buffer.length > length @@ -194,15 +216,16 @@ # Reads a single decrypted line from the file up to and including the optional sep_string. # A sep_string of nil reads the entire contents of the file # Returns nil on eof # The stream must be opened for reading or an IOError will be raised. - def gets(sep_string) - return read if sep_string.nil? + def gets(sep_string,length=nil) + return read(length) if sep_string.nil? # Read more data until we get the sep_string while (index = @read_buffer.index(sep_string)).nil? && !@ios.eof? + break if length && @read_buffer.length >= length read_block end index ||= -1 data = @read_buffer.slice!(0..index) @pos += data.length @@ -286,26 +309,31 @@ private # Read the header from the file if present def read_header - @compressed = nil + @compressed = nil @pos = 0 # Read first block and check for the header buf = @ios.read(@buffer_size) # Use cipher specified in header, or global cipher if it has no header - @compressed, iv, key, cipher_name, decryption_cipher = SymmetricEncryption::Cipher.parse_magic_header!(buf, @version) + @compressed, iv, key, cipher_name, version, decryption_cipher = SymmetricEncryption::Cipher.parse_magic_header!(buf, @version) + @header_present = true if iv || key || version @stream_cipher = ::OpenSSL::Cipher.new(cipher_name || decryption_cipher.cipher_name) @stream_cipher.decrypt @stream_cipher.key = key || decryption_cipher.send(:key) @stream_cipher.iv = iv || decryption_cipher.send(:iv) # First call to #update should return an empty string anyway - @read_buffer = @stream_cipher.update(buf) - @read_buffer << @stream_cipher.final if @ios.eof? + if buf && buf.length > 0 + @read_buffer = @stream_cipher.update(buf) + @read_buffer << @stream_cipher.final if @ios.eof? + else + @read_buffer = '' + end end # Read a block of data and append the decrypted data in the read buffer def read_block buf = @ios.read(@buffer_size)