Correctly handle LISTMESSAGES and STATUS commands using SeqNum instead of UID
This commit is contained in:
@@ -45,15 +45,9 @@ func (m *Mailbox) Info() (*imap.MailboxInfo, error) {
|
||||
// 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) {
|
||||
log.Printf("Mailbox(%v).Status(%#v)", m.stored.Rel(), items)
|
||||
|
||||
return &imap.MailboxStatus{
|
||||
Name: m.Name(),
|
||||
Flags: []string{}, // FIXME: what do we need here?
|
||||
PermanentFlags: []string{}, // FIXME: what do we need here?
|
||||
//UnseenSeqNum: 0, // FIXME: what do we need here? What even is a seqnum?
|
||||
Messages: 1, // FIXME: hardcoded
|
||||
}, nil
|
||||
status, err := m.buildStatus(items...)
|
||||
log.Printf("Mailbox(%v).Status(%#v): %#v %#v", m.stored.Rel(), items, status, err)
|
||||
return status, err
|
||||
}
|
||||
|
||||
// SetSubscribed adds or removes the mailbox to the server's set of "active"
|
||||
@@ -85,7 +79,7 @@ func (m *Mailbox) ListMessages(uid bool, seqset *imap.SeqSet, items []string, ch
|
||||
|
||||
var messages []store.Message
|
||||
for _, set := range seqset.Set {
|
||||
msgs, err := m.session.store.FindMessagesInRange(
|
||||
msgs, err := m.session.store.FindMessagesInRangeBySeqNum(
|
||||
m.stored, uint64(set.Start), uint64(set.Stop),
|
||||
)
|
||||
|
||||
@@ -119,14 +113,13 @@ func (m *Mailbox) ListMessages(uid bool, seqset *imap.SeqSet, items []string, ch
|
||||
|
||||
body := map[*imap.BodySectionName]imap.Literal{}
|
||||
|
||||
imapMsg := imap.NewMessage(uint32(message.UID), items)
|
||||
if err := fillItems(imapMsg, message); err != nil {
|
||||
imapMsg := imap.NewMessage(uint32(message.SeqNum), items)
|
||||
if err := m.fillMessageItems(imapMsg, message); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
imapMsg.Envelope = envelope
|
||||
imapMsg.Body = body
|
||||
imapMsg.Uid = uint32(message.UID)
|
||||
imapMsg.Size = uint32(message.Len())
|
||||
|
||||
log.Printf(" Sending message %#v", imapMsg)
|
||||
@@ -137,14 +130,50 @@ func (m *Mailbox) ListMessages(uid bool, seqset *imap.SeqSet, items []string, ch
|
||||
return nil
|
||||
}
|
||||
|
||||
func fillItems(imapMsg *imap.Message, msg store.Message) error {
|
||||
func (m *Mailbox) buildStatus(items ...string) (*imap.MailboxStatus, error) {
|
||||
out := &imap.MailboxStatus{
|
||||
Name: m.Name(),
|
||||
}
|
||||
|
||||
for _, item := range items {
|
||||
switch item {
|
||||
case "FLAGS": // TODO: ???
|
||||
out.Flags = []string{}
|
||||
case "PERMANENTFLAGS": // TODO: ???
|
||||
out.PermanentFlags = []string{}
|
||||
// The number of messages in the mailbox.
|
||||
case "MESSAGES":
|
||||
out.Messages = uint32(m.session.store.CountMessages(m.stored))
|
||||
// The number of messages with the \Recent flag set. TODO
|
||||
case "RECENT":
|
||||
// The next unique identifier value of the mailbox.
|
||||
case "UIDNEXT":
|
||||
out.UidNext = uint32(m.stored.NextUID())
|
||||
// The unique identifier validity value of the mailbox
|
||||
case "UIDVALIDITY":
|
||||
out.UidValidity = uint32(m.stored.UIDValidity())
|
||||
// The number of messages which do not have the \Seen flag set. TODO
|
||||
case "UNSEEN":
|
||||
default:
|
||||
return nil, fmt.Errorf("Unknown status item: %v", item)
|
||||
}
|
||||
}
|
||||
|
||||
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.Items[k] = msg.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()
|
||||
@@ -154,7 +183,7 @@ func fillItems(imapMsg *imap.Message, msg store.Message) error {
|
||||
|
||||
imapMsg.Items[k] = str
|
||||
default:
|
||||
return fmt.Errorf("Unknown item: %v", k)
|
||||
return fmt.Errorf("Unknown message item: %v", k)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -8,7 +8,9 @@ import (
|
||||
|
||||
type Entry struct {
|
||||
UID uint64
|
||||
SeqNum uint64
|
||||
Extra map[string][]string
|
||||
|
||||
LastKnownFilename string
|
||||
}
|
||||
|
||||
|
@@ -83,6 +83,12 @@ func handleFile(filename string, flags int) (*List, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *List) NextUID() uint64 {
|
||||
l.mutex.Lock()
|
||||
defer l.mutex.Unlock()
|
||||
|
||||
return l.header.NextUID
|
||||
}
|
||||
func (l *List) UIDValidity() uint64 {
|
||||
return l.header.UIDValidity
|
||||
}
|
||||
@@ -95,11 +101,35 @@ func (l *List) Entries() []Entry {
|
||||
l.mutex.Lock()
|
||||
defer l.mutex.Unlock()
|
||||
|
||||
// FIXME: use copy() ?
|
||||
return l.entries
|
||||
}
|
||||
|
||||
func (l *List) EntriesInRange(start, end uint64) []Entry {
|
||||
func (l *List) EntriesInRangeBySeqNum(start, end uint64) []Entry {
|
||||
l.mutex.Lock()
|
||||
defer l.mutex.Unlock()
|
||||
|
||||
// IMAP SeqNums are indexed from 1. So 0 should be invalid, right?
|
||||
if start == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
start--
|
||||
if int(start) > len(l.entries) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if end == 0 {
|
||||
return l.entries[start:]
|
||||
}
|
||||
|
||||
if int(end) > len(l.entries) {
|
||||
end = uint64(len(l.entries))
|
||||
}
|
||||
|
||||
return l.entries[start:end]
|
||||
}
|
||||
|
||||
func (l *List) EntriesInRangeByUID(start, end uint64) []Entry {
|
||||
l.mutex.Lock()
|
||||
defer l.mutex.Unlock()
|
||||
|
||||
@@ -124,6 +154,13 @@ func (l *List) Entry(key string) *Entry {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *List) Count() uint64 {
|
||||
l.mutex.Lock()
|
||||
defer l.mutex.Unlock()
|
||||
|
||||
return uint64(len(l.entries))
|
||||
}
|
||||
|
||||
func (l *List) Append(filenames ...string) error {
|
||||
l.mutex.Lock()
|
||||
defer l.mutex.Unlock()
|
||||
|
@@ -16,7 +16,8 @@ type MaildirInterface interface {
|
||||
CreateMaildir(*Maildir) error
|
||||
FindMaildir(account Account, name string) (Maildir, error)
|
||||
FindMaildirs(account Account) ([]Maildir, error)
|
||||
FindMessagesInRange(m Maildir, start, end uint64) ([]Message, error)
|
||||
FindMessagesInRangeBySeqNum(m Maildir, start, end uint64) ([]Message, error)
|
||||
CountMessages(m Maildir) uint64
|
||||
}
|
||||
|
||||
type Maildir struct {
|
||||
@@ -41,6 +42,14 @@ func (m *Maildir) Header(key string) (mail.Header, error) {
|
||||
return m.filesystem.Header(key)
|
||||
}
|
||||
|
||||
func (m *Maildir) UIDValidity() uint64 {
|
||||
return m.uids.UIDValidity()
|
||||
}
|
||||
|
||||
func (m *Maildir) NextUID() uint64 {
|
||||
return m.uids.NextUID()
|
||||
}
|
||||
|
||||
func (c *concrete) CreateMaildir(m *Maildir) error {
|
||||
if m.directory == "" {
|
||||
m.directory = c.buildMaildirPath(m.Account, m.Name)
|
||||
@@ -130,13 +139,14 @@ func (c *concrete) buildMaildirPath(account Account, parts ...string) string {
|
||||
return filepath.Join(bits...)
|
||||
}
|
||||
|
||||
func (c *concrete) FindMessagesInRange(m Maildir, start, end uint64) ([]Message, error) {
|
||||
func (c *concrete) FindMessagesInRangeBySeqNum(m Maildir, start, end uint64) ([]Message, error) {
|
||||
var out []Message
|
||||
entries := m.uids.EntriesInRange(start, end)
|
||||
entries := m.uids.EntriesInRangeBySeqNum(start, end)
|
||||
|
||||
for _, entry := range entries {
|
||||
msg := Message{
|
||||
UID: entry.UID,
|
||||
SeqNum: entry.SeqNum,
|
||||
Maildir: m,
|
||||
Key: entry.Key(),
|
||||
}
|
||||
@@ -146,3 +156,7 @@ func (c *concrete) FindMessagesInRange(m Maildir, start, end uint64) ([]Message,
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *concrete) CountMessages(m Maildir) uint64 {
|
||||
return m.uids.Count()
|
||||
}
|
||||
|
@@ -13,6 +13,7 @@ type Message struct {
|
||||
Maildir Maildir // maildir.name
|
||||
|
||||
UID uint64
|
||||
SeqNum uint64
|
||||
Key string
|
||||
Data io.ReadCloser
|
||||
hdrCache mail.Header
|
||||
|
Reference in New Issue
Block a user