1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
package keepass
import (
"bufio"
"context"
"fmt"
"io"
"os"
"path/filepath"
"sort"
"strings"
"codeberg.org/snonux/foostore/internal/store"
)
// Remove finds all indexes matching searchTerm and prompts before deleting.
func (b *Backend) Remove(ctx context.Context, searchTerm string, input io.Reader) error {
var indexes store.IndexSlice
if err := b.WalkIndexes(ctx, searchTerm, func(idx *store.Index) error {
indexes = append(indexes, idx)
return nil
}); err != nil {
return err
}
sort.Sort(indexes)
scanner := bufio.NewScanner(input)
for _, idx := range indexes {
if err := b.confirmAndRemove(idx, scanner); err != nil {
return err
}
}
return nil
}
// confirmAndRemove prompts the user for deletion confirmation and removes the
// entry (or attachment) on "y".
func (b *Backend) confirmAndRemove(idx *store.Index, scanner *bufio.Scanner) error {
for {
fmt.Print(idx.String())
fmt.Print("You really want to delete this? (y/n): ")
if !scanner.Scan() {
return nil
}
switch strings.TrimSpace(scanner.Text()) {
case "y":
return b.removeEntry(idx.Description)
case "n":
return nil
}
}
}
// removeEntry deletes the KeePass entry (or attachment) matching description
// and saves the DB.
//
// If description is a virtual attachment path ("Group/Title/file.bin"), the
// attachment is removed from the parent entry rather than removing the entry
// itself. For regular entries the full entry is deleted from its group.
func (b *Backend) removeEntry(description string) error {
// Detect virtual attachment paths: if the parent exists as a text entry but
// description itself does not, route to attachment removal.
if parentDesc, attachName, ok := b.isAttachmentPath(description); ok {
return b.removeAttachment(parentDesc, attachName)
}
return b.removeTextEntry(description)
}
// removeTextEntry deletes a regular (non-attachment) entry from its group.
func (b *Backend) removeTextEntry(description string) error {
groupPath, title, err := SplitDescriptionPath(description)
if err != nil {
return fmt.Errorf("keepass remove: %w", err)
}
g := EnsureGroup(b.root(), groupPath)
for i, e := range g.Entries {
if e.GetTitle() == title {
g.Entries = append(g.Entries[:i], g.Entries[i+1:]...)
return b.save()
}
}
return fmt.Errorf("keepass remove: entry %q not found", description)
}
// ShredAllExported securely deletes every regular file in cfg.ExportDir.
// Delegates to store.ShredFile for the actual destruction.
func (b *Backend) ShredAllExported(ctx context.Context) error {
entries, err := os.ReadDir(b.cfg.ExportDir)
if err != nil {
if os.IsNotExist(err) {
return nil
}
return fmt.Errorf("listing export dir: %w", err)
}
var lastErr error
for _, e := range entries {
if e.IsDir() {
continue
}
filePath := filepath.Join(b.cfg.ExportDir, e.Name())
if err := store.ShredFile(ctx, filePath); err != nil {
lastErr = err
}
}
return lastErr
}
|