imap: ListMessages: handle body viewing, etc
This commit is contained in:
@@ -6,6 +6,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/emersion/go-imap"
|
||||
"github.com/emersion/go-imap/backend/backendutil"
|
||||
"github.com/emersion/go-message"
|
||||
|
||||
"ur.gs/crockery/internal/store"
|
||||
)
|
||||
@@ -44,8 +46,8 @@ func (m *Mailbox) Info() (*imap.MailboxInfo, error) {
|
||||
// and UnseenSeqNum in the returned MailboxStatus must be always populated.
|
||||
// This function does not affect the state of any messages in the mailbox. See
|
||||
// RFC 3501 section 6.3.10 for a list of items that can be requested.
|
||||
func (m *Mailbox) Status(items []string) (*imap.MailboxStatus, error) {
|
||||
status, err := m.buildStatus(items...)
|
||||
func (m *Mailbox) Status(items []imap.StatusItem) (*imap.MailboxStatus, error) {
|
||||
status, err := m.buildStatus(items)
|
||||
log.Printf("Mailbox(%v).Status(%#v): %#v %#v", m.stored.Rel(), items, status, err)
|
||||
return status, err
|
||||
}
|
||||
@@ -73,7 +75,7 @@ func (m *Mailbox) Check() error {
|
||||
// 3501 section 6.4.5 for a list of items that can be requested.
|
||||
//
|
||||
// Messages must be sent to ch. When the function returns, ch must be closed.
|
||||
func (m *Mailbox) ListMessages(uid bool, seqset *imap.SeqSet, items []string, ch chan<- *imap.Message) error {
|
||||
func (m *Mailbox) ListMessages(uid bool, seqset *imap.SeqSet, items []imap.FetchItem, ch chan<- *imap.Message) error {
|
||||
log.Printf("Mailbox(%v).ListMessages(%#v, %#v, %#v, ch)", m.stored.Rel(), uid, *seqset, items)
|
||||
defer close(ch)
|
||||
|
||||
@@ -93,35 +95,11 @@ func (m *Mailbox) ListMessages(uid bool, seqset *imap.SeqSet, items []string, ch
|
||||
|
||||
log.Printf(" %v messages: %#v", len(messages), messages)
|
||||
for _, message := range messages {
|
||||
header, err := message.Header()
|
||||
imapMsg, err := m.buildMessage(message, items)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
envelope := &imap.Envelope{
|
||||
// Date:
|
||||
Subject: header.Get("Subject"),
|
||||
// From:
|
||||
// Sender:
|
||||
// ReplyTo:
|
||||
// To:
|
||||
// Cc:
|
||||
// Bcc:
|
||||
InReplyTo: header.Get("In-Reply-To"),
|
||||
MessageId: header.Get("Message-Id"),
|
||||
}
|
||||
|
||||
body := map[*imap.BodySectionName]imap.Literal{}
|
||||
|
||||
imapMsg := imap.NewMessage(uint32(message.SeqNum), items)
|
||||
if err := m.fillMessageItems(imapMsg, message); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
imapMsg.Envelope = envelope
|
||||
imapMsg.Body = body
|
||||
imapMsg.Size = uint32(message.Len())
|
||||
|
||||
log.Printf(" Sending message %#v", imapMsg)
|
||||
ch <- imapMsg
|
||||
log.Printf(" Sent message")
|
||||
@@ -130,7 +108,7 @@ func (m *Mailbox) ListMessages(uid bool, seqset *imap.SeqSet, items []string, ch
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Mailbox) buildStatus(items ...string) (*imap.MailboxStatus, error) {
|
||||
func (m *Mailbox) buildStatus(items []imap.StatusItem) (*imap.MailboxStatus, error) {
|
||||
out := &imap.MailboxStatus{
|
||||
Name: m.Name(),
|
||||
}
|
||||
@@ -162,32 +140,61 @@ func (m *Mailbox) buildStatus(items ...string) (*imap.MailboxStatus, error) {
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (m *Mailbox) fillMessageItems(imapMsg *imap.Message, msg store.Message) error {
|
||||
// FIXME: do I have to fill Items as well as the individual fields? Unclear
|
||||
for k, _ := range imapMsg.Items {
|
||||
switch k {
|
||||
case "UID":
|
||||
imapMsg.Uid = uint32(msg.UID)
|
||||
imapMsg.Items[k] = uint32(msg.UID)
|
||||
case "FLAGS":
|
||||
imapMsg.Flags = []string{}
|
||||
imapMsg.Items[k] = []string{}
|
||||
case "RFC822.SIZE":
|
||||
imapMsg.Size = uint32(msg.Len())
|
||||
imapMsg.Items[k] = uint32(msg.Len())
|
||||
case "RFC822.HEADER":
|
||||
str, err := msg.HeaderString()
|
||||
func (m *Mailbox) buildMessage(msg store.Message, items []imap.FetchItem) (*imap.Message, error) {
|
||||
out := imap.NewMessage(uint32(msg.SeqNum), items)
|
||||
|
||||
// FIXME: This might not be necessary on some calls, but is needed in 3
|
||||
// different cases below. Tidy it up.
|
||||
data, err := m.stored.MessageData(msg)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
defer data.Close()
|
||||
|
||||
entity, err := message.Read(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
imapMsg.Items[k] = str
|
||||
for _, item := range items {
|
||||
switch item {
|
||||
case imap.FetchEnvelope:
|
||||
env, err := backendutil.FetchEnvelope(entity.Header)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out.Envelope = env
|
||||
case imap.FetchBody, imap.FetchBodyStructure:
|
||||
bs, err := backendutil.FetchBodyStructure(entity, item == imap.FetchBodyStructure)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out.BodyStructure = bs
|
||||
case imap.FetchInternalDate:
|
||||
case imap.FetchUid:
|
||||
out.Uid = uint32(msg.UID)
|
||||
case imap.FetchFlags:
|
||||
out.Flags = []string{}
|
||||
case imap.FetchRFC822Size:
|
||||
out.Size = uint32(msg.Len())
|
||||
default:
|
||||
return fmt.Errorf("Unknown message item: %v", k)
|
||||
section, err := imap.ParseBodySectionName(item)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
l, err := backendutil.FetchBodySection(entity, section)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out.Body[section] = l
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// SearchMessages searches messages. The returned list must contain UIDs if
|
||||
|
@@ -2,6 +2,7 @@ package store
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net/mail"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -50,6 +51,15 @@ func (m *Maildir) NextUID() uint64 {
|
||||
return m.uids.NextUID()
|
||||
}
|
||||
|
||||
func (m *Maildir) MessageData(message Message) (io.ReadCloser, error) {
|
||||
filename, err := m.filesystem.Filename(message.Key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return os.Open(filename)
|
||||
}
|
||||
|
||||
func (c *concrete) CreateMaildir(m *Maildir) error {
|
||||
if m.directory == "" {
|
||||
m.directory = c.buildMaildirPath(m.Account, m.Name)
|
||||
|
@@ -9,12 +9,18 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
type MessageInterface interface {
|
||||
CreateMessage(Message) error
|
||||
}
|
||||
|
||||
type Message struct {
|
||||
Maildir Maildir // maildir.name
|
||||
|
||||
UID uint64
|
||||
SeqNum uint64
|
||||
Key string
|
||||
|
||||
// Data is usually nil, but may be an io.ReadCloser if SetupData is called
|
||||
Data io.ReadCloser
|
||||
hdrCache mail.Header
|
||||
}
|
||||
@@ -54,10 +60,6 @@ func (m *Message) HeaderString() (string, error) {
|
||||
return sb.String(), nil // FIXME
|
||||
}
|
||||
|
||||
type MessageInterface interface {
|
||||
CreateMessage(Message) error
|
||||
}
|
||||
|
||||
// DeliverMessage takes the given message and delivers it to the correct maildir
|
||||
// It does not call Close() on message.Data
|
||||
func (c *concrete) CreateMessage(message Message) error {
|
||||
|
Reference in New Issue
Block a user