diff options
Diffstat (limited to 'internal/keepass/remove.go')
| -rw-r--r-- | internal/keepass/remove.go | 106 |
1 files changed, 106 insertions, 0 deletions
diff --git a/internal/keepass/remove.go b/internal/keepass/remove.go new file mode 100644 index 0000000..40bbc9f --- /dev/null +++ b/internal/keepass/remove.go @@ -0,0 +1,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 +} |
