aboutsummaryrefslogtreecommitdiffstats
path: root/worker/notmuch/worker.go
diff options
context:
space:
mode:
authorJason Cox <me@jasoncarloscox.com>2024-02-23 11:40:17 -0500
committerRobin Jarry <robin@jarry.cc>2024-04-02 22:22:28 +0200
commit1ce82f50d0981a9ee047e75d94c7ab496070bd4a (patch)
tree72d835ad5da26eea6d5a6acbdd916640b75f1a4e /worker/notmuch/worker.go
parent54a72f83035bdf710368846e55a5b003ccab66cd (diff)
downloadaerc-1ce82f50d0981a9ee047e75d94c7ab496070bd4a.tar.gz
notmuch: add strategies for multi-file messages
A single notmuch message can represent multiple files. As a result, file-based operations like move, copy, and delete can be ambiguous. Add a new account config option, multi-file-strategy, to tell aerc how to handle these ambiguous cases. Also add options to relevant commands to set the multi-file strategy on a per-invocation basis. If no multi-file strategy is set, refuse to take file-based actions on multi-file messages. This default behavior is mostly the same as aerc's previous behavior, but a bit stricter in some cases which previously tried to be smart about multi-file operations (e.g., move and delete). Applying multi-file strategies to cross-account copy and move operations is not implemented. These operations will proceed as they have in the past -- aerc will copy/move a single file. However, for cross-account move operations, aerc will refuse to delete multiple files to prevent data loss as not all of the files are added to the destination account. See the changes to aerc-notmuch(5) for details on the currently supported multi-file strategies. Changelog-added: Tell aerc how to handle file-based operations on multi-file notmuch messages with the account config option `multi-file-strategy` and the `-m` flag to `:archive`, `:copy`, `:delete`, and `:move`. Signed-off-by: Jason Cox <me@jasoncarloscox.com> Tested-by: Maarten Aertsen <maarten@nlnetlabs.nl> Acked-by: Robin Jarry <robin@jarry.cc>
Diffstat (limited to 'worker/notmuch/worker.go')
-rw-r--r--worker/notmuch/worker.go62
1 files changed, 34 insertions, 28 deletions
diff --git a/worker/notmuch/worker.go b/worker/notmuch/worker.go
index aa2da391..fe41446e 100644
--- a/worker/notmuch/worker.go
+++ b/worker/notmuch/worker.go
@@ -55,6 +55,7 @@ type worker struct {
headers []string
headersExclude []string
state uint64
+ mfs types.MultiFileStrategy
}
// NewWorker creates a new notmuch worker with the provided worker.
@@ -243,6 +244,16 @@ func (w *worker) handleConfigure(msg *types.Configure) error {
w.headers = msg.Config.Headers
w.headersExclude = msg.Config.HeadersExclude
+ mfs := msg.Config.Params["multi-file-strategy"]
+ if mfs != "" {
+ w.mfs, ok = types.StrToStrategy[mfs]
+ if !ok {
+ return fmt.Errorf("invalid multi-file strategy %s", mfs)
+ }
+ } else {
+ w.mfs = types.Refuse
+ }
+
return nil
}
@@ -755,17 +766,12 @@ func (w *worker) handleDeleteMessages(msg *types.DeleteMessages) error {
var deleted []uint32
- // With notmuch, two identical files can be referenced under
- // the same index key, even if they exist in two different
- // folders. So in order to remove the message from the right
- // maildir folder we need to pass a hint to Remove() so it
- // can purge the right file.
folders, _ := w.store.FolderMap()
- path, ok := folders[w.currentQueryName]
- if !ok {
- w.err(msg, fmt.Errorf("Can only delete file from a maildir folder"))
- w.done(msg)
- return nil
+ curDir := folders[w.currentQueryName]
+
+ mfs := w.mfs
+ if msg.MultiFileStrategy != nil {
+ mfs = *msg.MultiFileStrategy
}
for _, uid := range msg.Uids {
@@ -775,7 +781,7 @@ func (w *worker) handleDeleteMessages(msg *types.DeleteMessages) error {
w.err(msg, err)
continue
}
- if err := m.Remove(path); err != nil {
+ if err := m.Remove(curDir, mfs); err != nil {
w.w.Errorf("could not remove message: %v", err)
w.err(msg, err)
continue
@@ -804,13 +810,20 @@ func (w *worker) handleCopyMessages(msg *types.CopyMessages) error {
return fmt.Errorf("Can only copy file to a maildir folder")
}
+ curDir := folders[w.currentQueryName]
+
+ mfs := w.mfs
+ if msg.MultiFileStrategy != nil {
+ mfs = *msg.MultiFileStrategy
+ }
+
for _, uid := range msg.Uids {
m, err := w.msgFromUid(uid)
if err != nil {
w.w.Errorf("could not get message: %v", err)
return err
}
- if err := m.Copy(dest); err != nil {
+ if err := m.Copy(curDir, dest, mfs); err != nil {
w.w.Errorf("could not copy message: %v", err)
return err
}
@@ -839,6 +852,13 @@ func (w *worker) handleMoveMessages(msg *types.MoveMessages) error {
return fmt.Errorf("Can only move file to a maildir folder")
}
+ curDir := folders[w.currentQueryName]
+
+ mfs := w.mfs
+ if msg.MultiFileStrategy != nil {
+ mfs = *msg.MultiFileStrategy
+ }
+
var err error
for _, uid := range msg.Uids {
m, err := w.msgFromUid(uid)
@@ -846,22 +866,8 @@ func (w *worker) handleMoveMessages(msg *types.MoveMessages) error {
w.w.Errorf("could not get message: %v", err)
break
}
- filenames, err := m.db.MsgFilenames(m.key)
- if err != nil {
- return err
- }
- // In the future, it'd be nice if we could overload move with
- // the possibility to affect some or all of the files
- // corresponding to a message.
- if len(filenames) > 1 {
- return fmt.Errorf("Cannot move: message %d has multiple files", m.uid)
- }
- source, key := parseFilename(filenames[0])
- if key == "" {
- return fmt.Errorf("failed to parse message filename: %s", filenames[0])
- }
- if err := m.Move(source, dest); err != nil {
- w.w.Errorf("could not copy message: %v", err)
+ if err := m.Move(curDir, dest, mfs); err != nil {
+ w.w.Errorf("could not move message: %v", err)
break
}
moved = append(moved, uid)