aboutsummaryrefslogtreecommitdiffstats
path: root/worker/notmuch/message.go
diff options
context:
space:
mode:
authorJulian Pidancet <julian.pidancet@oracle.com>2022-10-26 22:29:04 +0200
committerRobin Jarry <robin@jarry.cc>2022-10-27 21:45:24 +0200
commitc7bfe4e490fb1fcf779edceb23c96301eb763b47 (patch)
tree27de60f42275414bb392a853166714d1c6b24672 /worker/notmuch/message.go
parentea10b329ddd18573fdb066a1a3293c839d839fbd (diff)
downloadaerc-c7bfe4e490fb1fcf779edceb23c96301eb763b47.tar.gz
notmuch: add maildir support
By associating the notmuch database with a maildir store, we can add the Copy/Move/Delete operations on messages to the notmuch backend. This change assumes that the notmuch database location is also the root of the maildir store. In a previous change, we added the ability to dynamically add and remove message files to the notmuch DB. This change uses this facility to synchronize the database with the filesystem operations on maildir files. While it's still possible to use the query-map file to create virtual folders from notmuch search queries, the sidebar is now loaded with the folders found in the maildir store. With notmuch, two identical but distinct message files can be indexed in the database with the same key. This change takes extra care of only deleting or removing message files from the maildir corresponding to the folder that is currently selected (if any). Implements: https://todo.sr.ht/~rjarry/aerc/88 Fixes: https://todo.sr.ht/~rjarry/aerc/73 Signed-off-by: Julian Pidancet <julian.pidancet@oracle.com> Acked-by: Robin Jarry <robin@jarry.cc> Acked-by: Tim Culverhouse <tim@timculverhouse.com>
Diffstat (limited to 'worker/notmuch/message.go')
-rw-r--r--worker/notmuch/message.go109
1 files changed, 109 insertions, 0 deletions
diff --git a/worker/notmuch/message.go b/worker/notmuch/message.go
index 59367fd1..8f8deffd 100644
--- a/worker/notmuch/message.go
+++ b/worker/notmuch/message.go
@@ -7,6 +7,10 @@ import (
"fmt"
"io"
"os"
+ "path/filepath"
+ "strings"
+
+ "github.com/emersion/go-maildir"
"git.sr.ht/~rjarry/aerc/models"
"git.sr.ht/~rjarry/aerc/worker/lib"
@@ -175,3 +179,108 @@ func (m *Message) RemoveTag(tag string) error {
func (m *Message) ModifyTags(add, remove []string) error {
return m.db.MsgModifyTags(m.key, add, remove)
}
+
+func (m *Message) Remove(dir maildir.Dir) error {
+ filenames, err := m.db.MsgFilenames(m.key)
+ if err != nil {
+ return err
+ }
+ for _, filename := range filenames {
+ if dirContains(dir, filename) {
+ err := m.db.DeleteMessage(filename)
+ if err != nil {
+ return err
+ }
+
+ if err := os.Remove(filename); err != nil {
+ return err
+ }
+
+ return nil
+ }
+ }
+
+ return fmt.Errorf("no matching message file found in %s", string(dir))
+}
+
+func (m *Message) Copy(target maildir.Dir) error {
+ filename, err := m.Filename()
+ if err != nil {
+ return err
+ }
+
+ source, key := parseFilename(filename)
+ if key == "" {
+ return fmt.Errorf("failed to parse message filename: %s", filename)
+ }
+
+ newKey, err := source.Copy(target, key)
+ if err != nil {
+ return err
+ }
+ newFilename, err := target.Filename(newKey)
+ if err != nil {
+ return err
+ }
+ _, err = m.db.IndexFile(newFilename)
+ return err
+}
+
+func (m *Message) Move(srcDir, destDir maildir.Dir) error {
+ var src string
+
+ filenames, err := m.db.MsgFilenames(m.key)
+ if err != nil {
+ return err
+ }
+ for _, filename := range filenames {
+ if dirContains(srcDir, filename) {
+ src = filename
+ break
+ }
+ }
+
+ if src == "" {
+ return fmt.Errorf("no matching message file found in %s", string(srcDir))
+ }
+
+ // Remove encoded UID information from the key to prevent sync issues
+ name := lib.StripUIDFromMessageFilename(filepath.Base(src))
+ dest := filepath.Join(string(destDir), "cur", name)
+
+ if err := m.db.DeleteMessage(src); err != nil {
+ return err
+ }
+
+ if err := os.Rename(src, dest); err != nil {
+ return err
+ }
+
+ _, err = m.db.IndexFile(dest)
+ return err
+}
+
+func parseFilename(filename string) (maildir.Dir, string) {
+ base := filepath.Base(filename)
+ dir := filepath.Dir(filename)
+ dir, curdir := filepath.Split(dir)
+ if curdir != "cur" {
+ return "", ""
+ }
+ split := strings.Split(base, ":")
+ if len(split) < 2 {
+ return maildir.Dir(dir), ""
+ }
+ key := split[0]
+ return maildir.Dir(dir), key
+}
+
+func dirContains(dir maildir.Dir, filename string) bool {
+ for _, sub := range []string{"cur", "new"} {
+ match, _ := filepath.Match(filepath.Join(string(dir), sub, "*"), filename)
+ if match {
+ return true
+ }
+ }
+ return false
+}