From bae678e8f20605b64596c9e976623f2a37247adb Mon Sep 17 00:00:00 2001 From: Reto Brunner Date: Sat, 16 May 2020 20:03:42 +0200 Subject: [PATCH] imap: Remove FetchMessageBodyPart.{Encoding,Charset} Fixes https://todo.sr.ht/~sircmpwn/aerc2/352 exactly as suggested by emersion. --- lib/msgstore.go | 15 +-- worker/imap/fetch.go | 217 ++++++++++++++++++++------------------- worker/types/messages.go | 6 +- 3 files changed, 113 insertions(+), 125 deletions(-) diff --git a/lib/msgstore.go b/lib/msgstore.go index b3a86b3..3fe26cb 100644 --- a/lib/msgstore.go +++ b/lib/msgstore.go @@ -138,21 +138,10 @@ func (store *MessageStore) FetchFull(uids []uint32, cb func(*types.FullMessage)) func (store *MessageStore) FetchBodyPart( 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{ - Uid: uid, - Part: part, - Encoding: partbs.Encoding, - Charset: charset, + Uid: uid, + Part: part, }, func(resp types.WorkerMessage) { msg, ok := resp.(*types.MessageBodyPart) if !ok { diff --git a/worker/imap/fetch.go b/worker/imap/fetch.go index 9591ced..def0da8 100644 --- a/worker/imap/fetch.go +++ b/worker/imap/fetch.go @@ -2,11 +2,7 @@ package imap import ( "bufio" - "encoding/base64" "fmt" - "io" - "mime/quotedprintable" - "strings" "github.com/emersion/go-imap" "github.com/emersion/go-message" @@ -37,21 +33,90 @@ func (imapw *IMAPWorker) handleFetchMessageHeaders( imap.FetchUid, 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( msg *types.FetchMessageBodyPart) { imapw.worker.Logger.Printf("Fetching message part") - section := &imap.BodySectionName{} - section.Path = msg.Part - items := []imap.FetchItem{ - imap.FetchFlags, - imap.FetchUid, - section.FetchItem(), + + var partHeaderSection imap.BodySectionName + partHeaderSection.Peek = true + if len(msg.Part) > 0 { + partHeaderSection.Specifier = imap.MIMESpecifier + } 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( @@ -65,85 +130,53 @@ func (imapw *IMAPWorker) handleFetchFullMessages( imap.FetchUid, 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( msg types.WorkerMessage, uids []uint32, items []imap.FetchItem, - section *imap.BodySectionName) { + procFunc func(*imap.Message) error) { messages := make(chan *imap.Message) done := make(chan error) go func() { + var reterr error for _msg := range messages { imapw.seqMap[_msg.SeqNum-1] = _msg.Uid - switch msg := msg.(type) { - case *types.FetchMessageHeaders: - reader := _msg.GetBody(section) - textprotoHeader, err := textproto.ReadHeader(bufio.NewReader(reader)) - if err != nil { - done <- fmt.Errorf("could not read header: %v", err) - return + err := procFunc(_msg) + if err != nil { + if reterr == nil { + reterr = 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) - case *types.FetchFullMessages: - r := _msg.GetBody(section) - if r == nil { - done <- fmt.Errorf("could not get section %#v", section) - return + // drain the channel upon error + for range messages { } - - 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) { @@ -165,35 +198,3 @@ func (imapw *IMAPWorker) handleFetchMessages( imapw.worker.PostMessage( &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 -} diff --git a/worker/types/messages.go b/worker/types/messages.go index 6422ef5..f1ef36e 100644 --- a/worker/types/messages.go +++ b/worker/types/messages.go @@ -104,10 +104,8 @@ type FetchFullMessages struct { type FetchMessageBodyPart struct { Message - Uid uint32 - Part []int - Encoding string - Charset string + Uid uint32 + Part []int } type DeleteMessages struct {