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(: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(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);
30 sub _parent_container
{ 'entries' }
34 128-bit UUID identifying the entry within the database
.
38 Integer representing a
default icon
. See L
<File
::KDBX
::Constants
/":icon"> for valid
values.
40 =attr custom_icon_uuid
42 128-bit UUID identifying a custom icon within the database
.
44 =attr foreground_color
46 Text color represented as a string of the form C
<#000000>.
48 =attr background_color
50 Background color represented as a string of the form C
<#FFFFFF>.
58 Text string with arbitrary tags which can be used to build a taxonomy
.
66 data_transfer_obfuscation
=> 0,
67 default_sequence
=> '{USERNAME}{TAB}{PASSWORD}{ENTER}',
70 window
=> 'My Bank - Mozilla Firefox',
71 keystroke_sequence
=> '{PASSWORD}{ENTER}',
76 =attr auto_type_enabled
78 Whether
or not the entry
is eligible to be matched
for auto-typing
.
80 =attr auto_type_data_transfer_obfuscation
84 =attr auto_type_default_sequence
86 The
default auto-type keystroke sequence
.
88 =attr auto_type_associations
90 An array of window title
/ keystroke sequence associations
.
92 =attr previous_parent_group
94 128-bit UUID identifying a group within the database
.
98 Boolean indicating whether the entry password should be tested
for weakness
and show up
in reports
.
102 Hash with entry strings
, including the standard strings as well as any custom ones
.
105 # Every entry has these five strings:
106 Title
=> { value
=> 'Example Entry' },
107 UserName
=> { value
=> 'jdoe' },
108 Password
=> { value
=> 's3cr3t', protect
=> true
},
109 URL
=> { value
=> 'https://example.com' }
110 Notes
=> { value
=> '' },
111 # May also have custom strings:
112 MySystem
=> { value
=> 'The mainframe' },
117 Files
or attachments
.
121 A set of key-value pairs used to store arbitrary data
, usually used by software to keep track of
state rather
122 than by end users
(who typically work with the strings
and binaries
).
126 Array of historical entries
. Historical entries are prior versions of the same entry so they all share the
127 same UUID with the current entry
.
129 =attr last_modification_time
131 Date
and time when the entry was
last modified
.
135 Date
and time when the entry was created
.
137 =attr last_access_time
139 Date
and time when the entry was
last accessed
.
143 Date
and time when the entry expired
or will expire
.
147 Boolean value indicating whether
or not an entry
is expired
.
151 The number of
times an entry
has been used
, which typically means how many
times the B
<Password
> string
has
154 =attr location_changed
156 Date
and time when the entry was
last moved to a different group
.
160 Alias
for the B
<Notes
> string value
.
164 Alias
for the B
<Password
> string value
.
168 Alias
for the B
<Title
> string value
.
172 Alias
for the B
<URL
> string value
.
176 Aliases
for the B
<UserName
> string value
.
182 if (@_ || !defined $self->{uuid
}) {
183 my %args = @_ % 2 == 1 ? (uuid
=> shift, @_) : @_;
184 my $old_uuid = $self->{uuid
};
185 my $uuid = $self->{uuid
} = delete $args{uuid
} // generate_uuid
;
186 for my $entry (@{$self->history}) {
187 $entry->{uuid
} = $uuid;
189 $self->_signal('uuid.changed', $uuid, $old_uuid) if defined $old_uuid && $self->is_current;
194 # has uuid => sub { generate_uuid(printable => 1) };
195 has icon_id
=> ICON_PASSWORD
, coerce
=> \
&to_icon_constant
;
196 has custom_icon_uuid
=> undef, coerce
=> \
&to_uuid
;
197 has foreground_color
=> '', coerce
=> \
&to_string
;
198 has background_color
=> '', coerce
=> \
&to_string
;
199 has override_url
=> '', coerce
=> \
&to_string
;
200 has tags
=> '', coerce
=> \
&to_string
;
202 has previous_parent_group
=> undef, coerce
=> \
&to_uuid
;
203 has quality_check
=> true
, coerce
=> \
&to_bool
;
207 # has custom_data => {};
210 has last_modification_time
=> sub { gmtime }, store
=> 'times', coerce
=> \
&to_time
;
211 has creation_time
=> sub { gmtime }, store
=> 'times', coerce
=> \
&to_time
;
212 has last_access_time
=> sub { gmtime }, store
=> 'times', coerce
=> \
&to_time
;
213 has expiry_time
=> sub { gmtime }, store
=> 'times', coerce
=> \
&to_time
;
214 has expires
=> false
, store
=> 'times', coerce
=> \
&to_bool
;
215 has usage_count
=> 0, store
=> 'times', coerce
=> \
&to_number
;
216 has location_changed
=> sub { gmtime }, store
=> 'times', coerce
=> \
&to_time
;
218 # has 'auto_type.auto_type_enabled' => true, coerce => \&to_bool;
219 has 'auto_type_data_transfer_obfuscation' => 0, path
=> 'auto_type.data_transfer_obfuscation',
220 coerce
=> \
&to_number
;
221 has 'auto_type_default_sequence' => '{USERNAME}{TAB}{PASSWORD}{ENTER}',
222 path
=> 'auto_type.default_sequence', coerce
=> \
&to_string
;
223 has 'auto_type_associations' => [], path
=> 'auto_type.associations';
225 my %ATTRS_STRINGS = (
227 username
=> 'UserName',
228 password
=> 'Password',
232 while (my ($attr, $string_key) = each %ATTRS_STRINGS) {
233 no strict
'refs'; ## no critic (ProhibitNoStrict)
234 *{$attr} = sub { shift-
>string_value($string_key, @_) };
235 *{"expanded_${attr}"} = sub { shift-
>expanded_string_value($string_key, @_) };
238 my @ATTRS = qw(uuid custom_data history auto_type_enabled);
239 sub _set_nonlazy_attributes
{
241 $self->$_ for @ATTRS, keys %ATTRS_STRINGS, list_attributes
(ref $self);
248 while (my ($key, $val) = each %args) {
249 if (my $method = $self->can($key)) {
250 $self->$method($val);
253 $self->string($key => $val);
260 ##############################################################################
264 \
%string = $entry->string($string_key);
266 $entry->string($string_key, \
%string);
267 $entry->string($string_key, %attributes);
268 $entry->string($string_key, $value); # same as: value => $value
270 Get
or set a string
. Every string
has a unique
(to the entry
) key
and flags
and so are returned as a hash
271 structure
. For example
:
275 protect
=> true
, # optional
278 Every string should have a value
(but might be C
<undef> due to memory protection
) and these optional flags
282 * C<protect> - Whether or not the string value should be memory-protected.
288 my %args = @_ == 2 ? (key
=> shift, value
=> shift)
289 : @_ % 2 == 1 ? (key
=> shift, @_) : @_;
291 if (!defined $args{key
} && !defined $args{value
}) {
292 my %standard = (value
=> 1, protect
=> 1);
293 my @other_keys = grep { !$standard{$_} } keys %args;
294 if (@other_keys == 1) {
295 my $key = $args{key
} = $other_keys[0];
296 $args{value
} = delete $args{$key};
300 my $key = delete $args{key
} or throw
'Must provide a string key to access';
302 return $self->{strings
}{$key} = $args{value
} if is_plain_hashref
($args{value
});
304 while (my ($field, $value) = each %args) {
305 $self->{strings
}{$key}{$field} = $value;
308 # Auto-vivify the standard strings.
309 if ($STANDARD_STRINGS{$key}) {
310 return $self->{strings
}{$key} //= {value
=> '', $self->_protect($key) ? (protect
=> true
) : ()};
312 return $self->{strings
}{$key};
315 ### Get whether or not a standard string is configured to be protected
319 return false
if !$STANDARD_STRINGS{$key};
320 if (my $kdbx = eval { $self->kdbx }) {
321 my $protect = $kdbx->memory_protection($key);
322 return $protect if defined $protect;
324 return $key eq 'Password';
329 $string = $entry->string_value($string_key);
331 Access a string value directly
. The arguments are the same as
for L
</string
>. Returns C
<undef> if the string
332 is not set
or is currently memory-protected
. This
is just a shortcut
for:
335 my $s = $entry->string(...);
336 defined $s ? $s->{value
} : undef;
343 my $string = $self->string(@_) // return undef;
344 return $string->{value
};
347 =method expanded_string_value
349 $string = $entry->expanded_string_value;
351 Same as L
</string_value
> but will substitute placeholders
and resolve field references
. Any placeholders that
352 do not expand to
values are left as-is
.
354 See L
</Placeholders
>.
356 Some placeholders
(notably field references
) require the entry be connected to a database
and will throw an
361 sub _expand_placeholder
{
363 my $placeholder = shift;
368 my $placeholder_key = $placeholder;
370 $placeholder_key = $File::KDBX
::PLACEHOLDERS
{"${placeholder}:${arg}"} ? "${placeholder}:${arg}"
373 return if !defined $File::KDBX
::PLACEHOLDERS
{$placeholder_key};
375 my $local_key = join('/', Hash
::Util
::FieldHash
::id
($self), $placeholder_key);
376 local $PLACEHOLDERS{$local_key} = my $handler = $PLACEHOLDERS{$local_key} // do {
377 my $handler = $File::KDBX
::PLACEHOLDERS
{$placeholder_key} or next;
378 memoize recurse_limit
($handler, $PLACEHOLDER_MAX_DEPTH, sub {
379 alert
"Detected deep recursion while expanding $placeholder placeholder",
380 placeholder
=> $placeholder;
385 return $handler->($self, $arg, $placeholder);
392 my $expand = memoize
$self->can('_expand_placeholder'), $self;
394 # placeholders (including field references):
395 $str =~ s!\{([^:\}]+)(?::([^\}]*))?\}!$expand->(uc($1), $2, @_) // $&!egi;
397 # environment variables (alt syntax):
398 my $vars = join('|', map { quotemeta($_) } keys %ENV);
399 $str =~ s!\%($vars)\%!$expand->(ENV => $1, @_) // $&!eg;
404 sub expanded_string_value
{
406 my $str = $self->string_peek(@_) // return undef;
407 my $cleanup = erase_scoped
$str;
408 return $self->_expand_string($str);
411 =method other_strings
413 $other = $entry->other_strings;
414 $other = $entry->other_strings($delimiter);
416 Get a concatenation of all non-standard string
values. The
default delimiter
is a newline
. This
is is useful
417 for executing queries to search
for entities based on the contents of these other strings
(if any
).
423 my $delim = shift // "\n";
425 my @strings = map { $self->string_value($_) } grep { !$STANDARD_STRINGS{$_} } sort keys %{$self->strings};
426 return join($delim, @strings);
431 $string = $entry->string_peek($string_key);
433 Same as L
</string_value
> but can also retrieve the value from protected-memory
if the value
is currently
440 my $string = $self->string(@_);
441 return defined $string->{value
} ? $string->{value
} : $self->kdbx->peek($string);
444 ##############################################################################
446 sub add_auto_type_association
{
448 my $association = shift;
449 push @{$self->auto_type_associations}, $association;
452 sub expand_keystroke_sequence
{
454 my $association = shift;
456 my $keys = is_hashref
($association) && exists $association->{keystroke_sequence
} ?
457 $association->{keystroke_sequence
} : defined $association ? $association : '';
459 $keys = $self->auto_type_default_sequence if !$keys;
460 # TODO - Fall back to getting default sequence from parent group, which probably means we shouldn't be
461 # setting a default value in the entry..
463 return $self->_expand_string($keys);
466 ##############################################################################
470 \
%binary = $entry->binary($binary_key);
472 $entry->binary($binary_key, \
%binary);
473 $entry->binary($binary_key, %attributes);
474 $entry->binary($binary_key, $value); # same as: value => $value
476 Get
or set a binary
. Every binary
has a unique
(to the entry
) key
and flags
and so are returned as a hash
477 structure
. For example
:
481 protect
=> true
, # optional
484 Every binary should have a value
(but might be C
<undef> due to memory protection
) and these optional flags
488 * C<protect> - Whether or not the binary value should be memory-protected.
494 my %args = @_ == 2 ? (key
=> shift, value
=> shift)
495 : @_ % 2 == 1 ? (key
=> shift, @_) : @_;
497 if (!defined $args{key
} && !defined $args{value
}) {
498 my %standard = (value
=> 1, protect
=> 1);
499 my @other_keys = grep { !$standard{$_} } keys %args;
500 if (@other_keys == 1) {
501 my $key = $args{key
} = $other_keys[0];
502 $args{value
} = delete $args{$key};
506 my $key = delete $args{key
} or throw
'Must provide a binary key to access';
508 return $self->{binaries
}{$key} = $args{value
} if is_plain_hashref
($args{value
});
510 while (my ($field, $value) = each %args) {
511 $self->{binaries
}{$key}{$field} = $value;
513 return $self->{binaries
}{$key};
518 $binary = $entry->binary_value($binary_key);
520 Access a binary value directly
. The arguments are the same as
for L
</binary
>. Returns C
<undef> if the binary
521 is not set
or is currently memory-protected
. This
is just a shortcut
for:
524 my $b = $entry->binary(...);
525 defined $b ? $b->{value
} : undef;
532 my $binary = $self->binary(@_) // return undef;
533 return $binary->{value
};
536 ##############################################################################
538 sub searching_enabled
{
540 my $parent = $self->group;
541 return $parent->effective_enable_searching if $parent;
545 sub auto_type_enabled
{
547 $self->auto_type->{enabled
} = to_bool
(shift) if @_;
548 $self->auto_type->{enabled
} //= true
;
549 return false
if !$self->auto_type->{enabled
};
550 return true
if !$self->is_connected;
551 my $parent = $self->group;
552 return $parent->effective_enable_auto_type if $parent;
556 ##############################################################################
560 $otp = $entry->hmac_otp(%options);
562 Generate an HMAC-based one-time password
, or C
<undef> if HOTP
is not configured
for the entry
. The entry
's
563 strings generally must first be unprotected, just like when accessing the password. Valid options are:
566 * C<counter> - Specify the counter value
568 To configure HOTP, see L</"One-time Passwords">.
574 load_optional('Pass
::OTP
');
576 my %params = ($self->_hotp_params, @_);
577 return if !defined $params{type} || !defined $params{secret};
579 $params{secret} = encode_b32r($params{secret}) if !$params{base32};
582 my $otp = eval {Pass::OTP::otp(%params, @_) };
584 throw 'Unable to generate HOTP
', error => $err;
587 $self->_hotp_increment_counter($params{counter});
594 $otp = $entry->time_otp(%options);
596 Generate a time-based one-time password, or C<undef> if TOTP is not configured for the entry. The entry's
597 strings generally must first be unprotected
, just like
when accessing the password
. Valid options are
:
600 * C<now> - Specify the value for determining the time-step counter
602 To configure TOTP, see L</"One-time Passwords">.
608 load_optional
('Pass::OTP');
610 my %params = ($self->_totp_params, @_);
611 return if !defined $params{type
} || !defined $params{secret
};
613 $params{secret
} = encode_b32r
($params{secret
}) if !$params{base32
};
616 my $otp = eval {Pass
::OTP
::otp
(%params, @_) };
618 throw
'Unable to generate TOTP', error
=> $err;
628 $uri_string = $entry->hmac_otp_uri;
629 $uri_string = $entry->time_otp_uri;
631 Get a HOTP
or TOTP otpauth URI
for the entry
, if available
.
633 To configure OTP
, see L
</"One-time Passwords">.
637 sub hmac_otp_uri
{ $_[0]->_otp_uri($_[0]->_hotp_params) }
638 sub time_otp_uri
{ $_[0]->_otp_uri($_[0]->_totp_params) }
644 return if 4 != grep { defined } @params{qw(type secret issuer account)};
645 return if $params{type
} !~ /^[ht]otp$/i;
647 my $label = delete $params{label
};
648 $params{$_} = uri_escape_utf8
($params{$_}) for keys %params;
650 my $type = lc($params{type
});
651 my $issuer = $params{issuer
};
652 my $account = $params{account
};
654 $label //= "$issuer:$account";
656 my $secret = $params{secret
};
657 $secret = uc(encode_b32r
($secret)) if !$params{base32
};
659 delete $params{algorithm
} if defined $params{algorithm
} && $params{algorithm
} eq 'sha1';
660 delete $params{period
} if defined $params{period
} && $params{period
} == 30;
661 delete $params{digits
} if defined $params{digits
} && $params{digits
} == 6;
662 delete $params{counter
} if defined $params{counter
} && $params{counter
} == 0;
664 my $uri = "otpauth://$type/$label?secret=$secret&issuer=$issuer";
666 if (defined $params{encoder
}) {
667 $uri .= "&encoder=$params{encoder}";
670 $uri .= '&algorithm=' . uc($params{algorithm
}) if defined $params{algorithm
};
671 $uri .= "&digits=$params{digits}" if defined $params{digits
};
672 $uri .= "&counter=$params{counter}" if defined $params{counter
};
673 $uri .= "&period=$params{period}" if defined $params{period
};
683 issuer
=> $self->title || 'KDBX',
684 account
=> $self->username || 'none',
686 counter
=> $self->string_value('HmacOtp-Counter') // 0,
687 $self->_otp_secret_params('Hmac'),
689 return %params if $params{secret
};
691 my %otp_params = $self->_otp_params;
692 return () if !$otp_params{secret
} || $otp_params{type
} ne 'hotp';
694 # $otp_params{counter} = 0
696 return (%params, %otp_params);
703 'HMAC-SHA-1' => 'sha1',
704 'HMAC-SHA-256' => 'sha256',
705 'HMAC-SHA-512' => 'sha512',
709 issuer
=> $self->title || 'KDBX',
710 account
=> $self->username || 'none',
711 digits
=> $self->string_value('TimeOtp-Length') // 6,
712 algorithm
=> $algorithms{$self->string_value('TimeOtp-Algorithm') || ''} || 'sha1',
713 period
=> $self->string_value('TimeOtp-Period') // 30,
714 $self->_otp_secret_params('Time'),
716 return %params if $params{secret
};
718 my %otp_params = $self->_otp_params;
719 return () if !$otp_params{secret
} || $otp_params{type
} ne 'totp';
721 return (%params, %otp_params);
727 load_optional
('Pass::OTP::URI');
729 my $uri = $self->string_value('otp') || '';
731 %params = Pass
::OTP
::URI
::parse
($uri) if $uri =~ m!^otpauth://!;
732 return () if !$params{secret
} || !$params{type
};
734 if (($params{encoder
} // '') eq 'steam') {
736 $params{chars
} = '23456789BCDFGHJKMNPQRTVWXY';
739 # Pass::OTP::URI doesn't provide the issuer and account separately, so get them from the label
740 my ($issuer, $user) = split(':', $params{label
} // ':', 2);
741 $params{issuer
} //= uri_unescape_utf8
($issuer);
742 $params{account
} //= uri_unescape_utf8
($user);
744 $params{algorithm
} = lc($params{algorithm
}) if $params{algorithm
};
745 $params{counter
} = $self->string_value('HmacOtp-Counter') if $params{type
} eq 'hotp';
750 sub _otp_secret_params
{
752 my $type = shift // return ();
754 my $secret_txt = $self->string_value("${type}Otp-Secret");
755 my $secret_hex = $self->string_value("${type}Otp-Secret-Hex");
756 my $secret_b32 = $self->string_value("${type}Otp-Secret-Base32");
757 my $secret_b64 = $self->string_value("${type}Otp-Secret-Base64");
759 my $count = grep { defined } ($secret_txt, $secret_hex, $secret_b32, $secret_b64);
760 return () if $count == 0;
761 alert
"Found multiple ${type}Otp-Secret strings", count
=> $count if 1 < $count;
763 return (secret
=> $secret_b32, base32
=> 1) if defined $secret_b32;
764 return (secret
=> decode_b64
($secret_b64)) if defined $secret_b64;
765 return (secret
=> pack('H*', $secret_hex)) if defined $secret_hex;
766 return (secret
=> encode
('UTF-8', $secret_txt));
769 sub _hotp_increment_counter
{
771 my $counter = shift // $self->string_value('HmacOtp-Counter') || 0;
773 looks_like_number
($counter) or throw
'HmacOtp-Counter value must be a number', value
=> $counter;
774 my $next = $counter + 1;
775 $self->string('HmacOtp-Counter', $next);
779 ##############################################################################
783 $size = $entry->size;
785 Get the size
(in bytes
) of an entry
.
787 B
<NOTE
:> This
is not an exact figure because there
is no canonical serialization of an entry
. This size should
788 only be used as a rough estimate
for comparison with other entries
or to impose data size limitations
.
798 $size += length(encode
('UTF-8', $self->tags // ''));
800 # attributes (strings)
801 while (my ($key, $string) = each %{$self->strings}) {
802 next if !defined $string->{value
};
803 $size += length(encode
('UTF-8', $key)) + length(encode
('UTF-8', $string->{value
} // ''));
807 while (my ($key, $item) = each %{$self->custom_data}) {
808 next if !defined $item->{value
};
809 $size += length(encode
('UTF-8', $key)) + length(encode
('UTF-8', $item->{value
} // ''));
813 while (my ($key, $binary) = each %{$self->binaries}) {
814 next if !defined $binary->{value
};
815 my $value_len = utf8
::is_utf8
($binary->{value
}) ? length(encode
('UTF-8', $binary->{value
}))
816 : length($binary->{value
});
817 $size += length(encode
('UTF-8', $key)) + $value_len;
820 # autotype associations
821 for my $association (@{$self->auto_type->{associations
} || []}) {
822 $size += length(encode
('UTF-8', $association->{window
}))
823 + length(encode
('UTF-8', $association->{keystroke_sequence
} // ''));
829 ##############################################################################
833 my $entries = $self->{history
} //= [];
834 # FIXME - Looping through entries on each access is too expensive.
835 @$entries = map { $self->_wrap_entry($_, $self->kdbx) } @$entries;
841 $size = $entry->history_size;
843 Get the size
(in bytes
) of all historical entries combined
.
849 return sum0
map { $_->size } @{$self->history};
852 =method prune_history
854 $entry->prune_history(%options);
856 Remove as many older historical entries as necessary to get under the database limits
. The limits are taken
857 from the connected database
(if any
) or can be overridden with C
<%options>:
860 * C<max_items> - Maximum number of historical entries to keep (default: 10, no limit: -1)
861 * C<max_size> - Maximum total size (in bytes) of historical entries to keep (default: 6 MiB, no limit: -1)
869 my $max_items = $args{max_items
} // eval { $self->kdbx->history_max_items }
870 // HISTORY_DEFAULT_MAX_ITEMS
;
871 my $max_size = $args{max_size
} // eval { $self->kdbx->history_max_size }
872 // HISTORY_DEFAULT_MAX_SIZE
;
874 # history is ordered oldest to youngest
875 my $history = $self->history;
877 if (0 <= $max_items && $max_items < @$history) {
878 splice @$history, -$max_items;
881 if (0 <= $max_size) {
882 my $current_size = $self->history_size;
883 while ($max_size < $current_size) {
884 my $entry = shift @$history;
885 $current_size -= $entry->size;
890 =method add_historical_entry
892 $entry->add_historical_entry($entry);
894 Add an entry to the history
.
898 sub add_historical_entry
{
900 delete $_->{history
} for @_;
901 push @{$self->{history
} //= []}, map { $self->_wrap_entry($_) } @_;
904 =method current_entry
906 $current_entry = $entry->current_entry;
908 Get an entry
's current entry. If the entry itself is current (not historical), itself is returned.
914 my $group = $self->group;
917 my $id = $self->uuid;
918 my $entry = first { $id eq $_->uuid } @{$group->entries};
919 return $entry if $entry;
927 $bool = $entry->is_current;
929 Get whether or not an entry is considered current (i.e. not historical). An entry is current if it is directly
930 in the parent group's entry list
.
936 my $current = $self->current_entry;
937 return Hash
::Util
::FieldHash
::id
($self) == Hash
::Util
::FieldHash
::id
($current);
940 =method is_historical
942 $bool = $entry->is_historical;
944 Get whether
or not an entry
is considered historical
(i
.e
. not current
).
946 This
is just the inverse of L
</is_current
>.
950 sub is_historical
{ !$_[0]->is_current }
952 ##############################################################################
957 return $self->SUPER::_signal
("entry.$type", @_);
963 $self->add_historical_entry($orig);
965 $self->last_modification_time($time);
966 $self->last_access_time($time);
969 sub label
{ shift-
>expanded_title(@_) }
976 An entry in a KDBX database is a record that can contains strings (also called "fields") and binaries (also
977 called "files" or "attachments"). Every string and binary has a key or name. There is a default set of strings
978 that every entry has:
987 Beyond this, you can store any number of other strings and any number of binaries that you can use for
988 whatever purpose you want.
990 There is also some metadata associated with an entry. Each entry in a database is identified uniquely by
991 a UUID. An entry can also have an icon associated with it, and there are various timestamps. Take a look at
992 the attributes to see what's available.
994 A B<File::KDBX::Entry> is a subclass of L<File::KDBX::Object>.
998 Entry string and auto-type key sequences can have placeholders or template tags that can be replaced by other
999 values. Placeholders can appear like C<{PLACEHOLDER}>. For example, a B<URL> string might have a value of
1000 C<http://example.com?user={USERNAME}>. C<{USERNAME}> is a placeholder for the value of the B<UserName> string
1001 of the same entry. If the B<UserName> string had a value of "batman", the B<URL> string would expand to
1002 C<http://example.com?user=batman>.
1004 Some placeholders take an argument, where the argument follows the tag after a colon but before the closing
1005 brace, like C<{PLACEHOLDER:ARGUMENT}>.
1007 Placeholders are documented in the L<KeePass Help Center|https://keepass.info/help/base/placeholders.html>.
1008 This software supports many (but not all) of the placeholders documented there.
1010 =head3 Entry Placeholders
1013 * ☑ C<{TITLE}> - B<Title> string
1014 * ☑ C<{USERNAME}> - B<UserName> string
1015 * ☑ C<{PASSWORD}> - B<Password> string
1016 * ☑ C<{NOTES}> - B<Notes> string
1017 * ☑ C<{URL}> - B<URL> string
1018 * ☑ C<{URL:SCM}> / C<{URL:SCHEME}>
1019 * ☑ C<{URL:USERINFO}>
1020 * ☑ C<{URL:USERNAME}>
1021 * ☑ C<{URL:PASSWORD}>
1026 * ☑ C<{URL:FRAGMENT}> / C<{URL:HASH}>
1027 * ☑ C<{URL:RMVSCM}> / C<{URL:WITHOUTSCHEME}>
1028 * ☑ C<{S:Name}> - Custom string where C<Name> is the name or key of the string
1029 * ☑ C<{UUID}> - Identifier (32 hexidecimal characters)
1030 * ☑ C<{HMACOTP}> - Generate an HMAC-based one-time password (its counter B<will> be incremented)
1031 * ☑ C<{TIMEOTP}> - Generate a time-based one-time password
1032 * ☑ C<{GROUP_NOTES}> - Notes of the parent group
1033 * ☑ C<{GROUP_PATH}> - Full path of the parent group
1034 * ☑ C<{GROUP}> - Name of the parent group
1036 =head3 Field References
1039 * ☑ C<{REF:Wanted@SearchIn:Text}> - See L<File::KDBX/resolve_reference>
1041 =head3 File path Placeholders
1044 * ☑ C<{APPDIR}> - Program directory path
1045 * ☑ C<{FIREFOX}> - Path to the Firefox browser executable
1046 * ☑ C<{GOOGLECHROME}> - Path to the Chrome browser executable
1047 * ☑ C<{INTERNETEXPLORER}> - Path to the Firefox browser executable
1048 * ☑ C<{OPERA}> - Path to the Opera browser executable
1049 * ☑ C<{SAFARI}> - Path to the Safari browser executable
1050 * ☒ C<{DB_PATH}> - Full file path of the database
1051 * ☒ C<{DB_DIR}> - Directory path of the database
1052 * ☒ C<{DB_NAME}> - File name (including extension) of the database
1053 * ☒ C<{DB_BASENAME}> - File name (excluding extension) of the database
1054 * ☒ C<{DB_EXT}> - File name extension
1055 * ☑ C<{ENV_DIRSEP}> - Directory separator
1056 * ☑ C<{ENV_PROGRAMFILES_X86}> - One of C<%ProgramFiles(x86)%> or C<%ProgramFiles%>
1058 =head3 Date and Time Placeholders
1061 * ☑ C<{DT_SIMPLE}> - Current local date and time as a sortable string
1062 * ☑ C<{DT_YEAR}> - Year component of the current local date
1063 * ☑ C<{DT_MONTH}> - Month component of the current local date
1064 * ☑ C<{DT_DAY}> - Day component of the current local date
1065 * ☑ C<{DT_HOUR}> - Hour component of the current local time
1066 * ☑ C<{DT_MINUTE}> - Minute component of the current local time
1067 * ☑ C<{DT_SECOND}> - Second component of the current local time
1068 * ☑ C<{DT_UTC_SIMPLE}> - Current UTC date and time as a sortable string
1069 * ☑ C<{DT_UTC_YEAR}> - Year component of the current UTC date
1070 * ☑ C<{DT_UTC_MONTH}> - Month component of the current UTC date
1071 * ☑ C<{DT_UTC_DAY}> - Day component of the current UTC date
1072 * ☑ C<{DT_UTC_HOUR}> - Hour component of the current UTC time
1073 * ☑ C<{DT_UTC_MINUTE}> Minute Year component of the current UTC time
1074 * ☑ C<{DT_UTC_SECOND}> - Second component of the current UTC time
1076 If the current date and time is <2012-07-25 17:05:34>, the "simple" form would be C<20120725170534>.
1078 =head3 Special Key Placeholders
1080 Certain placeholders for use in auto-type key sequences are not supported for replacement, but they will
1081 remain as-is so that an auto-type engine (not included) can parse and replace them with the appropriate
1082 virtual key presses. For completeness, here is the list that the KeePass program claims to support:
1084 C<{TAB}>, C<{ENTER}>, C<{UP}>, C<{DOWN}>, C<{LEFT}>, C<{RIGHT}>, C<{HOME}>, C<{END}>, C<{PGUP}>, C<{PGDN}>,
1085 C<{INSERT}>, C<{DELETE}>, C<{SPACE}>
1087 C<{BACKSPACE}>, C<{BREAK}>, C<{CAPSLOCK}>, C<{ESC}>, C<{WIN}>, C<{LWIN}>, C<{RWIN}>, C<{APPS}>, C<{HELP}>,
1088 C<{NUMLOCK}>, C<{PRTSC}>, C<{SCROLLLOCK}>
1090 C<{F1}>, C<{F2}>, C<{F3}>, C<{F4}>, C<{F5}>, C<{F6}>, C<{F7}>, C<{F8}>, C<{F9}>, C<{F10}>, C<{F11}>, C<{F12}>,
1091 C<{F13}>, C<{F14}>, C<{F15}>, C<{F16}>
1093 C<{ADD}>, C<{SUBTRACT}>, C<{MULTIPLY}>, C<{DIVIDE}>, C<{NUMPAD0}>, C<{NUMPAD1}>, C<{NUMPAD2}>, C<{NUMPAD3}>,
1094 C<{NUMPAD4}>, C<{NUMPAD5}>, C<{NUMPAD6}>, C<{NUMPAD7}>, C<{NUMPAD8}>, C<{NUMPAD9}>
1096 =head3 Miscellaneous Placeholders
1100 * ☒ C<{BASE:SCM}> / C<{BASE:SCHEME}>
1101 * ☒ C<{BASE:USERINFO}>
1102 * ☒ C<{BASE:USERNAME}>
1103 * ☒ C<{BASE:PASSWORD}>
1108 * ☒ C<{BASE:FRAGMENT}> / C<{BASE:HASH}>
1109 * ☒ C<{BASE:RMVSCM}> / C<{BASE:WITHOUTSCHEME}>
1110 * ☒ C<{CLIPBOARD-SET:/Text/}>
1112 * ☒ C<{CMD:/CommandLine/Options/}>
1113 * ☑ C<{C:Comment}> - Comments are simply replaced by nothing
1114 * ☑ C<{ENV:}> and C<%ENV%> - Environment variables
1115 * ☒ C<{GROUP_SEL_NOTES}>
1116 * ☒ C<{GROUP_SEL_PATH}>
1118 * ☒ C<{NEWPASSWORD}>
1119 * ☒ C<{NEWPASSWORD:/Profile/}>
1120 * ☒ C<{PASSWORD_ENC}>
1122 * ☒ C<{PICKCHARS:Field:Options}>
1124 * ☒ C<{T-CONV:/Text/Type/}>
1125 * ☒ C<{T-REPLACE-RX:/Text/Type/Replace/}>
1127 Some of these that remain unimplemented, such as C<{CLIPBOARD}>, cannot be implemented portably. Some of these
1128 I haven't implemented (yet) just because they don't seem very useful. You can create your own placeholder to
1129 augment the list of default supported placeholders or to replace a built-in placeholder handler. To create
1130 a placeholder, just set it in the C<%File::KDBX::PLACEHOLDERS> hash. For example:
1132 $File::KDBX::PLACEHOLDERS{'MY_PLACEHOLDER'} = sub {
1137 If the placeholder is expanded in the context of an entry, C<$entry> is the B<File::KDBX::Entry> object in
1138 context. Otherwise it is C<undef>. An entry is in context if, for example, the placeholder is in an entry's
1139 strings or auto-complete key sequences.
1141 $File::KDBX::PLACEHOLDERS{'MY_PLACEHOLDER:'} = sub {
1142 my ($entry, $arg) = @_; # ^ Notice the colon here
1146 If the name of the placeholder ends in a colon, then it is expected to receive an argument. During expansion,
1147 everything after the colon and before the end of the placeholder is passed to your placeholder handler
1148 subroutine. So if the placeholder is C<{MY_PLACEHOLDER:whatever}>, C<$arg> will have the value B<whatever>.
1150 An argument is required for placeholders than take one. I.e. The placeholder handler won't be called if there
1151 is no argument. If you want a placeholder to support an optional argument, you'll need to set the placeholder
1152 both with and without a colon (or they could be different subroutines):
1154 $File::KDBX::PLACEHOLDERS{'RAND'} = $File::KDBX::PLACEHOLDERS{'RAND:'} = sub {
1155 (undef, my $arg) = @_;
1156 return defined $arg ? rand($arg) : rand;
1159 You can also remove placeholder handlers. If you want to disable placeholder expansion entirely, just delete
1162 %File::KDBX::PLACEHOLDERS = ();
1164 =head2 One-time Passwords
1166 An entry can be configured to generate one-time passwords, both HOTP (HMAC-based) and TOTP (time-based). The
1167 configuration storage isn't completely standardized, but this module supports two predominant configuration
1171 * L<KeePass 2|https://keepass.info/help/base/placeholders.html#otp>
1174 B<NOTE:> To use this feature, you must install the suggested dependency:
1179 To configure TOTP in the KeePassXC style, there is only one string to set: C<otp>. The value should be any
1180 valid otpauth URI. When generating an OTP, all of the relevant OTP properties are parsed from the URI.
1182 To configure TOTP in the KeePass 2 style, set the following strings:
1185 * C<TimeOtp-Algorithm> - Cryptographic algorithm, one of C<HMAC-SHA-1> (default), C<HMAC-SHA-256> and
1187 * C<TimeOtp-Length> - Number of digits each one-time password is (default: 6, maximum: 8)
1188 * C<TimeOtp-Period> - Time-step size in seconds (default: 30)
1189 * C<TimeOtp-Secret> - Text string secret, OR
1190 * C<TimeOtp-Secret-Hex> - Hexidecimal-encoded secret, OR
1191 * C<TimeOtp-Secret-Base32> - Base32-encoded secret (most common), OR
1192 * C<TimeOtp-Secret-Base64> - Base64-encoded secret
1194 To configure HOTP in the KeePass 2 style, set the following strings:
1197 * C<HmacOtp-Counter> - Counting value in decimal, starts on C<0> by default and increments when L</hmac_otp>
1199 * C<HmacOtp-Secret> - Text string secret, OR
1200 * C<HmacOtp-Secret-Hex> - Hexidecimal-encoded secret, OR
1201 * C<HmacOtp-Secret-Base32> - Base32-encoded secret (most common), OR
1202 * C<HmacOtp-Secret-Base64> - Base64-encoded secret
1204 B<NOTE:> The multiple "Secret" strings are simply a way to store a secret in different formats. Only one of
1205 these should actually be set or an error will be thrown.
1207 Here's a basic example:
1209 $entry->string(otp => 'otpauth://totp/Issuer:user?secret=NBSWY3DP&issuer=Issuer');
1211 $entry->string('TimeOtp-Secret-Base32' => 'NBSWY3DP');
1213 my $otp = $entry->time_otp;