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)