use Crypt::PRNG qw(random_bytes);
use Devel::GlobalDestruction;
-use File::KDBX::Constants qw(:all);
+use File::KDBX::Constants qw(:all :icon);
use File::KDBX::Error;
-use File::KDBX::Iterator;
use File::KDBX::Safe;
use File::KDBX::Util qw(:class :coercion :empty :search :uuid erase simple_expression_query snakify);
use Hash::Util::FieldHash qw(fieldhashes);
my $base = $lineage[-1] or return [];
my $uuid = $object->uuid;
- return \@lineage if any { $_->uuid eq $uuid } @{$base->groups || []}, @{$base->entries || []};
+ return \@lineage if any { $_->uuid eq $uuid } @{$base->groups}, @{$base->entries};
- for my $subgroup (@{$base->groups || []}) {
+ for my $subgroup (@{$base->groups}) {
my $result = $self->_trace_lineage($object, @lineage, $subgroup);
return $result if $result;
}
}
+=method recycle_bin
+
+ $group = $kdbx->recycle_bin;
+ $kdbx->recycle_bin($group);
+
+Get or set the recycle bin group. Returns C<undef> if there is no recycle bin and L</recycle_bin_enabled> is
+false, otherwise the current recycle bin or an autovivified recycle bin group is returned.
+
+=cut
+
+sub recycle_bin {
+ my $self = shift;
+ if (my $group = shift) {
+ $self->recycle_bin_uuid($group->uuid);
+ return $group;
+ }
+ my $group;
+ my $uuid = $self->recycle_bin_uuid;
+ $group = $self->groups->grep(uuid => $uuid)->next if $uuid ne UUID_NULL;
+ if (!$group && $self->recycle_bin_enabled) {
+ $group = $self->add_group(
+ name => 'Recycle Bin',
+ icon_id => ICON_TRASHCAN_FULL,
+ enable_auto_type => false,
+ enable_searching => false,
+ );
+ $self->recycle_bin_uuid($group->uuid);
+ }
+ return $group;
+}
+
+=method entry_templates
+
+ $group = $kdbx->entry_templates;
+ $kdbx->entry_templates($group);
+
+Get or set the entry templates group. May return C<undef> if unset.
+
+=cut
+
+sub entry_templates {
+ my $self = shift;
+ if (my $group = shift) {
+ $self->entry_templates_group($group->uuid);
+ return $group;
+ }
+ my $uuid = $self->entry_templates_group;
+ return if $uuid eq UUID_NULL;
+ return $self->groups->grep(uuid => $uuid)->next;
+}
+
+=method last_selected
+
+ $group = $kdbx->last_selected;
+ $kdbx->last_selected($group);
+
+Get or set the last selected group. May return C<undef> if unset.
+
+=cut
+
+sub last_selected {
+ my $self = shift;
+ if (my $group = shift) {
+ $self->last_selected_group($group->uuid);
+ return $group;
+ }
+ my $uuid = $self->last_selected_group;
+ return if $uuid eq UUID_NULL;
+ return $self->groups->grep(uuid => $uuid)->next;
+}
+
+=method last_top_visible
+
+ $group = $kdbx->last_top_visible;
+ $kdbx->last_top_visible($group);
+
+Get or set the last top visible group. May return C<undef> if unset.
+
+=cut
+
+sub last_top_visible {
+ my $self = shift;
+ if (my $group = shift) {
+ $self->last_top_visible_group($group->uuid);
+ return $group;
+ }
+ my $uuid = $self->last_top_visible_group;
+ return if $uuid eq UUID_NULL;
+ return $self->groups->grep(uuid => $uuid)->next;
+}
+
##############################################################################
=method add_group
sub groups {
my $self = shift;
my %args = @_ % 2 == 0 ? @_ : (base => shift, @_);
- my $base = $args{base} // $self->root;
-
- my @groups = ($args{inclusive} // 1) ? $base : @{$base->groups};
- my $algo = lc($args{algorithm} || 'ids');
-
- if ($algo eq 'dfs') {
- my %visited;
- return File::KDBX::Iterator->new(sub {
- my $next = shift @groups or return;
- if (!$visited{Hash::Util::FieldHash::id($next)}++) {
- while (my @children = @{$next->groups}) {
- unshift @groups, @children, $next;
- $next = shift @groups;
- $visited{Hash::Util::FieldHash::id($next)}++;
- }
- }
- $next;
- });
- }
- elsif ($algo eq 'bfs') {
- return File::KDBX::Iterator->new(sub {
- my $next = shift @groups or return;
- push @groups, @{$next->groups};
- $next;
- });
- }
- return File::KDBX::Iterator->new(sub {
- my $next = shift @groups or return;
- unshift @groups, @{$next->groups};
- $next;
- });
+ my $base = delete $args{base} // $self->root;
+
+ return $base->groups_deeply(%args);
}
##############################################################################
sub entries {
my $self = shift;
my %args = @_ % 2 == 0 ? @_ : (base => shift, @_);
+ my $base = delete $args{base} // $self->root;
- my $searching = $args{searching};
- my $auto_type = $args{auto_type};
- my $history = $args{history};
-
- my $groups = $self->groups(%args);
- my @entries;
-
- return File::KDBX::Iterator->new(sub {
- if (!@entries) {
- while (my $group = $groups->next) {
- next if $searching && !$group->effective_enable_searching;
- next if $auto_type && !$group->effective_enable_auto_type;
- @entries = @{$group->entries};
- @entries = grep { $_->auto_type->{enabled} } @entries if $auto_type;
- @entries = map { ($_, @{$_->history}) } @entries if $history;
- last if @entries;
- }
- }
- shift @entries;
- });
+ return $base->entries_deeply(%args);
}
##############################################################################
=method objects
- \&iterator = $kdbx->entries(%options);
- \&iterator = $kdbx->entries($base_group, %options);
+ \&iterator = $kdbx->objects(%options);
+ \&iterator = $kdbx->objects($base_group, %options);
Get an iterator over I<objects> within a database. Groups and entries are considered objects, so this is
essentially a combination of L</groups> and L</entries>. This won't often be useful, but it can be convenient
sub objects {
my $self = shift;
my %args = @_ % 2 == 0 ? @_ : (base => shift, @_);
+ my $base = delete $args{base} // $self->root;
- my $searching = $args{searching};
- my $auto_type = $args{auto_type};
- my $history = $args{history};
-
- my $groups = $self->groups(%args);
- my @entries;
-
- return File::KDBX::Iterator->new(sub {
- if (!@entries) {
- while (my $group = $groups->next) {
- next if $searching && !$group->effective_enable_searching;
- next if $auto_type && !$group->effective_enable_auto_type;
- @entries = @{$group->entries};
- @entries = grep { $_->auto_type->{enabled} } @entries if $auto_type;
- @entries = map { ($_, @{$_->history}) } @entries if $history;
- return $group;
- }
- }
- shift @entries;
- });
+ return $base->objects_deeply(%args);
}
sub __iter__ { $_[0]->objects }