about summary refs log tree commit diff stats
path: root/worker
diff options
context:
space:
mode:
Diffstat (limited to 'worker')
-rw-r--r--worker/imap/fetch.go213
-rw-r--r--worker/types/messages.go6
2 files changed, 109 insertions, 110 deletions
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
+
+	var partHeaderSection imap.BodySectionName
+	partHeaderSection.Peek = true
+	if len(msg.Part) > 0 {
+		partHeaderSection.Specifier = imap.MIMESpecifier
+	} else {
+		partHeaderSection.Specifier = imap.HeaderSpecifier
+	}
+	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.FetchFlags,
+		imap.FetchEnvelope,
 		imap.FetchUid,
-		section.FetchItem(),
+		imap.FetchBodyStructure,
+		imap.FetchFlags,
+		partHeaderSection.FetchItem(),
+		partBodySection.FetchItem(),
 	}
-	imapw.handleFetchMessages(msg, []uint32{msg.Uid}, items, section)
+	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
-				}
-
-				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
+				// drain the channel upon error
+				for range messages {
 				}
-				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 {