lib/symmetric_encryption/reader.rb in symmetric-encryption-0.5.2 vs lib/symmetric_encryption/reader.rb in symmetric-encryption-0.6.0
- old
+ new
@@ -2,38 +2,10 @@
# Read from encrypted files and other IO streams
#
# Features:
# * Decryption on the fly whilst reading files
# * Large file support by only buffering small amounts of data in memory
- #
- # # Example: Read and decrypt a line at a time from a file
- # SymmetricEncryption::Reader.open('test_file') do |file|
- # file.each_line {|line| p line }
- # end
- #
- # # Example: Read and decrypt entire file in memory
- # # Not recommended for large files
- # SymmetricEncryption::Reader.open('test_file') {|f| f.read }
- #
- # # Example: Reading a limited number of bytes at a time from the file
- # SymmetricEncryption::Reader.open('test_file') do |file|
- # file.read(1)
- # file.read(5)
- # file.read
- # end
- #
- # # Example: Read and decrypt 5 bytes at a time until the end of file is reached
- # SymmetricEncryption::Reader.open('test_file') do |file|
- # while !file.eof? do
- # file.read(5)
- # end
- # end
- #
- # # Example: Read, Unencrypt and decompress data in a file
- # SymmetricEncryption::Reader.open('encrypted_compressed.zip', :compress => true) do |file|
- # file.each_line {|line| p line }
- # end
class Reader
# Open a file for reading, or use the supplied IO Stream
#
# Parameters:
# filename_or_stream:
@@ -60,22 +32,61 @@
# :buffer_size
# Amount of data to read at a time
# Default: 4096
#
# Note: Decryption occurs before decompression
+ #
+ # # Example: Read and decrypt a line at a time from a file
+ # SymmetricEncryption::Reader.open('test_file') do |file|
+ # file.each_line {|line| p line }
+ # end
+ #
+ # # Example: Read and decrypt entire file in memory
+ # # Not recommended for large files
+ # SymmetricEncryption::Reader.open('test_file') {|f| f.read }
+ #
+ # # Example: Reading a limited number of bytes at a time from the file
+ # SymmetricEncryption::Reader.open('test_file') do |file|
+ # file.read(1)
+ # file.read(5)
+ # file.read
+ # end
+ #
+ # # Example: Read and decrypt 5 bytes at a time until the end of file is reached
+ # SymmetricEncryption::Reader.open('test_file') do |file|
+ # while !file.eof? do
+ # file.read(5)
+ # end
+ # end
+ #
+ # # Example: Read, Unencrypt and decompress data in a file
+ # SymmetricEncryption::Reader.open('encrypted_compressed.zip', :compress => true) do |file|
+ # file.each_line {|line| p line }
+ # end
+ #
+ # # Example: Reading from a CSV file
+ #
+ # require 'fastercsv'
+ # begin
+ # # Must supply :row_sep for FasterCSV otherwise it will attempt to rewind the file etc.
+ # csv = FasterCSV.new(SymmetricEncryption::Reader.open('csv_encrypted'), :row_sep => "\n")
+ # csv.each {|row| p row}
+ # ensure
+ # csv.close if csv
+ # end
def self.open(filename_or_stream, options={}, &block)
raise "options must be a hash" unless options.respond_to?(:each_pair)
mode = options.fetch(:mode, 'r')
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
- block.call(file)
+ block ? block.call(file) : file
ensure
- file.close if file
+ file.close if block && file
end
end
# Decrypt data before reading from the supplied stream
def initialize(ios,options={})
@@ -87,12 +98,12 @@
# Read first block and check for the header
buf = @ios.read(@buffer_size)
if buf.start_with?(SymmetricEncryption::MAGIC_HEADER)
# Header includes magic header and version byte
# Remove header and extract flags
- header, flags = buf.slice!(0..MAGIC_HEADER_SIZE).unpack(MAGIC_HEADER_UNPACK)
- @compressed = flags & 0b1000_0000_0000_0000
+ header, flags = buf.slice!(0..MAGIC_HEADER_SIZE+1).unpack(MAGIC_HEADER_UNPACK)
+ @compressed = (flags & 0b1000_0000_0000_0000) != 0
@version = @compressed ? flags - 0b1000_0000_0000_0000 : flags
else
@version = options[:version]
end
@@ -177,36 +188,61 @@
end
data
end
# Reads a single decrypted line from the file up to and including the optional sep_string.
- # Returns nil on eof
+ # Raises EOFError on eof
# The stream must be opened for reading or an IOError will be raised.
def readline(sep_string = "\n")
+ gets(sep_string) || raise(EOFError.new("End of file reached when trying to read a line"))
+ end
+
+ # 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?
+
# Read more data until we get the sep_string
while (index = @read_buffer.index(sep_string)).nil? && !@ios.eof?
read_block
end
index ||= -1
- @read_buffer.slice!(0..index)
+ data = @read_buffer.slice!(0..index)
+ return nil if data.length == 0 && eof?
+ data
end
# ios.each(sep_string="\n") {|line| block } => ios
# ios.each_line(sep_string="\n") {|line| block } => ios
# Executes the block for every line in ios, where lines are separated by sep_string.
# ios must be opened for reading or an IOError will be raised.
def each_line(sep_string = "\n")
while !eof?
- yield readline(sep_string)
+ yield gets(sep_string)
end
self
end
alias_method :each, :each_line
# Returns whether the end of file has been reached for this stream
def eof?
(@read_buffer.size == 0) && @ios.eof?
+ end
+
+ # Return the approximate offset in bytes of the current input stream
+ # Since the encrypted data size does not match the unencrypted size
+ # this value cannot be guaranteed. Especially if compression is turned on
+ def pos
+ @ios.pos - @read_buffer.size
+ end
+
+ # Rewind back to the beginning of the file
+ def rewind
+ @read_buffer = ''
+ @ios.rewind
end
private
# Read a block of data and append the decrypted data in the read buffer