]> Dogcows Code - chaz/p5-File-KDBX/blobdiff - lib/File/KDBX/Entry.pm
use functions moved to File::KDBX::XS module
[chaz/p5-File-KDBX] / lib / File / KDBX / Entry.pm
index c3ddcb95ead4d4fb03c623e60feef3f6900c4524..5e666bbb5fa1c4e5d88a4901a3a2cb28fbd28e2c 100644 (file)
@@ -10,9 +10,10 @@ use Encode qw(encode);
 use File::KDBX::Constants qw(:history :icon);
 use File::KDBX::Error;
 use File::KDBX::Util qw(:function :uri generate_uuid load_optional);
 use File::KDBX::Constants qw(:history :icon);
 use File::KDBX::Error;
 use File::KDBX::Util qw(:function :uri generate_uuid load_optional);
+use Hash::Util::FieldHash;
 use List::Util qw(sum0);
 use List::Util qw(sum0);
-use Ref::Util qw(is_plain_hashref is_ref);
-use Scalar::Util qw(looks_like_number refaddr);
+use Ref::Util qw(is_plain_hashref);
+use Scalar::Util qw(looks_like_number);
 use Storable qw(dclone);
 use Time::Piece;
 use boolean;
 use Storable qw(dclone);
 use Time::Piece;
 use boolean;
@@ -26,6 +27,8 @@ my $PLACEHOLDER_MAX_DEPTH = 10;
 my %PLACEHOLDERS;
 my %STANDARD_STRINGS = map { $_ => 1 } qw(Title UserName Password URL Notes);
 
 my %PLACEHOLDERS;
 my %STANDARD_STRINGS = map { $_ => 1 } qw(Title UserName Password URL Notes);
 
+sub _parent_container { 'entries' }
+
 =attr uuid
 
 128-bit UUID identifying the entry within the database.
 =attr uuid
 
 128-bit UUID identifying the entry within the database.
@@ -129,7 +132,7 @@ Boolean value indicating whether or not an entry is expired.
 
 =attr usage_count
 
 
 =attr usage_count
 
-The number of times an entry has been used, which typically means how many times the C<Password> string has
+The number of times an entry has been used, which typically means how many times the B<Password> string has
 been accessed.
 
 =attr location_changed
 been accessed.
 
 =attr location_changed
@@ -138,23 +141,23 @@ Date and time when the entry was last moved to a different group.
 
 =attr notes
 
 
 =attr notes
 
-Alias for the C<Notes> string value.
+Alias for the B<Notes> string value.
 
 =attr password
 
 
 =attr password
 
-Alias for the C<Password> string value.
+Alias for the B<Password> string value.
 
 =attr title
 
 
 =attr title
 
-Alias for the C<Title> string value.
+Alias for the B<Title> string value.
 
 =attr url
 
 
 =attr url
 
-Alias for the C<URL> string value.
+Alias for the B<URL> string value.
 
 =attr username
 
 
 =attr username
 
-Aliases for the C<UserName> string value.
+Aliases for the B<UserName> string value.
 
 =cut
 
 
 =cut
 
@@ -167,7 +170,7 @@ sub uuid {
         for my $entry (@{$self->history}) {
             $entry->{uuid} = $uuid;
         }
         for my $entry (@{$self->history}) {
             $entry->{uuid} = $uuid;
         }
-        # if (defined $old_uuid and my $kdbx = $KDBX{refaddr($self)}) {
+        # if (defined $old_uuid and my $kdbx = $KDBX{$self}) {
         #     $kdbx->_update_entry_uuid($old_uuid, $uuid, $self);
         # }
     }
         #     $kdbx->_update_entry_uuid($old_uuid, $uuid, $self);
         # }
     }
@@ -252,8 +255,6 @@ sub init {
     return $self;
 }
 
     return $self;
 }
 
-sub label { shift->title(@_) }
-
 ##############################################################################
 
 =method string
 ##############################################################################
 
 =method string
@@ -269,10 +270,11 @@ structure. For example:
 
     $string = {
         value   => 'Password',
 
     $string = {
         value   => 'Password',
-        protect => true,
+        protect => true,    # optional
     };
 
     };
 
-Every string should have a value and these optional flags which might exist:
+Every string should have a value (but might be C<undef> due to memory protection) and these optional flags
+which might exist:
 
 =for :list
 * C<protect> - Whether or not the string value should be memory-protected.
 
 =for :list
 * C<protect> - Whether or not the string value should be memory-protected.
