aerc/lib/messageview.go
Koni Marti d3b62dd3b0 view: add peek flag and propagate
Add a peek flag -p to the view commands to open the message viewer
without setting the "seen" flag. If the flag is set, it would ignore the
"auto-mark-read" config.

The SetSeen flag will be propagated in case the message viewer moves on
to other messages, i.e. with the delete or archive commands.

Signed-off-by: Koni Marti <koni.marti@gmail.com>
Acked-by: Robin Jarry <robin@jarry.cc>
2022-10-04 09:44:04 +02:00

147 lines
3.2 KiB
Go

package lib
import (
"bytes"
"io"
"github.com/ProtonMail/go-crypto/openpgp"
_ "github.com/emersion/go-message/charset"
"git.sr.ht/~rjarry/aerc/lib/crypto"
"git.sr.ht/~rjarry/aerc/models"
"git.sr.ht/~rjarry/aerc/worker/lib"
"git.sr.ht/~rjarry/aerc/worker/types"
)
// 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(part []int, cb func(io.Reader))
MessageDetails() *models.MessageDetails
// SeenFlagSet returns true if the "seen" flag has been set
SeenFlagSet() bool
}
func usePGP(info *models.BodyStructure) bool {
if info == nil {
return false
}
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 *models.MessageDetails
bodyStructure *models.BodyStructure
setSeen bool
}
func NewMessageStoreView(messageInfo *models.MessageInfo, setSeen bool,
store *MessageStore, pgp crypto.Provider, decryptKeys openpgp.PromptFunction,
cb func(MessageView, error),
) {
msv := &MessageStoreView{
messageInfo, store,
nil, nil, messageInfo.BodyStructure,
setSeen,
}
if usePGP(messageInfo.BodyStructure) {
store.FetchFull([]uint32{messageInfo.Uid}, func(fm *types.FullMessage) {
reader := lib.NewCRLFReader(fm.Content.Reader)
md, err := pgp.Decrypt(reader, decryptKeys)
if err != nil {
cb(nil, err)
return
}
msv.message, err = io.ReadAll(md.Body)
if err != nil {
cb(nil, err)
return
}
decrypted, err := lib.ReadMessage(bytes.NewBuffer(msv.message))
if err != nil {
cb(nil, err)
return
}
bs, err := lib.ParseEntityStructure(decrypted)
if err != nil {
cb(nil, err)
return
}
msv.bodyStructure = bs
msv.details = md
cb(msv, nil)
})
} else {
cb(msv, nil)
}
if setSeen {
store.Flag([]uint32{messageInfo.Uid}, models.SeenFlag, true, nil)
}
}
func (msv *MessageStoreView) SeenFlagSet() bool {
return msv.setSeen
}
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) MessageDetails() *models.MessageDetails {
return msv.details
}
func (msv *MessageStoreView) FetchBodyPart(part []int, cb func(io.Reader)) {
if msv.message == nil {
msv.messageStore.FetchBodyPart(msv.messageInfo.Uid, part, cb)
return
}
buf := bytes.NewBuffer(msv.message)
msg, err := lib.ReadMessage(buf)
if err != nil {
panic(err)
}
reader, err := lib.FetchEntityPartReader(msg, part)
if err != nil {
panic(err)
}
cb(reader)
}