2020-03-03 22:20:07 +01:00
|
|
|
package lib
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
|
|
|
|
2021-12-30 10:25:07 +01:00
|
|
|
"github.com/ProtonMail/go-crypto/openpgp"
|
2020-03-03 22:20:07 +01:00
|
|
|
"github.com/emersion/go-message"
|
|
|
|
_ "github.com/emersion/go-message/charset"
|
|
|
|
|
2022-04-25 15:30:43 +02:00
|
|
|
"git.sr.ht/~rjarry/aerc/lib/crypto"
|
2021-11-05 10:19:46 +01:00
|
|
|
"git.sr.ht/~rjarry/aerc/models"
|
|
|
|
"git.sr.ht/~rjarry/aerc/worker/lib"
|
|
|
|
"git.sr.ht/~rjarry/aerc/worker/types"
|
2020-03-03 22:20:07 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
// 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
|
2020-05-17 11:44:38 +02:00
|
|
|
FetchBodyPart(part []int, cb func(io.Reader))
|
2020-03-03 22:20:07 +01:00
|
|
|
|
2022-04-25 15:30:43 +02:00
|
|
|
MessageDetails() *models.MessageDetails
|
2020-03-03 22:20:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func usePGP(info *models.BodyStructure) bool {
|
2021-10-28 14:59:54 +02:00
|
|
|
if info == nil {
|
|
|
|
return false
|
|
|
|
}
|
2020-03-03 22:20:07 +01:00
|
|
|
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
|
2022-04-25 15:30:43 +02:00
|
|
|
details *models.MessageDetails
|
2020-03-03 22:20:07 +01:00
|
|
|
bodyStructure *models.BodyStructure
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewMessageStoreView(messageInfo *models.MessageInfo,
|
2022-04-25 15:30:43 +02:00
|
|
|
store *MessageStore, pgp crypto.Provider, decryptKeys openpgp.PromptFunction,
|
2020-05-19 13:06:46 +02:00
|
|
|
cb func(MessageView, error)) {
|
2020-03-03 22:20:07 +01:00
|
|
|
|
|
|
|
msv := &MessageStoreView{messageInfo, store,
|
|
|
|
nil, nil, messageInfo.BodyStructure}
|
|
|
|
|
|
|
|
if usePGP(messageInfo.BodyStructure) {
|
2020-04-24 22:31:39 +02:00
|
|
|
store.FetchFull([]uint32{messageInfo.Uid}, func(fm *types.FullMessage) {
|
2022-04-22 13:38:41 +02:00
|
|
|
reader := lib.NewCRLFReader(fm.Content.Reader)
|
2022-04-25 15:30:43 +02:00
|
|
|
md, err := pgp.Decrypt(reader, decryptKeys)
|
2020-03-03 22:20:07 +01:00
|
|
|
if err != nil {
|
2020-05-19 13:06:46 +02:00
|
|
|
cb(nil, err)
|
|
|
|
return
|
2020-03-03 22:20:07 +01:00
|
|
|
}
|
2022-04-25 15:30:43 +02:00
|
|
|
msv.message, err = ioutil.ReadAll(md.Body)
|
2020-03-03 22:20:07 +01:00
|
|
|
if err != nil {
|
2020-05-19 13:06:46 +02:00
|
|
|
cb(nil, err)
|
|
|
|
return
|
2020-03-03 22:20:07 +01:00
|
|
|
}
|
|
|
|
decrypted, err := message.Read(bytes.NewBuffer(msv.message))
|
|
|
|
if err != nil {
|
2020-05-19 13:06:46 +02:00
|
|
|
cb(nil, err)
|
|
|
|
return
|
2020-03-03 22:20:07 +01:00
|
|
|
}
|
|
|
|
bs, err := lib.ParseEntityStructure(decrypted)
|
|
|
|
if err != nil {
|
2020-05-19 13:06:46 +02:00
|
|
|
cb(nil, err)
|
|
|
|
return
|
2020-03-03 22:20:07 +01:00
|
|
|
}
|
|
|
|
msv.bodyStructure = bs
|
2022-04-25 15:30:43 +02:00
|
|
|
msv.details = md
|
2020-05-19 13:06:46 +02:00
|
|
|
cb(msv, nil)
|
2020-03-03 22:20:07 +01:00
|
|
|
})
|
|
|
|
} else {
|
2020-05-19 13:06:46 +02:00
|
|
|
cb(msv, nil)
|
2020-03-03 22:20:07 +01:00
|
|
|
}
|
2020-07-05 16:29:52 +02:00
|
|
|
store.Flag([]uint32{messageInfo.Uid}, models.SeenFlag, true, nil)
|
2020-03-03 22:20:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2022-04-25 15:30:43 +02:00
|
|
|
func (msv *MessageStoreView) MessageDetails() *models.MessageDetails {
|
2020-03-03 22:20:07 +01:00
|
|
|
return msv.details
|
|
|
|
}
|
|
|
|
|
2020-05-17 11:44:38 +02:00
|
|
|
func (msv *MessageStoreView) FetchBodyPart(part []int, cb func(io.Reader)) {
|
2020-03-03 22:20:07 +01:00
|
|
|
|
|
|
|
if msv.message == nil {
|
2020-05-17 11:44:38 +02:00
|
|
|
msv.messageStore.FetchBodyPart(msv.messageInfo.Uid, part, cb)
|
2020-03-03 22:20:07 +01:00
|
|
|
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)
|
|
|
|
}
|