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
|
// 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.
|
// RFC 3501 section 6.3.10 for a list of items that can be requested.
|
||||||
func (m *Mailbox) Status(items []string) (*imap.MailboxStatus, error) {
|
func (m *Mailbox) Status(items []string) (*imap.MailboxStatus, error) {
|
||||||
log.Printf("Mailbox(%v).Status(%#v)", m.stored.Rel(), items)
|
status, err := m.buildStatus(items...)
|
||||||
|
log.Printf("Mailbox(%v).Status(%#v): %#v %#v", m.stored.Rel(), items, status, err)
|
||||||
return &imap.MailboxStatus{
|
return status, err
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetSubscribed adds or removes the mailbox to the server's set of "active"
|
// 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
|
var messages []store.Message
|
||||||
for _, set := range seqset.Set {
|
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),
|
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{}
|
body := map[*imap.BodySectionName]imap.Literal{}
|
||||||
|
|
||||||
imapMsg := imap.NewMessage(uint32(message.UID), items)
|
imapMsg := imap.NewMessage(uint32(message.SeqNum), items)
|
||||||
if err := fillItems(imapMsg, message); err != nil {
|
if err := m.fillMessageItems(imapMsg, message); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
imapMsg.Envelope = envelope
|
imapMsg.Envelope = envelope
|
||||||
imapMsg.Body = body
|
imapMsg.Body = body
|
||||||
imapMsg.Uid = uint32(message.UID)
|
|
||||||
imapMsg.Size = uint32(message.Len())
|
imapMsg.Size = uint32(message.Len())
|
||||||
|
|
||||||
log.Printf(" Sending message %#v", imapMsg)
|
log.Printf(" Sending message %#v", imapMsg)
|
||||||
@@ -137,14 +130,50 @@ func (m *Mailbox) ListMessages(uid bool, seqset *imap.SeqSet, items []string, ch
|
|||||||
return nil
|
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 {
|
for k, _ := range imapMsg.Items {
|
||||||
switch k {
|
switch k {
|
||||||
case "UID":
|
case "UID":
|
||||||
imapMsg.Items[k] = msg.UID
|
imapMsg.Uid = uint32(msg.UID)
|
||||||
|
imapMsg.Items[k] = uint32(msg.UID)
|
||||||
case "FLAGS":
|
case "FLAGS":
|
||||||
|
imapMsg.Flags = []string{}
|
||||||
imapMsg.Items[k] = []string{}
|
imapMsg.Items[k] = []string{}
|
||||||
case "RFC822.SIZE":
|
case "RFC822.SIZE":
|
||||||
|
imapMsg.Size = uint32(msg.Len())
|
||||||
imapMsg.Items[k] = uint32(msg.Len())
|
imapMsg.Items[k] = uint32(msg.Len())
|
||||||
case "RFC822.HEADER":
|
case "RFC822.HEADER":
|
||||||
str, err := msg.HeaderString()
|
str, err := msg.HeaderString()
|
||||||
@@ -154,7 +183,7 @@ func fillItems(imapMsg *imap.Message, msg store.Message) error {
|
|||||||
|
|
||||||
imapMsg.Items[k] = str
|
imapMsg.Items[k] = str
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("Unknown item: %v", k)
|
return fmt.Errorf("Unknown message item: %v", k)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -7,8 +7,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Entry struct {
|
type Entry struct {
|
||||||
UID uint64
|
UID uint64
|
||||||
Extra map[string][]string
|
SeqNum uint64
|
||||||
|
Extra map[string][]string
|
||||||
|
|
||||||
LastKnownFilename string
|
LastKnownFilename string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -83,6 +83,12 @@ func handleFile(filename string, flags int) (*List, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *List) NextUID() uint64 {
|
||||||
|
l.mutex.Lock()
|
||||||
|
defer l.mutex.Unlock()
|
||||||
|
|
||||||
|
return l.header.NextUID
|
||||||
|
}
|
||||||
func (l *List) UIDValidity() uint64 {
|
func (l *List) UIDValidity() uint64 {
|
||||||
return l.header.UIDValidity
|
return l.header.UIDValidity
|
||||||
}
|
}
|
||||||
@@ -95,11 +101,35 @@ func (l *List) Entries() []Entry {
|
|||||||
l.mutex.Lock()
|
l.mutex.Lock()
|
||||||
defer l.mutex.Unlock()
|
defer l.mutex.Unlock()
|
||||||
|
|
||||||
// FIXME: use copy() ?
|
|
||||||
return l.entries
|
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()
|
l.mutex.Lock()
|
||||||
defer l.mutex.Unlock()
|
defer l.mutex.Unlock()
|
||||||
|
|
||||||
@@ -124,6 +154,13 @@ func (l *List) Entry(key string) *Entry {
|
|||||||
return nil
|
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 {
|
func (l *List) Append(filenames ...string) error {
|
||||||
l.mutex.Lock()
|
l.mutex.Lock()
|
||||||
defer l.mutex.Unlock()
|
defer l.mutex.Unlock()
|
||||||
|
@@ -16,7 +16,8 @@ type MaildirInterface interface {
|
|||||||
CreateMaildir(*Maildir) error
|
CreateMaildir(*Maildir) error
|
||||||
FindMaildir(account Account, name string) (Maildir, error)
|
FindMaildir(account Account, name string) (Maildir, error)
|
||||||
FindMaildirs(account Account) ([]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 {
|
type Maildir struct {
|
||||||
@@ -41,6 +42,14 @@ func (m *Maildir) Header(key string) (mail.Header, error) {
|
|||||||
return m.filesystem.Header(key)
|
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 {
|
func (c *concrete) CreateMaildir(m *Maildir) error {
|
||||||
if m.directory == "" {
|
if m.directory == "" {
|
||||||
m.directory = c.buildMaildirPath(m.Account, m.Name)
|
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...)
|
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
|
var out []Message
|
||||||
entries := m.uids.EntriesInRange(start, end)
|
entries := m.uids.EntriesInRangeBySeqNum(start, end)
|
||||||
|
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
msg := Message{
|
msg := Message{
|
||||||
UID: entry.UID,
|
UID: entry.UID,
|
||||||
|
SeqNum: entry.SeqNum,
|
||||||
Maildir: m,
|
Maildir: m,
|
||||||
Key: entry.Key(),
|
Key: entry.Key(),
|
||||||
}
|
}
|
||||||
@@ -146,3 +156,7 @@ func (c *concrete) FindMessagesInRange(m Maildir, start, end uint64) ([]Message,
|
|||||||
|
|
||||||
return out, nil
|
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
|
Maildir Maildir // maildir.name
|
||||||
|
|
||||||
UID uint64
|
UID uint64
|
||||||
|
SeqNum uint64
|
||||||
Key string
|
Key string
|
||||||
Data io.ReadCloser
|
Data io.ReadCloser
|
||||||
hdrCache mail.Header
|
hdrCache mail.Header
|
||||||
|
Reference in New Issue
Block a user