lib/keepassx/entry.rb in keepassx-0.1.0 vs lib/keepassx/entry.rb in keepassx-1.0.0

- old
+ new

@@ -1,12 +1,14 @@ +# frozen_string_literal: true + # One entry: [FIELDTYPE(FT)][FIELDSIZE(FS)][FIELDDATA(FD)] # [FT+FS+(FD)][FT+FS+(FD)][FT+FS+(FD)][FT+FS+(FD)][FT+FS+(FD)]... - +# # [ 2 bytes] FIELDTYPE # [ 4 bytes] FIELDSIZE, size of FIELDDATA in bytes # [ n bytes] FIELDDATA, n = FIELDSIZE - +# # Notes: # - Strings are stored in UTF-8 encoded form and are null-terminated. # - FIELDTYPE can be one of the following identifiers: # * 0000: Invalid or comment block, block is ignored # * 0001: UUID, uniquely identifying an entry, FIELDSIZE must be 16 @@ -23,59 +25,89 @@ # * 000B: Last access time, FIELDSIZE = 5, FIELDDATA = packed date/time # * 000C: Expiration time, FIELDSIZE = 5, FIELDDATA = packed date/time # * 000D: Binary description UTF-8 encoded string # * 000E: Binary data # * FFFF: Entry terminator, FIELDSIZE must be 0 -# ''' module Keepassx - class Entry - def self.extract_from_payload(header, payload_io) - groups = [] - header.nentries.times do - group = Entry.new(payload_io) - groups << group + class Entry < Fieldable + + set_field_descriptor Keepassx::Field::Entry + + attr_reader :group + + def initialize(payload) + super do + # Do some validation + raise ArgumentError, "'name' is required (type: string)" unless valid_string?(payload[:name]) + raise ArgumentError, "'group_id' is required (type: integer)" unless payload[:group] || valid_integer?(payload[:group_id]) + + # First set @group and @group_id. + # Remove key from payload to not interfere with KeePassX fields format + self.group = payload.delete(:group) + + # Add group_id key to respect KeePassX fields format + payload[:group_id] = group.id + + # Build list of fields + @fields = build_payload(payload) end - groups end - attr_reader :fields - def initialize(payload_io) - fields = [] - begin - field = EntryField.new(payload_io) - fields << field - end while not field.terminator? + class << self - @fields = fields - end + def extract_from_payload(header, payload) + entries = [] + header.entries_count.times { entries << Entry.new(payload) } + entries + end - def length - @fields.map(&:length).reduce(&:+) end - def notes - @fields.detect { |field| field.name == 'notes' }.data.chomp("\000") - end - def password - @fields.detect { |field| field.name == 'password' }.data.chomp("\000") - end + def group=(value) + raise ArgumentError, "Expected Keepassx::Group, got #{value.class}" unless value.is_a?(Keepassx::Group) - def title - @fields.detect { |field| field.name == 'title' }.data.chomp("\000") + self.group_id = value.id + @group = value end - def username - @fields.detect { |field| field.name == 'username' }.data.chomp("\000") - end - def group_id - @fields.detect { |field| field.name == 'groupid' }.data - end + private - def inspect - "Entry<title=#{title.inspect}, username=[FILTERED], password=[FILTERED], notes=#{notes.inspect}>" - end + + # rubocop:disable Metrics/MethodLength + def default_fields + @default_fields ||= { + id: SecureRandom.uuid.gsub('-', ''), + group_id: nil, + icon: 1, + name: nil, + url: nil, + username: nil, + password: nil, + notes: nil, + creation_time: Time.now, + last_mod_time: Time.now, + last_acc_time: Time.now, + expiration_time: Time.local(2999, 12, 28, 23, 59, 59), + binary_desc: nil, + binary_data: nil, + terminator: nil, + } + end + # rubocop:enable Metrics/MethodLength + + + # Keep this method private in order to avoid group/group_id divergence + def group_id=(value) + set :group_id, value + end + + + def exclusion_list + super.concat(%w[binary_desc binary_data]) + end + end end