summary refs log tree commit diff stats
path: root/worker/notmuch/message.go
diff options
context:
space:
mode:
authorReto Brunner <reto@labrat.space>2019-08-05 09:16:09 +0200
committerDrew DeVault <sir@cmpwn.com>2019-08-08 10:10:34 +0900
commitc38ddf8d3069663b5ff6a0de36dba3e9efba912d (patch)
treef4ad4d39e1f8a4e841146a273930efcff4183ff9 /worker/notmuch/message.go
parent2485d509838013351672db50b2cb57880b7c3e1c (diff)
downloadaerc-c38ddf8d3069663b5ff6a0de36dba3e9efba912d.tar.gz
Add notmuch backend
This commit introduces the notmuch backend.
The backend is conditionally compiled in if the "notmuch" tag is provided.

Most of the message types are implemented, with the notable exceptions
of DeleteMessages as well as any copy / move / append type.
Reason being, that those aren't normally applicable in a notmuch based workflow.

Changes v2 --> v3, based on review comments
* Use account config for configuration
Diffstat (limited to 'worker/notmuch/message.go')
-rw-r--r--worker/notmuch/message.go123
1 files changed, 123 insertions, 0 deletions
diff --git a/worker/notmuch/message.go b/worker/notmuch/message.go
new file mode 100644
index 0000000..077fb92
--- /dev/null
+++ b/worker/notmuch/message.go
@@ -0,0 +1,123 @@
+//+build notmuch
+
+package notmuch
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+
+	"git.sr.ht/~sircmpwn/aerc/models"
+	"git.sr.ht/~sircmpwn/aerc/worker/lib"
+	"github.com/emersion/go-message"
+	_ "github.com/emersion/go-message/charset"
+	notmuch "github.com/zenhack/go.notmuch"
+)
+
+type Message struct {
+	uid uint32
+	key string
+	msg *notmuch.Message
+}
+
+// NewReader reads a message into memory and returns an io.Reader for it.
+func (m Message) NewReader() (io.Reader, error) {
+	f, err := os.Open(m.msg.Filename())
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+	b, err := ioutil.ReadAll(f)
+	if err != nil {
+		return nil, err
+	}
+	return bytes.NewReader(b), nil
+}
+
+// MessageInfo populates a models.MessageInfo struct for the message.
+func (m Message) MessageInfo() (*models.MessageInfo, error) {
+	return lib.MessageInfo(m)
+}
+
+// NewBodyPartReader creates a new io.Reader for the requested body part(s) of
+// the message.
+func (m Message) NewBodyPartReader(requestedParts []int) (io.Reader, error) {
+	f, err := os.Open(m.msg.Filename())
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+	msg, err := message.Read(f)
+	if err != nil {
+		return nil, fmt.Errorf("could not read message: %v", err)
+	}
+	return lib.FetchEntityPartReader(msg, requestedParts)
+}
+
+// MarkRead either adds or removes the maildir.FlagSeen flag from the message.
+func (m Message) MarkRead(seen bool) error {
+	haveUnread := false
+	for _, t := range m.tags() {
+		if t == "unread" {
+			haveUnread = true
+			break
+		}
+	}
+	if (haveUnread && !seen) || (!haveUnread && seen) {
+		// we already have the desired state
+		return nil
+	}
+
+	if haveUnread {
+		err := m.msg.RemoveTag("unread")
+		if err != nil {
+			return err
+		}
+		return nil
+	}
+
+	err := m.msg.AddTag("unread")
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+// tags returns the notmuch tags of a message
+func (m Message) tags() []string {
+	ts := m.msg.Tags()
+	var tags []string
+	var tag *notmuch.Tag
+	for ts.Next(&tag) {
+		tags = append(tags, tag.Value)
+	}
+	return tags
+}
+
+func (m Message) ModelFlags() ([]models.Flag, error) {
+	var flags []models.Flag
+	seen := true
+
+	for _, tag := range m.tags() {
+		switch tag {
+		case "replied":
+			flags = append(flags, models.AnsweredFlag)
+		case "flagged":
+			flags = append(flags, models.FlaggedFlag)
+		case "unread":
+			seen = false
+		default:
+			continue
+		}
+	}
+	if seen {
+		flags = append(flags, models.SeenFlag)
+	}
+	return flags, nil
+}
+
+func (m Message) UID() uint32 {
+	return m.uid
+}