@@ -281,10 +283,6 @@ Every string should have a value and these optional flags which might exist:
 
 sub string {
     my $self = shift;
 
 sub string {
     my $self = shift;
-    # use Data::Dumper;
-    # $self->{strings} = shift if @_ == 1 && is_plain_hashref($_[0]);
-    # return $self->{strings} //= {} if !@_;
-
     my %args = @_     == 2 ? (key => shift, value => shift)
              : @_ % 2 == 1 ? (key => shift, @_) : @_;
 
     my %args = @_     == 2 ? (key => shift, value => shift)
              : @_ % 2 == 1 ? (key => shift, @_) : @_;
 
@@ -366,7 +364,7 @@ sub _expand_placeholder {
     }
     return if !defined $File::KDBX::PLACEHOLDERS{$placeholder_key};
 
     }
     return if !defined $File::KDBX::PLACEHOLDERS{$placeholder_key};
 
-    my $local_key = join('/', refaddr($self), $placeholder_key);
+    my $local_key = join('/', Hash::Util::FieldHash::id($self), $placeholder_key);
     local $PLACEHOLDERS{$local_key} = my $handler = $PLACEHOLDERS{$local_key} // do {
         my $handler = $File::KDBX::PLACEHOLDERS{$placeholder_key} or next;
         memoize recurse_limit($handler, $PLACEHOLDER_MAX_DEPTH, sub {
     local $PLACEHOLDERS{$local_key} = my $handler = $PLACEHOLDERS{$local_key} // do {
         my $handler = $File::KDBX::PLACEHOLDERS{$placeholder_key} or next;
         memoize recurse_limit($handler, $PLACEHOLDER_MAX_DEPTH, sub {
@@ -461,6 +459,11 @@ sub binary_value {
     return $binary->{value};
 }
 
     return $binary->{value};
 }
 
+sub auto_type_enabled {
+    my $entry = shift;
+    # TODO
+}
+
 ##############################################################################
 
 =method hmac_otp
 ##############################################################################
 
 =method hmac_otp
@@ -738,7 +741,10 @@ sub size {
 
 sub history {
     my $self = shift;
 
 sub history {
     my $self = shift;
-    return [map { __PACKAGE__->wrap($_, $self->kdbx) } @{$self->{history} || []}];
+    my $entries = $self->{history} //= [];
+    # FIXME - Looping through entries on each access is too expensive.
+    @$entries = map { $self->_wrap_entry($_, $self->kdbx) } @$entries;
+    return $entries;
 }
 
 =method history_size
 }
 
 =method history_size
@@ -759,7 +765,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
     $entry->prune_history(%options);
 
 Remove as many older historical entries as necessary to get under the database limits. The limits are taken
-from the database or can be specified with C<%options>:
+from the associated database (if any) or can be overridden with C<%options>:
 
 =for :list
 * C<max_items> - Maximum number of historical entries to keep (default: 10, no limit: -1)
 
 =for :list
 * C<max_items> - Maximum number of historical entries to keep (default: 10, no limit: -1)
@@ -795,7 +801,7 @@ sub prune_history {
 sub add_history {
     my $self = shift;
     delete $_->{history} for @_;
 sub add_history {
     my $self = shift;
     delete $_->{history} for @_;
-    push @{$self->{history} //= []}, @_;
+    push @{$self->{history} //= []}, map { $self->_wrap_entry($_) } @_;
 }
 
 ##############################################################################
 }
 
 ##############################################################################
@@ -813,7 +819,7 @@ sub _commit {
     $self->last_modification_time(gmtime);
 }
 
     $self->last_modification_time(gmtime);
 }
 
-sub TO_JSON { +{%{$_[0]}} }
+sub label { shift->expanded_title(@_) }
 
 1;
 __END__
 
 1;
 __END__
@@ -825,11 +831,11 @@ called "files" or "attachments"). Every string and binary has a key or name. The
 that every entry has:
 
 =for :list
 that every entry has:
 
 =for :list
-* C<Title>
-* C<UserName>
-* C<Password>
-* C<URL>
-* C<Notes>
+* B<Title>
+* B<UserName>
+* B<Password>
+* B<URL>
+* B<Notes>
 
 Beyond this, you can store any number of other strings and any number of binaries that you can use for
 whatever purpose you want.
 
 Beyond this, you can store any number of other strings and any number of binaries that you can use for
 whatever purpose you want.
@@ -840,14 +846,14 @@ the attributes to see what's available.
 
 =head2 Placeholders
 
 
 =head2 Placeholders
 
-Entry strings and auto-type key sequences can have placeholders or template tags that can be replaced by other
+Entry string and auto-type key sequences can have placeholders or template tags that can be replaced by other
 values. Placeholders can appear like C<{PLACEHOLDER}>. For example, a B<URL> string might have a value of
 C<http://example.com?user={USERNAME}>. C<{USERNAME}> is a placeholder for the value of the B<UserName> string
 values. Placeholders can appear like C<{PLACEHOLDER}>. For example, a B<URL> string might have a value of
 C<http://example.com?user={USERNAME}>. C<{USERNAME}> is a placeholder for the value of the B<UserName> string
-of the same entry. If the C<UserName> string had a value of "batman", the B<URL> string would expand to
+of the same entry. If the B<UserName> string had a value of "batman", the B<URL> string would expand to
 C<http://example.com?user=batman>.
 
 C<http://example.com?user=batman>.
 
-Some placeholders take an argument, where the argument follows the tag after a colon. The syntax for this is
-C<{PLACEHOLDER:ARGUMENT}>.
+Some placeholders take an argument, where the argument follows the tag after a colon but before the closing
+brace, like C<{PLACEHOLDER:ARGUMENT}>.
 
 Placeholders are documented in the L<KeePass Help Center|https://keepass.info/help/base/placeholders.html>.
 This software supports many (but not all) of the placeholders documented there.
 
 Placeholders are documented in the L<KeePass Help Center|https://keepass.info/help/base/placeholders.html>.
 This software supports many (but not all) of the placeholders documented there.
@@ -872,7 +878,7 @@ This software supports many (but not all) of the placeholders documented there.
 * ☑ C<{URL:RMVSCM}> / C<{URL:WITHOUTSCHEME}>
 * ☑ C<{S:Name}> - Custom string where C<Name> is the name or key of the string
 * ☑ C<{UUID}> - Identifier (32 hexidecimal characters)
 * ☑ C<{URL:RMVSCM}> / C<{URL:WITHOUTSCHEME}>
 * ☑ C<{S:Name}> - Custom string where C<Name> is the name or key of the string
 * ☑ C<{UUID}> - Identifier (32 hexidecimal characters)
-* ☑ C<{HMACOTP}> - Generate an HMAC-based one-time password
+* ☑ C<{HMACOTP}> - Generate an HMAC-based one-time password (its counter B<will> be incremented)
 * ☑ C<{TIMEOTP}> - Generate a time-based one-time password
 * ☑ C<{GROUP_NOTES}> - Notes of the parent group
 * ☑ C<{GROUP_PATH}> - Full path of the parent group
 * ☑ C<{TIMEOTP}> - Generate a time-based one-time password
 * ☑ C<{GROUP_NOTES}> - Notes of the parent group
 * ☑ C<{GROUP_PATH}> - Full path of the parent group
@@ -956,7 +962,7 @@ C<{NUMPAD4}>, C<{NUMPAD5}>, C<{NUMPAD6}>, C<{NUMPAD7}>, C<{NUMPAD8}>, C<{NUMPAD9
 * ☒ C<{CLIPBOARD}>
 * ☒ C<{CMD:/CommandLine/Options/}>
 * ☑ C<{C:Comment}> - Comments are simply replaced by nothing
 * ☒ C<{CLIPBOARD}>
 * ☒ C<{CMD:/CommandLine/Options/}>
 * ☑ C<{C:Comment}> - Comments are simply replaced by nothing
-* ☑ C<{ENV:} and C<%ENV%> - Environment variables
+* ☑ C<{ENV:}> and C<%ENV%> - Environment variables
 * ☒ C<{GROUP_SEL_NOTES}>
 * ☒ C<{GROUP_SEL_PATH}>
 * ☒ C<{GROUP_SEL}>
 * ☒ C<{GROUP_SEL_NOTES}>
 * ☒ C<{GROUP_SEL_PATH}>
 * ☒ C<{GROUP_SEL}>
@@ -990,7 +996,7 @@ strings or auto-complete key sequences.
 
 If the name of the placeholder ends in a colon, then it is expected to receive an argument. During expansion,
 everything after the colon and before the end of the placeholder is passed to your placeholder handler
 
 If the name of the placeholder ends in a colon, then it is expected to receive an argument. During expansion,
 everything after the colon and before the end of the placeholder is passed to your placeholder handler
-subroutine. So if the placeholder is C<{MY_PLACEHOLDER:whatever}>, C<$arg> will have the value C<whatever>.
+subroutine. So if the placeholder is C<{MY_PLACEHOLDER:whatever}>, C<$arg> will have the value B<whatever>.
 
 An argument is required for placeholders than take one. I.e. The placeholder handler won't be called if there
 is no argument. If you want a placeholder to support an optional argument, you'll need to set the placeholder
 
 An argument is required for placeholders than take one. I.e. The placeholder handler won't be called if there
 is no argument. If you want a placeholder to support an optional argument, you'll need to set the placeholder
This page took 0.027405 seconds and 4 git commands to generate.