imap: Remove FetchMessageBodyPart.{Encoding,Charset}

Fixes https://todo.sr.ht/~sircmpwn/aerc2/352 exactly as suggested by emersion.
This commit is contained in:
Reto Brunner 2020-05-16 20:03:42 +02:00
parent ea2646fc03
commit bae678e8f2
3 changed files with 113 additions and 125 deletions

View file

@ -138,21 +138,10 @@ func (store *MessageStore) FetchFull(uids []uint32, cb func(*types.FullMessage))
func (store *MessageStore) FetchBodyPart( func (store *MessageStore) FetchBodyPart(
uid uint32, parent *models.BodyStructure, part []int, cb func(io.Reader)) { uid uint32, parent *models.BodyStructure, part []int, cb func(io.Reader)) {
partbs, err := parent.PartAtIndex(part)
if err != nil {
store.worker.Logger.Printf("FetchBodyPart: %v\n", err)
}
var charset string
var ok bool
if charset, ok = partbs.Params["charset"]; !ok {
charset = ""
}
store.worker.PostAction(&types.FetchMessageBodyPart{ store.worker.PostAction(&types.FetchMessageBodyPart{
Uid: uid, Uid: uid,
Part: part, Part: part,
Encoding: partbs.Encoding,
Charset: charset,
}, func(resp types.WorkerMessage) { }, func(resp types.WorkerMessage) {
msg, ok := resp.(*types.MessageBodyPart) msg, ok := resp.(*types.MessageBodyPart)
if !ok { if !ok {

View file

@ -2,11 +2,7 @@ package imap
import ( import (
"bufio" "bufio"
"encoding/base64"
"fmt" "fmt"
"io"
"mime/quotedprintable"
"strings"
"github.com/emersion/go-imap" "github.com/emersion/go-imap"
"github.com/emersion/go-message" "github.com/emersion/go-message"
@ -37,21 +33,90 @@ func (imapw *IMAPWorker) handleFetchMessageHeaders(
imap.FetchUid, imap.FetchUid,
section.FetchItem(), section.FetchItem(),
} }
imapw.handleFetchMessages(msg, msg.Uids, items, section) imapw.handleFetchMessages(msg, msg.Uids, items,
func(_msg *imap.Message) error {
reader := _msg.GetBody(section)
textprotoHeader, err := textproto.ReadHeader(bufio.NewReader(reader))
if err != nil {
return fmt.Errorf("could not read header: %v", err)
}
header := &mail.Header{message.Header{textprotoHeader}}
imapw.worker.PostMessage(&types.MessageInfo{
Message: types.RespondTo(msg),
Info: &models.MessageInfo{
BodyStructure: translateBodyStructure(_msg.BodyStructure),
Envelope: translateEnvelope(_msg.Envelope),
Flags: translateImapFlags(_msg.Flags),
InternalDate: _msg.InternalDate,
RFC822Headers: header,
Uid: _msg.Uid,
},
}, nil)
return nil
})
} }
func (imapw *IMAPWorker) handleFetchMessageBodyPart( func (imapw *IMAPWorker) handleFetchMessageBodyPart(
msg *types.FetchMessageBodyPart) { msg *types.FetchMessageBodyPart) {
imapw.worker.Logger.Printf("Fetching message part") imapw.worker.Logger.Printf("Fetching message part")
section := &imap.BodySectionName{}
section.Path = msg.Part var partHeaderSection imap.BodySectionName
items := []imap.FetchItem{ partHeaderSection.Peek = true
imap.FetchFlags, if len(msg.Part) > 0 {
imap.FetchUid, partHeaderSection.Specifier = imap.MIMESpecifier
section.FetchItem(), } else {
partHeaderSection.Specifier = imap.HeaderSpecifier
} }
imapw.handleFetchMessages(msg, []uint32{msg.Uid}, items, section) partHeaderSection.Path = msg.Part
var partBodySection imap.BodySectionName
if len(msg.Part) > 0 {
partBodySection.Specifier = imap.EntireSpecifier
} else {
partBodySection.Specifier = imap.TextSpecifier
}
partBodySection.Path = msg.Part
items := []imap.FetchItem{
imap.FetchEnvelope,
imap.FetchUid,
imap.FetchBodyStructure,
imap.FetchFlags,
partHeaderSection.FetchItem(),
partBodySection.FetchItem(),
}
imapw.handleFetchMessages(msg, []uint32{msg.Uid}, items,
func(_msg *imap.Message) error {
headerReader := bufio.NewReader(_msg.GetBody(&partHeaderSection))
h, err := textproto.ReadHeader(headerReader)
if err != nil {
return fmt.Errorf("failed to read part header: %v", err)
}
part, err := message.New(message.Header{h},
_msg.GetBody(&partBodySection))
if err != nil {
return fmt.Errorf("failed to create message reader: %v", err)
}
imapw.worker.PostMessage(&types.MessageBodyPart{
Message: types.RespondTo(msg),
Part: &models.MessageBodyPart{
Reader: part.Body,
Uid: _msg.Uid,
},
}, nil)
// Update flags (to mark message as read)
imapw.worker.PostMessage(&types.MessageInfo{
Message: types.RespondTo(msg),
Info: &models.MessageInfo{
Flags: translateImapFlags(_msg.Flags),
Uid: _msg.Uid,
},
}, nil)
return nil
})
} }
func (imapw *IMAPWorker) handleFetchFullMessages( func (imapw *IMAPWorker) handleFetchFullMessages(
@ -65,85 +130,53 @@ func (imapw *IMAPWorker) handleFetchFullMessages(
imap.FetchUid, imap.FetchUid,
section.FetchItem(), section.FetchItem(),
} }
imapw.handleFetchMessages(msg, msg.Uids, items, section) imapw.handleFetchMessages(msg, msg.Uids, items,
func(_msg *imap.Message) error {
r := _msg.GetBody(section)
if r == nil {
return fmt.Errorf("could not get section %#v", section)
}
imapw.worker.PostMessage(&types.FullMessage{
Message: types.RespondTo(msg),
Content: &models.FullMessage{
Reader: bufio.NewReader(r),
Uid: _msg.Uid,
},
}, nil)
// Update flags (to mark message as read)
imapw.worker.PostMessage(&types.MessageInfo{
Message: types.RespondTo(msg),
Info: &models.MessageInfo{
Flags: translateImapFlags(_msg.Flags),
Uid: _msg.Uid,
},
}, nil)
return nil
})
} }
func (imapw *IMAPWorker) handleFetchMessages( func (imapw *IMAPWorker) handleFetchMessages(
msg types.WorkerMessage, uids []uint32, items []imap.FetchItem, msg types.WorkerMessage, uids []uint32, items []imap.FetchItem,
section *imap.BodySectionName) { procFunc func(*imap.Message) error) {
messages := make(chan *imap.Message) messages := make(chan *imap.Message)
done := make(chan error) done := make(chan error)
go func() { go func() {
var reterr error
for _msg := range messages { for _msg := range messages {
imapw.seqMap[_msg.SeqNum-1] = _msg.Uid imapw.seqMap[_msg.SeqNum-1] = _msg.Uid
switch msg := msg.(type) { err := procFunc(_msg)
case *types.FetchMessageHeaders: if err != nil {
reader := _msg.GetBody(section) if reterr == nil {
textprotoHeader, err := textproto.ReadHeader(bufio.NewReader(reader)) reterr = err
if err != nil {
done <- fmt.Errorf("could not read header: %v", err)
return
} }
header := &mail.Header{message.Header{textprotoHeader}} // drain the channel upon error
imapw.worker.PostMessage(&types.MessageInfo{ for range messages {
Message: types.RespondTo(msg),
Info: &models.MessageInfo{
BodyStructure: translateBodyStructure(_msg.BodyStructure),
Envelope: translateEnvelope(_msg.Envelope),
Flags: translateImapFlags(_msg.Flags),
InternalDate: _msg.InternalDate,
RFC822Headers: header,
Uid: _msg.Uid,
},
}, nil)
case *types.FetchFullMessages:
r := _msg.GetBody(section)
if r == nil {
done <- fmt.Errorf("could not get section %#v", section)
return
} }
imapw.worker.PostMessage(&types.FullMessage{
Message: types.RespondTo(msg),
Content: &models.FullMessage{
Reader: bufio.NewReader(r),
Uid: _msg.Uid,
},
}, nil)
// Update flags (to mark message as read)
imapw.worker.PostMessage(&types.MessageInfo{
Message: types.RespondTo(msg),
Info: &models.MessageInfo{
Flags: translateImapFlags(_msg.Flags),
Uid: _msg.Uid,
},
}, nil)
case *types.FetchMessageBodyPart:
reader, err := getDecodedPart(msg, _msg, section)
if err != nil {
done <- err
return
}
imapw.worker.PostMessage(&types.MessageBodyPart{
Message: types.RespondTo(msg),
Part: &models.MessageBodyPart{
Reader: reader,
Uid: _msg.Uid,
},
}, nil)
// Update flags (to mark message as read)
imapw.worker.PostMessage(&types.MessageInfo{
Message: types.RespondTo(msg),
Info: &models.MessageInfo{
Flags: translateImapFlags(_msg.Flags),
Uid: _msg.Uid,
},
}, nil)
} }
} }
done <- nil done <- reterr
}() }()
emitErr := func(err error) { emitErr := func(err error) {
@ -165,35 +198,3 @@ func (imapw *IMAPWorker) handleFetchMessages(
imapw.worker.PostMessage( imapw.worker.PostMessage(
&types.Done{types.RespondTo(msg)}, nil) &types.Done{types.RespondTo(msg)}, nil)
} }
func getDecodedPart(task *types.FetchMessageBodyPart, msg *imap.Message,
section *imap.BodySectionName) (io.Reader, error) {
var r io.Reader
var err error
r = msg.GetBody(section)
if r == nil {
return nil, nil
}
r = encodingReader(task.Encoding, r)
if task.Charset != "" {
r, err = message.CharsetReader(task.Charset, r)
}
if err != nil {
return nil, err
}
return r, err
}
func encodingReader(encoding string, r io.Reader) io.Reader {
reader := r
// email parts are encoded as 7bit (plaintext), quoted-printable, or base64
if strings.EqualFold(encoding, "base64") {
reader = base64.NewDecoder(base64.StdEncoding, r)
} else if strings.EqualFold(encoding, "quoted-printable") {
reader = quotedprintable.NewReader(r)
}
return reader
}

View file

@ -104,10 +104,8 @@ type FetchFullMessages struct {
type FetchMessageBodyPart struct { type FetchMessageBodyPart struct {
Message Message
Uid uint32 Uid uint32
Part []int Part []int
Encoding string
Charset string
} }
type DeleteMessages struct { type DeleteMessages struct {