X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=lib%2FFile%2FKDBX%2FEntry.pm;h=2afe50fd155c879ca7338836b95bbe0492ff4063;hb=8ccefe1cedea9b0886a44ad096aa5710528eaac7;hp=fc744fd8c3c00c3c9e47deadc9f8a7d71db05a17;hpb=37b09e0f2832514b33de4499a83f22d5ffe7c0a3;p=chaz%2Fp5-File-KDBX diff --git a/lib/File/KDBX/Entry.pm b/lib/File/KDBX/Entry.pm index fc744fd..2afe50f 100644 --- a/lib/File/KDBX/Entry.pm +++ b/lib/File/KDBX/Entry.pm @@ -9,10 +9,10 @@ use Devel::GlobalDestruction; use Encode qw(encode); use File::KDBX::Constants qw(:history :icon); use File::KDBX::Error; -use File::KDBX::Util qw(:class :coercion :function :uri generate_uuid load_optional); +use File::KDBX::Util qw(:class :coercion :erase :function :uri generate_uuid load_optional); use Hash::Util::FieldHash; use List::Util qw(first sum0); -use Ref::Util qw(is_coderef is_plain_hashref); +use Ref::Util qw(is_coderef is_hashref is_plain_hashref); use Scalar::Util qw(looks_like_number); use Storable qw(dclone); use Time::Piece; @@ -73,6 +73,22 @@ Auto-type details. ], } +=attr auto_type_enabled + +Whether or not the entry is eligible to be matched for auto-typing. + +=attr auto_type_data_transfer_obfuscation + +TODO + +=attr auto_type_default_sequence + +The default auto-type keystroke sequence. + +=attr auto_type_associations + +An array of window title / keystroke sequence associations. + =attr previous_parent_group 128-bit UUID identifying a group within the database. @@ -175,33 +191,37 @@ sub uuid { $self->{uuid}; } -my @ATTRS = qw(uuid custom_data history icon_id); -my %ATTRS = ( - # uuid => sub { generate_uuid(printable => 1) }, - # icon_id => sub { defined $_[1] ? icon($_[1]) : ICON_PASSWORD }, - custom_icon_uuid => [undef, coerce => \&to_uuid], - foreground_color => ['', coerce => \&to_string], - background_color => ['', coerce => \&to_string], - override_url => ['', coerce => \&to_string], - tags => ['', coerce => \&to_string], - auto_type => [{}], - previous_parent_group => [undef, coerce => \&to_uuid], - quality_check => [true, coerce => \&to_bool], - strings => [{}], - binaries => [{}], - times => [{}], - # custom_data => {}, - # history => [], -); -my %ATTRS_TIMES = ( - last_modification_time => [sub { gmtime }, coerce => \&to_time], - creation_time => [sub { gmtime }, coerce => \&to_time], - last_access_time => [sub { gmtime }, coerce => \&to_time], - expiry_time => [sub { gmtime }, coerce => \&to_time], - expires => [false, coerce => \&to_bool], - usage_count => [0, coerce => \&to_number], - location_changed => [sub { gmtime }, coerce => \&to_time], -); +# has uuid => sub { generate_uuid(printable => 1) }; +has icon_id => ICON_PASSWORD, coerce => \&to_icon_constant; +has custom_icon_uuid => undef, coerce => \&to_uuid; +has foreground_color => '', coerce => \&to_string; +has background_color => '', coerce => \&to_string; +has override_url => '', coerce => \&to_string; +has tags => '', coerce => \&to_string; +has auto_type => {}; +has previous_parent_group => undef, coerce => \&to_uuid; +has quality_check => true, coerce => \&to_bool; +has strings => {}; +has binaries => {}; +has times => {}; +# has custom_data => {}; +# has history => []; + +has last_modification_time => sub { gmtime }, store => 'times', coerce => \&to_time; +has creation_time => sub { gmtime }, store => 'times', coerce => \&to_time; +has last_access_time => sub { gmtime }, store => 'times', coerce => \&to_time; +has expiry_time => sub { gmtime }, store => 'times', coerce => \&to_time; +has expires => false, store => 'times', coerce => \&to_bool; +has usage_count => 0, store => 'times', coerce => \&to_number; +has location_changed => sub { gmtime }, store => 'times', coerce => \&to_time; + +# has 'auto_type.auto_type_enabled' => true, coerce => \&to_bool; +has 'auto_type_data_transfer_obfuscation' => 0, path => 'auto_type.data_transfer_obfuscation', + coerce => \&to_number; +has 'auto_type_default_sequence' => '{USERNAME}{TAB}{PASSWORD}{ENTER}', + path => 'auto_type.default_sequence', coerce => \&to_string; +has 'auto_type_associations' => [], path => 'auto_type.associations'; + my %ATTRS_STRINGS = ( title => 'Title', username => 'UserName', @@ -209,24 +229,16 @@ my %ATTRS_STRINGS = ( url => 'URL', notes => 'Notes', ); - -has icon_id => ICON_PASSWORD, coerce => sub { icon($_[0]) }; - -while (my ($attr, $default) = each %ATTRS) { - has $attr => @$default; -} -while (my ($attr, $default) = each %ATTRS_TIMES) { - has $attr => @$default, store => 'times'; -} while (my ($attr, $string_key) = each %ATTRS_STRINGS) { no strict 'refs'; ## no critic (ProhibitNoStrict) *{$attr} = sub { shift->string_value($string_key, @_) }; *{"expanded_${attr}"} = sub { shift->expanded_string_value($string_key, @_) }; } -sub _set_default_attributes { +my @ATTRS = qw(uuid custom_data history auto_type_enabled); +sub _set_nonlazy_attributes { my $self = shift; - $self->$_ for @ATTRS, keys %ATTRS, keys %ATTRS_TIMES, keys %ATTRS_STRINGS; + $self->$_ for @ATTRS, keys %ATTRS_STRINGS, list_attributes(ref $self); } sub init { @@ -314,9 +326,15 @@ sub _protect { =method string_value - $string = $entry->string_value; + $string = $entry->string_value($string_key); -Access a string value directly. Returns C if the string is not set. +Access a string value directly. The arguments are the same as for L. Returns C if the string +is not set or is currently memory-protected. This is just a shortcut for: + + my $string = do { + my $s = $entry->string(...); + defined $s ? $s->{value} : undef; + }; =cut @@ -335,8 +353,8 @@ do not expand to values are left as-is. See L. -Some placeholders (notably field references) require the entry be associated with a database and will throw an -error if there is no association. +Some placeholders (notably field references) require the entry be connected to a database and will throw an +error if it is not. =cut @@ -385,7 +403,8 @@ sub _expand_string { sub expanded_string_value { my $self = shift; - my $str = $self->string_value(@_) // return undef; + my $str = $self->string_peek(@_) // return undef; + my $cleanup = erase_scoped $str; return $self->_expand_string($str); } @@ -407,13 +426,42 @@ sub other_strings { return join($delim, @strings); } +=method string_peek + + $string = $entry->string_peek($string_key); + +Same as L but can also retrieve the value from protected-memory if the value is currently +protected. + +=cut + sub string_peek { my $self = shift; my $string = $self->string(@_); return defined $string->{value} ? $string->{value} : $self->kdbx->peek($string); } -sub password_peek { $_[0]->string_peek('Password') } +############################################################################## + +sub add_auto_type_association { + my $self = shift; + my $association = shift; + push @{$self->auto_type_associations}, $association; +} + +sub expand_keystroke_sequence { + my $self = shift; + my $association = shift; + + my $keys = is_hashref($association) && exists $association->{keystroke_sequence} ? + $association->{keystroke_sequence} : defined $association ? $association : ''; + + $keys = $self->auto_type_default_sequence if !$keys; + # TODO - Fall back to getting default sequence from parent group, which probably means we shouldn't be + # setting a default value in the entry.. + + return $self->_expand_string($keys); +} ############################################################################## @@ -449,9 +497,22 @@ sub binary_value { return $binary->{value}; } +sub searching_enabled { + my $self = shift; + my $parent = $self->group; + return $parent->effective_enable_searching if $parent; + return true; +} + sub auto_type_enabled { - my $entry = shift; - # TODO + my $self = shift; + $self->auto_type->{enabled} = to_bool(shift) if @_; + $self->auto_type->{enabled} //= true; + return false if !$self->auto_type->{enabled}; + return true if !$self->is_connected; + my $parent = $self->group; + return $parent->effective_enable_auto_type if $parent; + return true; } ############################################################################## @@ -755,7 +816,7 @@ sub history_size { $entry->prune_history(%options); Remove as many older historical entries as necessary to get under the database limits. The limits are taken -from the associated database (if any) or can be overridden with C<%options>: +from the connected database (if any) or can be overridden with C<%options>: =for :list * C - Maximum number of historical entries to keep (default: 10, no limit: -1) @@ -812,7 +873,7 @@ Get an entry's current entry. If the entry itself is current (not historical), i sub current_entry { my $self = shift; - my $group = $self->parent; + my $group = $self->group; if ($group) { my $id = $self->uuid;