class PuTTY::Key::PPK::Reader
Handles reading .ppk files.
@private
Public Class Methods
Initializes a new {Reader} with an IO
-like instance to read from.
@param file [Object] The IO
-like instance to read from.
# File lib/putty/key/ppk.rb, line 529 def initialize(file) @file = file @consumed_byte = nil end
Opens a .ppk file for reading (or uses the provided IO
-like instance), creates a new instance of Reader
and yields it to the caller.
@param path_or_io [Object] The path of the .ppk file to be read or an IO
-like object.
@return [Object] The result of yielding to the caller.
raise [Errno::ENOENT] If the file specified by path
does not exist.
# File lib/putty/key/ppk.rb, line 510 def self.open(path_or_io) if path_or_io.kind_of?(String) || path_or_io.kind_of?(Pathname) File.open(path_or_io.to_s, 'rb') do |file| yield Reader.new(file) end else path_or_io.binmode if path_or_io.respond_to?(:binmode) unless path_or_io.respond_to?(:getbyte) path_or_io = GetbyteIo.new(path_or_io) end yield Reader.new(path_or_io) end end
Public Instance Methods
Reads a blob from the file consisting of a Lines field whose value gives the number of Base64 encoded lines in the blob.
@return [String] The Base64-decoded value of the blob.
@raise [FormatError] If there is not a blob starting at the current file position. @raise [FormatError] If the value of the Lines field is not a positive integer.
# File lib/putty/key/ppk.rb, line 597 def blob(name) lines = unsigned_integer("#{name}-Lines") lines.times.map { read_line }.join("\n").unpack('m48').first end
Reads the next field from the file.
@param name [String] The expected field name.
@return [String] The value of the field.
@raise [FormatError] If the current position in the file was not the start of a field with the expected name.
# File lib/putty/key/ppk.rb, line 542 def field(name) line = read_line raise FormatError, "Expected field #{name}, but found #{line}" unless line.start_with?("#{name}: ") line.byteslice(name.bytesize + 2, line.bytesize - name.bytesize - 2) end
Reads the next field from the file.
@param name_regexp [Regexp] A Regexp
that matches the expected field name.
@return [String] The value of the field if the regular expression has no captures. @return [Array] An array containing the regular expression captures as the first elements and the value of the field as the last element.
@raise [FormatError] If the current position in the file was not the start of a field with the expected name.
# File lib/putty/key/ppk.rb, line 560 def field_matching(name_regexp) line = read_line line_regexp = Regexp.new("\\A#{name_regexp.source}: ", name_regexp.options) match = line_regexp.match(line) raise FormatError, "Expected field matching #{name_regexp}, but found #{line}" unless match prefix = match[0] value = line.byteslice(prefix.bytesize, line.bytesize - prefix.bytesize) captures = match.captures captures.empty? ? value : captures + [value] end
Reads the next field from the file as an unsigned integer.
@param name [String] The expected field name.
@return [Integer] The value of the field.
@raise [FormatError] If the current position in the file was not the start of a field with the expected name. @raise [FormatError] If the field did not contain a positive integer.
# File lib/putty/key/ppk.rb, line 580 def unsigned_integer(name, maximum: nil) value = field(name) value = value =~ /\A[0-9]+\z/ && value.to_i raise FormatError, "Expected field #{name} to contain an unsigned integer value, but found #{value}" unless value raise FormatError, "Expected field #{name} to have a maximum of #{maximum}, but found #{value}" if maximum && value > maximum value end
Private Instance Methods
Reads a single new-line (n, rn or r) terminated line from the file, removing the new-line character.
@return [String] The line.
@raise [FormatError] If the end of file was detected before reading a line.
# File lib/putty/key/ppk.rb, line 611 def read_line line = ''.b if @consumed_byte line << @consumed_byte @consumed_byte = nil end while byte = @file.getbyte return line if byte == 0x0a if byte == 0x0d byte = @file.getbyte return line if !byte || byte == 0x0a @consumed_byte = byte return line end line << byte end return line if line.bytesize > 0 raise FormatError, 'Truncated ppk file detected' end