1 package File
::KDBX
::Entry
;
2 # ABSTRACT: A KDBX database entry
7 use Crypt
::Misc
0.029 qw(decode_b64 encode_b32r);
8 use Devel
::GlobalDestruction
;
10 use File
::KDBX
::Constants
qw(:history :icon);
11 use File
::KDBX
::Error
;
12 use File
::KDBX
::Util
qw(:assert :class :coercion :erase :function :uri generate_uuid load_optional);
13 use Hash
::Util
::FieldHash
;
14 use List
::Util
qw(first sum0);
15 use Ref
::Util
qw(is_coderef is_hashref is_plain_hashref);
16 use Scalar
::Util
qw(blessed looks_like_number);
17 use Storable
qw(dclone);
22 extends
'File::KDBX::Object';
24 our $VERSION = '999.999'; # VERSION
26 my $PLACEHOLDER_MAX_DEPTH = 10;
28 my %STANDARD_STRINGS = map { $_ => 1 } qw(Title UserName Password URL Notes);
32 128-bit UUID identifying the entry within the database
.
36 Integer representing a
default icon
. See L
<File
::KDBX
::Constants
/":icon"> for valid
values.
38 =attr custom_icon_uuid
40 128-bit UUID identifying a custom icon within the database
.
42 =attr foreground_color
44 Text color represented as a string of the form C
<#000000>.
46 =attr background_color
48 Background color represented as a string of the form C
<#FFFFFF>.
56 Text string with arbitrary tags which can be used to build a taxonomy
.
58 =attr auto_type_enabled
60 Whether
or not the entry
is eligible to be matched
for auto-typing
.
62 =attr auto_type_obfuscation
64 Whether
or not to
use some kind of obfuscation
when sending keystroke sequences to applications
.
66 =attr auto_type_default_sequence
68 The
default auto-type keystroke sequence
.
70 =attr auto_type_associations
72 An array of window title
/ keystroke sequence associations
.
75 window
=> 'Example Window Title',
76 keystroke_sequence
=> '{USERNAME}{TAB}{PASSWORD}{ENTER}',
79 Keystroke sequences can have
</Placeholders
>, most commonly C
<{USERNAME
}> and C
<{PASSWORD
}>.
81 =attr previous_parent_group
83 128-bit UUID identifying a group within the database
.
87 Boolean indicating whether the entry password should be tested
for weakness
and show up
in reports
.
91 Hash with entry strings
, including the standard strings as well as any custom ones
.
94 # Every entry has these five strings:
95 Title
=> { value
=> 'Example Entry' },
96 UserName
=> { value
=> 'jdoe' },
97 Password
=> { value
=> 's3cr3t', protect
=> true
},
98 URL
=> { value
=> 'https://example.com' }
99 Notes
=> { value
=> '' },
100 # May also have custom strings:
101 MySystem
=> { value
=> 'The mainframe' },
104 There are methods available to provide more convenient access to strings
, including L
</string
>,
105 L
</string_value>, L</expand_string_value
> and L
</string_peek
>.
109 Files
or attachments
. Binaries are similar to strings except they have a value of bytes instead of test
122 There are methods available to provide more convenient access to binaries
, including L
</binary
> and
127 A set of key-value pairs used to store arbitrary data
, usually used by software to keep track of
state rather
128 than by end users
(who typically work with the strings
and binaries
).
132 Array of historical entries
. Historical entries are prior versions of the same entry so they all share the
133 same UUID with the current entry
.
135 =attr last_modification_time
137 Date
and time when the entry was
last modified
.
141 Date
and time when the entry was created
.
143 =attr last_access_time
145 Date
and time when the entry was
last accessed
.
149 Date
and time when the entry expired
or will expire
.
153 Boolean value indicating whether
or not an entry
is expired
.
157 The number of
times an entry
has been used
, which typically means how many
times the B
<Password
> string
has
160 =attr location_changed
162 Date
and time when the entry was
last moved to a different parent group
.
166 Alias
for the B
<Notes
> string value
.
170 Alias
for the B
<Password
> string value
.
174 Alias
for the B
<Title
> string value
.
178 Alias
for the B
<URL
> string value
.
182 Aliases
for the B
<UserName
> string value
.
188 if (@_ || !defined $self->{uuid
}) {
189 my %args = @_ % 2 == 1 ? (uuid
=> shift, @_) : @_;
190 my $old_uuid = $self->{uuid
};
191 my $uuid = $self->{uuid
} = delete $args{uuid
} // generate_uuid
;
192 for my $entry (@{$self->history}) {
193 $entry->{uuid
} = $uuid;
195 $self->_signal('uuid.changed', $uuid, $old_uuid) if defined $old_uuid && $self->is_current;
200 # has uuid => sub { generate_uuid(printable => 1) };
201 has icon_id
=> ICON_PASSWORD
, coerce
=> \
&to_icon_constant
;
202 has custom_icon_uuid
=> undef, coerce
=> \
&to_uuid
;
203 has foreground_color
=> '', coerce
=> \
&to_string
;
204 has background_color
=> '', coerce
=> \
&to_string
;
205 has override_url
=> '', coerce
=> \
&to_string
;
206 has tags
=> '', coerce
=> \
&to_string
;
208 has previous_parent_group
=> undef, coerce
=> \
&to_uuid
;
209 has quality_check
=> true
, coerce
=> \
&to_bool
;
213 # has custom_data => {};
216 has last_modification_time
=> sub { gmtime }, store
=> 'times', coerce
=> \
&to_time
;
217 has creation_time
=> sub { gmtime }, store
=> 'times', coerce
=> \
&to_time
;
218 has last_access_time
=> sub { gmtime }, store
=> 'times', coerce
=> \
&to_time
;
219 has expiry_time
=> sub { gmtime }, store
=> 'times', coerce
=> \
&to_time
;
220 has expires
=> false
, store
=> 'times', coerce
=> \
&to_bool
;
221 has usage_count
=> 0, store
=> 'times', coerce
=> \
&to_number
;
222 has location_changed
=> sub { gmtime }, store
=> 'times', coerce
=> \
&to_time
;
224 # has 'auto_type.auto_type_enabled' => true, coerce => \&to_bool;
225 has 'auto_type_obfuscation' => 0, path
=> 'auto_type.data_transfer_obfuscation',
226 coerce
=> \
&to_number
;
227 has 'auto_type_default_sequence' => '{USERNAME}{TAB}{PASSWORD}{ENTER}',
228 path
=> 'auto_type.default_sequence', coerce
=> \
&to_string
;
229 has 'auto_type_associations' => [], path
=> 'auto_type.associations';
231 my %ATTRS_STRINGS = (
233 username
=> 'UserName',
234 password
=> 'Password',
238 while (my ($attr, $string_key) = each %ATTRS_STRINGS) {
239 no strict
'refs'; ## no critic (ProhibitNoStrict)
240 *{$attr} = sub { shift-
>string_value($string_key, @_) };
241 *{"expand_${attr}"} = sub { shift-
>expand_string_value($string_key, @_) };
244 my @ATTRS = qw(uuid custom_data history auto_type_enabled);
245 sub _set_nonlazy_attributes
{
247 $self->$_ for @ATTRS, keys %ATTRS_STRINGS, list_attributes
(ref $self);
254 while (my ($key, $val) = each %args) {
255 if (my $method = $self->can($key)) {
256 $self->$method($val);
259 $self->string($key => $val);
266 ##############################################################################
270 \
%string = $entry->string($string_key);
272 $entry->string($string_key, \
%string);
273 $entry->string($string_key, %attributes);
274 $entry->string($string_key, $value); # same as: value => $value
276 Get
or set a string
. Every string
has a unique
(to the entry
) key
and flags
and so are returned as a hash
277 structure
. For example
:
281 protect
=> true
, # optional
284 Every string should have a value
(but might be C
<undef> due to memory protection
) and these optional flags
288 * C<protect> - Whether or not the string value should be memory-protected.
294 my %args = @_ == 2 ? (key
=> shift, value
=> shift)
295 : @_ % 2 == 1 ? (key
=> shift, @_) : @_;
297 if (!defined $args{key
} && !defined $args{value
}) {
298 my %standard = (value
=> 1, protect
=> 1);
299 my @other_keys = grep { !$standard{$_} } keys %args;
300 if (@other_keys == 1) {
301 my $key = $args{key
} = $other_keys[0];
302 $args{value
} = delete $args{$key};
306 my $key = delete $args{key
} or throw
'Must provide a string key to access';
308 return $self->{strings
}{$key} = $args{value
} if is_plain_hashref
($args{value
});
310 while (my ($field, $value) = each %args) {
311 $self->{strings
}{$key}{$field} = $value;
314 # Auto-vivify the standard strings.
315 if ($STANDARD_STRINGS{$key}) {
316 return $self->{strings
}{$key} //= {value
=> '', $self->_protect($key) ? (protect
=> true
) : ()};
318 return $self->{strings
}{$key};
321 ### Get whether or not a standard string is configured to be protected
325 return false
if !$STANDARD_STRINGS{$key};
326 if (my $kdbx = eval { $self->kdbx }) {
327 my $protect = $kdbx->memory_protection($key);
328 return $protect if defined $protect;
330 return $key eq 'Password';
335 $string = $entry->string_value($string_key);
337 Access a string value directly
. The arguments are the same as
for L
</string
>. Returns C
<undef> if the string
338 is not set
or is currently memory-protected
. This
is just a shortcut
for:
341 my $s = $entry->string(...);
342 defined $s ? $s->{value
} : undef;
349 my $string = $self->string(@_) // return undef;
350 return $string->{value
};
353 =method expand_string_value
355 $string = $entry->expand_string_value;
357 Same as L
</string_value
> but will substitute placeholders
and resolve field references
. Any placeholders that
358 do not expand to
values are left as-is
.
360 See L
</Placeholders
>.
362 Some placeholders
(notably field references
) require the entry be connected to a database
and will throw an
367 sub _expand_placeholder
{
369 my $placeholder = shift;
374 my $placeholder_key = $placeholder;
376 $placeholder_key = $File::KDBX
::PLACEHOLDERS
{"${placeholder}:${arg}"} ? "${placeholder}:${arg}"
379 return if !defined $File::KDBX
::PLACEHOLDERS
{$placeholder_key};
381 my $local_key = join('/', Hash
::Util
::FieldHash
::id
($self), $placeholder_key);
382 local $PLACEHOLDERS{$local_key} = my $handler = $PLACEHOLDERS{$local_key} // do {
383 my $handler = $File::KDBX
::PLACEHOLDERS
{$placeholder_key} or next;
384 memoize recurse_limit
($handler, $PLACEHOLDER_MAX_DEPTH, sub {
385 alert
"Detected deep recursion while expanding $placeholder placeholder",
386 placeholder
=> $placeholder;
391 return $handler->($self, $arg, $placeholder);
398 my $expand = memoize
$self->can('_expand_placeholder'), $self;
400 # placeholders (including field references):
401 $str =~ s!\{([^:\}]+)(?::([^\}]*))?\}!$expand->(uc($1), $2, @_) // $&!egi;
403 # environment variables (alt syntax):
404 my $vars = join('|', map { quotemeta($_) } keys %ENV);
405 $str =~ s!\%($vars)\%!$expand->(ENV => $1, @_) // $&!eg;
410 sub expand_string_value
{
412 my $str = $self->string_peek(@_) // return undef;
413 my $cleanup = erase_scoped
$str;
414 return $self->_expand_string($str);
419 Shortcut equivalent to C
<< ->expand_string_value('Notes') >>.
421 =attr expand_password
423 Shortcut equivalent to C
<< ->expand_string_value('Password') >>.
427 Shortcut equivalent to C
<< ->expand_string_value('Title') >>.
431 Shortcut equivalent to C
<< ->expand_string_value('URL') >>.
433 =attr expand_username
435 Shortcut equivalent to C
<< ->expand_string_value('UserName') >>.
437 =method other_strings
439 $other = $entry->other_strings;
440 $other = $entry->other_strings($delimiter);
442 Get a concatenation of all non-standard string
values. The
default delimiter
is a newline
. This
is is useful
443 for executing queries to search
for entities based on the contents of these other strings
(if any
).
449 my $delim = shift // "\n";
451 my @strings = map { $self->string_value($_) } grep { !$STANDARD_STRINGS{$_} } sort keys %{$self->strings};
452 return join($delim, @strings);
457 $string = $entry->string_peek($string_key);
459 Same as L
</string_value
> but can also retrieve the value from protected-memory
if the value
is currently
466 my $string = $self->string(@_);
467 return defined $string->{value
} ? $string->{value
} : $self->kdbx->peek($string);
470 ##############################################################################
472 =method add_auto_type_association
474 $entry->add_auto_type_association(\
%association);
476 Add a new auto-type association to an entry
.
480 sub add_auto_type_association
{
482 my $association = shift;
483 push @{$self->auto_type_associations}, $association;
486 =method expand_keystroke_sequence
488 $string = $entry->expand_keystroke_sequence($keystroke_sequence);
489 $string = $entry->expand_keystroke_sequence(\
%association);
490 $string = $entry->expand_keystroke_sequence; # use default auto-type sequence
492 Get a keystroke sequence after placeholder expansion
.
496 sub expand_keystroke_sequence
{
498 my $association = shift;
502 $keys = is_hashref
($association) && exists $association->{keystroke_sequence
} ?
503 $association->{keystroke_sequence
} : defined $association ? $association : '';
506 $keys = $self->auto_type_default_sequence if !$keys;
507 # TODO - Fall back to getting default sequence from parent group, which probably means we shouldn't be
508 # setting a default value in the entry..
510 return $self->_expand_string($keys);
513 ##############################################################################
517 \
%binary = $entry->binary($binary_key);
519 $entry->binary($binary_key, \
%binary);
520 $entry->binary($binary_key, %attributes);
521 $entry->binary($binary_key, $value); # same as: value => $value
523 Get
or set a binary
. Every binary
has a unique
(to the entry
) key
and flags
and so are returned as a hash
524 structure
. For example
:
528 protect
=> true
, # optional
531 Every binary should have a value
(but might be C
<undef> due to memory protection
) and these optional flags
535 * C<protect> - Whether or not the binary value should be memory-protected.
541 my %args = @_ == 2 ? (key
=> shift, value
=> shift)
542 : @_ % 2 == 1 ? (key
=> shift, @_) : @_;
544 if (!defined $args{key
} && !defined $args{value
}) {
545 my %standard = (value
=> 1, protect
=> 1);
546 my @other_keys = grep { !$standard{$_} } keys %args;
547 if (@other_keys == 1) {
548 my $key = $args{key
} = $other_keys[0];
549 $args{value
} = delete $args{$key};
553 my $key = delete $args{key
} or throw
'Must provide a binary key to access';
555 return $self->{binaries
}{$key} = $args{value
} if is_plain_hashref
($args{value
});
557 assert
{ !defined $args{value
} || !utf8
::is_utf8
($args{value
}) };
558 while (my ($field, $value) = each %args) {
559 $self->{binaries
}{$key}{$field} = $value;
561 return $self->{binaries
}{$key};
566 $binary = $entry->binary_value($binary_key);
568 Access a binary value directly
. The arguments are the same as
for L
</binary
>. Returns C
<undef> if the binary
569 is not set
or is currently memory-protected
. This
is just a shortcut
for:
572 my $b = $entry->binary(...);
573 defined $b ? $b->{value
} : undef;
580 my $binary = $self->binary(@_) // return undef;
581 return $binary->{value
};
584 ##############################################################################
588 $otp = $entry->hmac_otp(%options);
590 Generate an HMAC-based one-time password
, or C
<undef> if HOTP
is not configured
for the entry
. The entry
's
591 strings generally must first be unprotected, just like when accessing the password. Valid options are:
594 * C<counter> - Specify the counter value
596 To configure HOTP, see L</"One-time Passwords">.
602 load_optional('Pass
::OTP
');
604 my %params = ($self->_hotp_params, @_);
605 return if !defined $params{type} || !defined $params{secret};
607 $params{secret} = encode_b32r($params{secret}) if !$params{base32};
610 my $otp = eval {Pass::OTP::otp(%params, @_) };
612 throw 'Unable to generate HOTP
', error => $err;
615 $self->_hotp_increment_counter($params{counter});
622 $otp = $entry->time_otp(%options);
624 Generate a time-based one-time password, or C<undef> if TOTP is not configured for the entry. The entry's
625 strings generally must first be unprotected
, just like
when accessing the password
. Valid options are
:
628 * C<now> - Specify the value for determining the time-step counter
630 To configure TOTP, see L</"One-time Passwords">.
636 load_optional
('Pass::OTP');
638 my %params = ($self->_totp_params, @_);
639 return if !defined $params{type
} || !defined $params{secret
};
641 $params{secret
} = encode_b32r
($params{secret
}) if !$params{base32
};
644 my $otp = eval {Pass
::OTP
::otp
(%params, @_) };
646 throw
'Unable to generate TOTP', error
=> $err;
656 $uri_string = $entry->hmac_otp_uri;
657 $uri_string = $entry->time_otp_uri;
659 Get a HOTP
or TOTP otpauth URI
for the entry
, if available
.
661 To configure OTP
, see L
</"One-time Passwords">.
665 sub hmac_otp_uri
{ $_[0]->_otp_uri($_[0]->_hotp_params) }
666 sub time_otp_uri
{ $_[0]->_otp_uri($_[0]->_totp_params) }
672 return if 4 != grep { defined } @params{qw(type secret issuer account)};
673 return if $params{type
} !~ /^[ht]otp$/i;
675 my $label = delete $params{label
};
676 $params{$_} = uri_escape_utf8
($params{$_}) for keys %params;
678 my $type = lc($params{type
});
679 my $issuer = $params{issuer
};
680 my $account = $params{account
};
682 $label //= "$issuer:$account";
684 my $secret = $params{secret
};
685 $secret = uc(encode_b32r
($secret)) if !$params{base32
};
687 delete $params{algorithm
} if defined $params{algorithm
} && $params{algorithm
} eq 'sha1';
688 delete $params{period
} if defined $params{period
} && $params{period
} == 30;
689 delete $params{digits
} if defined $params{digits
} && $params{digits
} == 6;
690 delete $params{counter
} if defined $params{counter
} && $params{counter
} == 0;
692 my $uri = "otpauth://$type/$label?secret=$secret&issuer=$issuer";
694 if (defined $params{encoder
}) {
695 $uri .= "&encoder=$params{encoder}";
698 $uri .= '&algorithm=' . uc($params{algorithm
}) if defined $params{algorithm
};
699 $uri .= "&digits=$params{digits}" if defined $params{digits
};
700 $uri .= "&counter=$params{counter}" if defined $params{counter
};
701 $uri .= "&period=$params{period}" if defined $params{period
};
711 issuer
=> $self->title || 'KDBX',
712 account
=> $self->username || 'none',
714 counter
=> $self->string_value('HmacOtp-Counter') // 0,
715 $self->_otp_secret_params('Hmac'),
717 return %params if $params{secret
};
719 my %otp_params = $self->_otp_params;
720 return () if !$otp_params{secret
} || $otp_params{type
} ne 'hotp';
722 # $otp_params{counter} = 0
724 return (%params, %otp_params);
731 'HMAC-SHA-1' => 'sha1',
732 'HMAC-SHA-256' => 'sha256',
733 'HMAC-SHA-512' => 'sha512',
737 issuer
=> $self->title || 'KDBX',
738 account
=> $self->username || 'none',
739 digits
=> $self->string_value('TimeOtp-Length') // 6,
740 algorithm
=> $algorithms{$self->string_value('TimeOtp-Algorithm') || ''} || 'sha1',
741 period
=> $self->string_value('TimeOtp-Period') // 30,
742 $self->_otp_secret_params('Time'),
744 return %params if $params{secret
};
746 my %otp_params = $self->_otp_params;
747 return () if !$otp_params{secret
} || $otp_params{type
} ne 'totp';
749 return (%params, %otp_params);
755 load_optional
('Pass::OTP::URI');
757 my $uri = $self->string_value('otp') || '';
759 %params = Pass
::OTP
::URI
::parse
($uri) if $uri =~ m!^otpauth://!;
760 return () if !$params{secret
} || !$params{type
};
762 if (($params{encoder
} // '') eq 'steam') {
764 $params{chars
} = '23456789BCDFGHJKMNPQRTVWXY';
767 # Pass::OTP::URI doesn't provide the issuer and account separately, so get them from the label
768 my ($issuer, $user) = split(':', $params{label
} // ':', 2);
769 $params{issuer
} //= uri_unescape_utf8
($issuer);
770 $params{account
} //= uri_unescape_utf8
($user);
772 $params{algorithm
} = lc($params{algorithm
}) if $params{algorithm
};
773 $params{counter
} = $self->string_value('HmacOtp-Counter') if $params{type
} eq 'hotp';
778 sub _otp_secret_params
{
780 my $type = shift // return ();
782 my $secret_txt = $self->string_value("${type}Otp-Secret");
783 my $secret_hex = $self->string_value("${type}Otp-Secret-Hex");
784 my $secret_b32 = $self->string_value("${type}Otp-Secret-Base32");
785 my $secret_b64 = $self->string_value("${type}Otp-Secret-Base64");
787 my $count = grep { defined } ($secret_txt, $secret_hex, $secret_b32, $secret_b64);
788 return () if $count == 0;
789 alert
"Found multiple ${type}Otp-Secret strings", count
=> $count if 1 < $count;
791 return (secret
=> $secret_b32, base32
=> 1) if defined $secret_b32;
792 return (secret
=> decode_b64
($secret_b64)) if defined $secret_b64;
793 return (secret
=> pack('H*', $secret_hex)) if defined $secret_hex;
794 return (secret
=> encode
('UTF-8', $secret_txt));
797 sub _hotp_increment_counter
{
799 my $counter = shift // $self->string_value('HmacOtp-Counter') || 0;
801 looks_like_number
($counter) or throw
'HmacOtp-Counter value must be a number', value
=> $counter;
802 my $next = $counter + 1;
803 $self->string('HmacOtp-Counter', $next);
807 ##############################################################################
811 $size = $entry->size;
813 Get the size
(in bytes
) of an entry
.
815 B
<NOTE
:> This
is not an exact figure because there
is no canonical serialization of an entry
. This size should
816 only be used as a rough estimate
for comparison with other entries
or to impose data size limitations
.
826 $size += length(encode
('UTF-8', $self->tags // ''));
828 # attributes (strings)
829 while (my ($key, $string) = each %{$self->strings}) {
830 next if !defined $string->{value
};
831 $size += length(encode
('UTF-8', $key)) + length(encode
('UTF-8', $string->{value
} // ''));
835 while (my ($key, $item) = each %{$self->custom_data}) {
836 next if !defined $item->{value
};
837 $size += length(encode
('UTF-8', $key)) + length(encode
('UTF-8', $item->{value
} // ''));
841 while (my ($key, $binary) = each %{$self->binaries}) {
842 next if !defined $binary->{value
};
843 my $value_len = utf8
::is_utf8
($binary->{value
}) ? length(encode
('UTF-8', $binary->{value
}))
844 : length($binary->{value
});
845 $size += length(encode
('UTF-8', $key)) + $value_len;
848 # autotype associations
849 for my $association (@{$self->auto_type->{associations
} || []}) {
850 $size += length(encode
('UTF-8', $association->{window
}))
851 + length(encode
('UTF-8', $association->{keystroke_sequence
} // ''));
857 ##############################################################################
861 my $entries = $self->{history
} //= [];
862 if (@$entries && !blessed
($entries->[0])) {
863 @$entries = map { $self->_wrap_entry($_, $self->kdbx) } @$entries;
865 assert
{ !any
{ !blessed
$_ } @$entries };
871 $size = $entry->history_size;
873 Get the size
(in bytes
) of all historical entries combined
.
879 return sum0
map { $_->size } @{$self->history};
882 =method prune_history
884 @removed_historical_entries = $entry->prune_history(%options);
886 Remove just as many older historical entries as necessary to get under the database limits
. The limits are
887 taken from the connected database
(if any
) or can be overridden with C
<%options>:
890 * C<max_items> - Maximum number of historical entries to keep (default: 10, no limit: -1)
891 * C<max_size> - Maximum total size (in bytes) of historical entries to keep (default: 6 MiB, no limit: -1)
892 * C<max_age> - Maximum age (in days) of historical entries to keep (default: 365, no limit: -1)
900 my $max_items = $args{max_items
} // eval { $self->kdbx->history_max_items } // HISTORY_DEFAULT_MAX_ITEMS
;
901 my $max_size = $args{max_size
} // eval { $self->kdbx->history_max_size } // HISTORY_DEFAULT_MAX_SIZE
;
902 my $max_age = $args{max_age
} // HISTORY_DEFAULT_MAX_AGE
;
904 # history is ordered oldest to newest
905 my $history = $self->history;
909 if (0 <= $max_items && $max_items < @$history) {
910 push @removed, splice @$history, -$max_items;
913 if (0 <= $max_size) {
914 my $current_size = $self->history_size;
915 while ($max_size < $current_size) {
916 push @removed, my $entry = shift @$history;
917 $current_size -= $entry->size;
922 my $cutoff = gmtime - ($max_age * 86400);
923 for (my $i = @$history - 1; 0 <= $i; --$i) {
924 my $entry = $history->[$i];
925 next if $cutoff <= $entry->last_modification_time;
926 push @removed, splice @$history, $i, 1;
930 @removed = sort { $a->last_modification_time <=> $b->last_modification_time } @removed;
934 =method add_historical_entry
936 $entry->add_historical_entry($entry);
938 Add an entry to the history
.
942 sub add_historical_entry
{
944 delete $_->{history
} for @_;
945 push @{$self->{history
} //= []}, map { $self->_wrap_entry($_) } @_;
948 =method remove_historical_entry
950 $entry->remove_historical_entry($historical_entry);
952 Remove an entry from the history
.
956 sub remove_historical_entry
{
959 my $history = $self->history;
962 for (my $i = @$history - 1; 0 <= $i; --$i) {
963 my $item = $history->[$i];
964 next if Hash
::Util
::FieldHash
::id
($entry) != Hash
::Util
::FieldHash
::id
($item);
965 push @removed, splice @{$self->{history
}}, $i, 1;
970 =method current_entry
972 $current_entry = $entry->current_entry;
974 Get an entry
's current entry. If the entry itself is current (not historical), itself is returned.
980 my $parent = $self->group;
983 my $id = $self->uuid;
984 my $entry = first { $id eq $_->uuid } @{$parent->entries};
985 return $entry if $entry;
993 $bool = $entry->is_current;
995 Get whether or not an entry is considered current (i.e. not historical). An entry is current if it is directly
996 in the parent group's entry list
.
1002 my $current = $self->current_entry;
1003 return Hash
::Util
::FieldHash
::id
($self) == Hash
::Util
::FieldHash
::id
($current);
1006 =method is_historical
1008 $bool = $entry->is_historical;
1010 Get whether
or not an entry
is considered historical
(i
.e
. not current
).
1012 This
is just the inverse of L
</is_current
>.
1016 sub is_historical
{ !$_[0]->is_current }
1020 $entry = $entry->remove;
1022 Remove an entry from its parent group
. If the entry
is historical
, remove it from the history of the current
1023 entry
. If the entry
is current
, this behaves the same as L
<File
::KDBX
::Object
/remove
>.
1029 my $current = $self->current_entry;
1030 return $self if $current->remove_historical_entry($self);
1031 $self->SUPER::remove
(@_);
1034 ##############################################################################
1036 =method searching_enabled
1038 $bool = $entry->searching_enabled;
1040 Get whether
or not an entry may show up
in search results
. This
is determine from the entry
's parent group's
1041 L
<File
::KDBX
::Group
/effective_enable_searching
> value
.
1043 Throws
if entry
has no parent group
or if the entry
is not connected to a database
.
1047 sub searching_enabled
{
1049 my $parent = $self->group;
1050 return $parent->effective_enable_searching if $parent;
1054 sub auto_type_enabled
{
1056 $self->auto_type->{enabled
} = to_bool
(shift) if @_;
1057 $self->auto_type->{enabled
} //= true
;
1058 return false
if !$self->auto_type->{enabled
};
1059 return true
if !$self->is_connected;
1060 my $parent = $self->group;
1061 return $parent->effective_enable_auto_type if $parent;
1065 ##############################################################################
1070 return $self->SUPER::_signal
("entry.$type", @_);
1076 $self->add_historical_entry($orig);
1078 $self->last_modification_time($time);
1079 $self->last_access_time($time);
1082 sub label
{ shift-
>expand_title(@_) }
1084 ### Name of the parent attribute expected to contain the object
1085 sub _parent_container
{ 'entries' }
1090 =for Pod::Coverage auto_type times
1094 An entry in a KDBX database is a record that can contains strings (also called "fields") and binaries (also
1095 called "files" or "attachments"). Every string and binary has a key or name. There is a default set of strings
1096 that every entry has:
1105 Beyond this, you can store any number of other strings and any number of binaries that you can use for
1106 whatever purpose you want.
1108 There is also some metadata associated with an entry. Each entry in a database is identified uniquely by
1109 a UUID. An entry can also have an icon associated with it, and there are various timestamps. Take a look at
1110 the attributes to see what's available.
1112 A B<File::KDBX::Entry> is a subclass of L<File::KDBX::Object>.
1116 Entry string and auto-type key sequences can have placeholders or template tags that can be replaced by other
1117 values. Placeholders can appear like C<{PLACEHOLDER}>. For example, a B<URL> string might have a value of
1118 C<http://example.com?user={USERNAME}>. C<{USERNAME}> is a placeholder for the value of the B<UserName> string
1119 of the same entry. If the B<UserName> string had a value of "batman", the B<URL> string would expand to
1120 C<http://example.com?user=batman>.
1122 Some placeholders take an argument, where the argument follows the tag after a colon but before the closing
1123 brace, like C<{PLACEHOLDER:ARGUMENT}>.
1125 Placeholders are documented in the L<KeePass Help Center|https://keepass.info/help/base/placeholders.html>.
1126 This software supports many (but not all) of the placeholders documented there.
1128 =head3 Entry Placeholders
1131 * ☑ C<{TITLE}> - B<Title> string
1132 * ☑ C<{USERNAME}> - B<UserName> string
1133 * ☑ C<{PASSWORD}> - B<Password> string
1134 * ☑ C<{NOTES}> - B<Notes> string
1135 * ☑ C<{URL}> - B<URL> string
1136 * ☑ C<{URL:SCM}> / C<{URL:SCHEME}>
1137 * ☑ C<{URL:USERINFO}>
1138 * ☑ C<{URL:USERNAME}>
1139 * ☑ C<{URL:PASSWORD}>
1144 * ☑ C<{URL:FRAGMENT}> / C<{URL:HASH}>
1145 * ☑ C<{URL:RMVSCM}> / C<{URL:WITHOUTSCHEME}>
1146 * ☑ C<{S:Name}> - Custom string where C<Name> is the name or key of the string
1147 * ☑ C<{UUID}> - Identifier (32 hexidecimal characters)
1148 * ☑ C<{HMACOTP}> - Generate an HMAC-based one-time password (its counter B<will> be incremented)
1149 * ☑ C<{TIMEOTP}> - Generate a time-based one-time password
1150 * ☑ C<{GROUP_NOTES}> - Notes of the parent group
1151 * ☑ C<{GROUP_PATH}> - Full path of the parent group
1152 * ☑ C<{GROUP}> - Name of the parent group
1154 =head3 Field References
1157 * ☑ C<{REF:Wanted@SearchIn:Text}> - See L<File::KDBX/resolve_reference>
1159 =head3 File path Placeholders
1162 * ☑ C<{APPDIR}> - Program directory path
1163 * ☑ C<{FIREFOX}> - Path to the Firefox browser executable
1164 * ☑ C<{GOOGLECHROME}> - Path to the Chrome browser executable
1165 * ☑ C<{INTERNETEXPLORER}> - Path to the Firefox browser executable
1166 * ☑ C<{OPERA}> - Path to the Opera browser executable
1167 * ☑ C<{SAFARI}> - Path to the Safari browser executable
1168 * ☒ C<{DB_PATH}> - Full file path of the database
1169 * ☒ C<{DB_DIR}> - Directory path of the database
1170 * ☒ C<{DB_NAME}> - File name (including extension) of the database
1171 * ☒ C<{DB_BASENAME}> - File name (excluding extension) of the database
1172 * ☒ C<{DB_EXT}> - File name extension
1173 * ☑ C<{ENV_DIRSEP}> - Directory separator
1174 * ☑ C<{ENV_PROGRAMFILES_X86}> - One of C<%ProgramFiles(x86)%> or C<%ProgramFiles%>
1176 =head3 Date and Time Placeholders
1179 * ☑ C<{DT_SIMPLE}> - Current local date and time as a sortable string
1180 * ☑ C<{DT_YEAR}> - Year component of the current local date
1181 * ☑ C<{DT_MONTH}> - Month component of the current local date
1182 * ☑ C<{DT_DAY}> - Day component of the current local date
1183 * ☑ C<{DT_HOUR}> - Hour component of the current local time
1184 * ☑ C<{DT_MINUTE}> - Minute component of the current local time
1185 * ☑ C<{DT_SECOND}> - Second component of the current local time
1186 * ☑ C<{DT_UTC_SIMPLE}> - Current UTC date and time as a sortable string
1187 * ☑ C<{DT_UTC_YEAR}> - Year component of the current UTC date
1188 * ☑ C<{DT_UTC_MONTH}> - Month component of the current UTC date
1189 * ☑ C<{DT_UTC_DAY}> - Day component of the current UTC date
1190 * ☑ C<{DT_UTC_HOUR}> - Hour component of the current UTC time
1191 * ☑ C<{DT_UTC_MINUTE}> Minute Year component of the current UTC time
1192 * ☑ C<{DT_UTC_SECOND}> - Second component of the current UTC time
1194 If the current date and time is <2012-07-25 17:05:34>, the "simple" form would be C<20120725170534>.
1196 =head3 Special Key Placeholders
1198 Certain placeholders for use in auto-type key sequences are not supported for replacement, but they will
1199 remain as-is so that an auto-type engine (not included) can parse and replace them with the appropriate
1200 virtual key presses. For completeness, here is the list that the KeePass program claims to support:
1202 C<{TAB}>, C<{ENTER}>, C<{UP}>, C<{DOWN}>, C<{LEFT}>, C<{RIGHT}>, C<{HOME}>, C<{END}>, C<{PGUP}>, C<{PGDN}>,
1203 C<{INSERT}>, C<{DELETE}>, C<{SPACE}>
1205 C<{BACKSPACE}>, C<{BREAK}>, C<{CAPSLOCK}>, C<{ESC}>, C<{WIN}>, C<{LWIN}>, C<{RWIN}>, C<{APPS}>, C<{HELP}>,
1206 C<{NUMLOCK}>, C<{PRTSC}>, C<{SCROLLLOCK}>
1208 C<{F1}>, C<{F2}>, C<{F3}>, C<{F4}>, C<{F5}>, C<{F6}>, C<{F7}>, C<{F8}>, C<{F9}>, C<{F10}>, C<{F11}>, C<{F12}>,
1209 C<{F13}>, C<{F14}>, C<{F15}>, C<{F16}>
1211 C<{ADD}>, C<{SUBTRACT}>, C<{MULTIPLY}>, C<{DIVIDE}>, C<{NUMPAD0}>, C<{NUMPAD1}>, C<{NUMPAD2}>, C<{NUMPAD3}>,
1212 C<{NUMPAD4}>, C<{NUMPAD5}>, C<{NUMPAD6}>, C<{NUMPAD7}>, C<{NUMPAD8}>, C<{NUMPAD9}>
1214 =head3 Miscellaneous Placeholders
1218 * ☒ C<{BASE:SCM}> / C<{BASE:SCHEME}>
1219 * ☒ C<{BASE:USERINFO}>
1220 * ☒ C<{BASE:USERNAME}>
1221 * ☒ C<{BASE:PASSWORD}>
1226 * ☒ C<{BASE:FRAGMENT}> / C<{BASE:HASH}>
1227 * ☒ C<{BASE:RMVSCM}> / C<{BASE:WITHOUTSCHEME}>
1228 * ☒ C<{CLIPBOARD-SET:/Text/}>
1230 * ☒ C<{CMD:/CommandLine/Options/}>
1231 * ☑ C<{C:Comment}> - Comments are simply replaced by nothing
1232 * ☑ C<{ENV:}> and C<%ENV%> - Environment variables
1233 * ☒ C<{GROUP_SEL_NOTES}>
1234 * ☒ C<{GROUP_SEL_PATH}>
1236 * ☒ C<{NEWPASSWORD}>
1237 * ☒ C<{NEWPASSWORD:/Profile/}>
1238 * ☒ C<{PASSWORD_ENC}>
1240 * ☒ C<{PICKCHARS:Field:Options}>
1242 * ☒ C<{T-CONV:/Text/Type/}>
1243 * ☒ C<{T-REPLACE-RX:/Text/Type/Replace/}>
1245 Some of these that remain unimplemented, such as C<{CLIPBOARD}>, cannot be implemented portably. Some of these
1246 I haven't implemented (yet) just because they don't seem very useful. You can create your own placeholder to
1247 augment the list of default supported placeholders or to replace a built-in placeholder handler. To create
1248 a placeholder, just set it in the C<%File::KDBX::PLACEHOLDERS> hash. For example:
1250 $File::KDBX::PLACEHOLDERS{'MY_PLACEHOLDER'} = sub {
1255 If the placeholder is expanded in the context of an entry, C<$entry> is the B<File::KDBX::Entry> object in
1256 context. Otherwise it is C<undef>. An entry is in context if, for example, the placeholder is in an entry's
1257 strings or auto-complete key sequences.
1259 $File::KDBX::PLACEHOLDERS{'MY_PLACEHOLDER:'} = sub {
1260 my ($entry, $arg) = @_; # ^ Notice the colon here
1264 If the name of the placeholder ends in a colon, then it is expected to receive an argument. During expansion,
1265 everything after the colon and before the end of the placeholder is passed to your placeholder handler
1266 subroutine. So if the placeholder is C<{MY_PLACEHOLDER:whatever}>, C<$arg> will have the value B<whatever>.
1268 An argument is required for placeholders than take one. I.e. The placeholder handler won't be called if there
1269 is no argument. If you want a placeholder to support an optional argument, you'll need to set the placeholder
1270 both with and without a colon (or they could be different subroutines):
1272 $File::KDBX::PLACEHOLDERS{'RAND'} = $File::KDBX::PLACEHOLDERS{'RAND:'} = sub {
1273 (undef, my $arg) = @_;
1274 return defined $arg ? rand($arg) : rand;
1277 You can also remove placeholder handlers. If you want to disable placeholder expansion entirely, just delete
1280 %File::KDBX::PLACEHOLDERS = ();
1282 =head2 One-time Passwords
1284 An entry can be configured to generate one-time passwords, both HOTP (HMAC-based) and TOTP (time-based). The
1285 configuration storage isn't completely standardized, but this module supports two predominant configuration
1289 * L<KeePass 2|https://keepass.info/help/base/placeholders.html#otp>
1292 B<NOTE:> To use this feature, you must install the suggested dependency:
1297 To configure TOTP in the KeePassXC style, there is only one string to set: C<otp>. The value should be any
1298 valid otpauth URI. When generating an OTP, all of the relevant OTP properties are parsed from the URI.
1300 To configure TOTP in the KeePass 2 style, set the following strings:
1303 * C<TimeOtp-Algorithm> - Cryptographic algorithm, one of C<HMAC-SHA-1> (default), C<HMAC-SHA-256> and
1305 * C<TimeOtp-Length> - Number of digits each one-time password is (default: 6, maximum: 8)
1306 * C<TimeOtp-Period> - Time-step size in seconds (default: 30)
1307 * C<TimeOtp-Secret> - Text string secret, OR
1308 * C<TimeOtp-Secret-Hex> - Hexidecimal-encoded secret, OR
1309 * C<TimeOtp-Secret-Base32> - Base32-encoded secret (most common), OR
1310 * C<TimeOtp-Secret-Base64> - Base64-encoded secret
1312 To configure HOTP in the KeePass 2 style, set the following strings:
1315 * C<HmacOtp-Counter> - Counting value in decimal, starts on C<0> by default and increments when L</hmac_otp>
1317 * C<HmacOtp-Secret> - Text string secret, OR
1318 * C<HmacOtp-Secret-Hex> - Hexidecimal-encoded secret, OR
1319 * C<HmacOtp-Secret-Base32> - Base32-encoded secret (most common), OR
1320 * C<HmacOtp-Secret-Base64> - Base64-encoded secret
1322 B<NOTE:> The multiple "Secret" strings are simply a way to store a secret in different formats. Only one of
1323 these should actually be set or an error will be thrown.
1325 Here's a basic example:
1327 $entry->string(otp => 'otpauth://totp/Issuer:user?secret=NBSWY3DP&issuer=Issuer');
1329 $entry->string('TimeOtp-Secret-Base32' => 'NBSWY3DP');
1331 my $otp = $entry->time_otp;