Browse Source

imap: ListMessages: handle body viewing, etc

master
Nick Thomas 1 year ago
parent
commit
6b17c216a1
Signed by: lupine <me@ur.gs> GPG Key ID: 1F1A7ECCCFE0B92F
3 changed files with 74 additions and 55 deletions
  1. +55
    -48
      internal/imap/mailbox.go
  2. +10
    -0
      internal/store/maildir.go
  3. +9
    -7
      internal/store/message.go

+ 55
- 48
internal/imap/mailbox.go View File

@@ -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
- 0
internal/store/maildir.go View File

@@ -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
- 7
internal/store/message.go View File

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