Browse Source

imap: ListMessages: handle body viewing, etc

main
Nick Thomas 4 years ago
parent
commit
6b17c216a1
Signed by: lupine
GPG Key ID: 1F1A7ECCCFE0B92F
  1. 103
      internal/imap/mailbox.go
  2. 10
      internal/store/maildir.go
  3. 16
      internal/store/message.go

103
internal/imap/mailbox.go

@ -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 nil, err
}
defer data.Close()
entity, err := message.Read(data)
if err != nil {
return nil, err
}
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 err
return nil, err
}
imapMsg.Items[k] = str
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

10
internal/store/maildir.go

@ -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)

16
internal/store/message.go

@ -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
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 {

Loading…
Cancel
Save