about summary refs log tree commit diff stats
path: root/lib/messageview.go
diff options
context:
space:
mode:
authorDrew DeVault <sir@cmpwn.com>2020-03-03 16:20:07 -0500
committerDrew DeVault <sir@cmpwn.com>2020-03-03 16:49:52 -0500
commitf3158b36f1f210ff54febbe82b571c1379b30c98 (patch)
tree10cde839c9517609f55b8f1057b1cf84ac592632 /lib/messageview.go
parent89f1684ea4b5e680db7ff06a54b2d4e78212cd12 (diff)
downloadaerc-f3158b36f1f210ff54febbe82b571c1379b30c98.tar.gz
Initial support for PGP decryption & signatures
Diffstat (limited to 'lib/messageview.go')
-rw-r--r--lib/messageview.go128
1 files changed, 128 insertions, 0 deletions
diff --git a/lib/messageview.go b/lib/messageview.go
new file mode 100644
index 0000000..be3b90f
--- /dev/null
+++ b/lib/messageview.go
@@ -0,0 +1,128 @@
+package lib
+
+import (
+	"bytes"
+	"io"
+	"io/ioutil"
+
+	"github.com/emersion/go-message"
+	_ "github.com/emersion/go-message/charset"
+	"github.com/emersion/go-pgpmail"
+	"golang.org/x/crypto/openpgp"
+
+	"git.sr.ht/~sircmpwn/aerc/models"
+	"git.sr.ht/~sircmpwn/aerc/worker/lib"
+)
+
+// This is an abstraction for viewing a message with semi-transparent PGP
+// support.
+type MessageView interface {
+	// Returns the MessageInfo for this message
+	MessageInfo() *models.MessageInfo
+
+	// Returns the BodyStructure for this message
+	BodyStructure() *models.BodyStructure
+
+	// Returns the message store that this message was originally sourced from
+	Store() *MessageStore
+
+	// Fetches a specific body part for this message
+	FetchBodyPart(parent *models.BodyStructure,
+		part []int, cb func(io.Reader))
+
+	PGPDetails() *openpgp.MessageDetails
+}
+
+func usePGP(info *models.BodyStructure) bool {
+	if info.MIMEType == "application" {
+		if info.MIMESubType == "pgp-encrypted" ||
+			info.MIMESubType == "pgp-signature" {
+
+			return true
+		}
+	}
+	for _, part := range info.Parts {
+		if usePGP(part) {
+			return true
+		}
+	}
+	return false
+}
+
+type MessageStoreView struct {
+	messageInfo   *models.MessageInfo
+	messageStore  *MessageStore
+	message       []byte
+	details       *openpgp.MessageDetails
+	bodyStructure *models.BodyStructure
+}
+
+func NewMessageStoreView(messageInfo *models.MessageInfo,
+	store *MessageStore, decryptKeys openpgp.PromptFunction,
+	cb func(MessageView)) {
+
+	msv := &MessageStoreView{messageInfo, store,
+		nil, nil, messageInfo.BodyStructure}
+
+	if usePGP(messageInfo.BodyStructure) {
+		store.FetchFull([]uint32{messageInfo.Uid}, func(reader io.Reader) {
+			pgpReader, err := pgpmail.Read(reader, Keyring, decryptKeys, nil)
+			if err != nil {
+				panic(err)
+			}
+			msv.message, err = ioutil.ReadAll(pgpReader.MessageDetails.UnverifiedBody)
+			if err != nil {
+				panic(err)
+			}
+			decrypted, err := message.Read(bytes.NewBuffer(msv.message))
+			if err != nil {
+				panic(err)
+			}
+			bs, err := lib.ParseEntityStructure(decrypted)
+			if err != nil {
+				panic(err)
+			}
+			msv.bodyStructure = bs
+			msv.details = pgpReader.MessageDetails
+			cb(msv)
+		})
+	} else {
+		cb(msv)
+	}
+}
+
+func (msv *MessageStoreView) MessageInfo() *models.MessageInfo {
+	return msv.messageInfo
+}
+
+func (msv *MessageStoreView) BodyStructure() *models.BodyStructure {
+	return msv.bodyStructure
+}
+
+func (msv *MessageStoreView) Store() *MessageStore {
+	return msv.messageStore
+}
+
+func (msv *MessageStoreView) PGPDetails() *openpgp.MessageDetails {
+	return msv.details
+}
+
+func (msv *MessageStoreView) FetchBodyPart(parent *models.BodyStructure,
+	part []int, cb func(io.Reader)) {
+
+	if msv.message == nil {
+		msv.messageStore.FetchBodyPart(msv.messageInfo.Uid, parent, part, cb)
+		return
+	}
+
+	buf := bytes.NewBuffer(msv.message)
+	msg, err := message.Read(buf)
+	if err != nil {
+		panic(err)
+	}
+	reader, err := lib.FetchEntityPartReader(msg, part)
+	if err != nil {
+		panic(err)
+	}
+	cb(reader)
+}