GODT-1152: Correctly resolve wildcard sequence/UID set

This commit is contained in:
James Houlahan
2021-04-30 10:35:34 +00:00
committed by Jakub Cuth
parent 323303a98b
commit 27cfda680d
5 changed files with 129 additions and 31 deletions

View File

@ -526,18 +526,6 @@ func (im *imapMailbox) listMessages(isUID bool, seqSet *imap.SeqSet, items []ima
return err
}
// From RFC: UID range of 559:* always includes the UID of the last message
// in the mailbox, even if 559 is higher than any assigned UID value.
// See: https://tools.ietf.org/html/rfc3501#page-61
if isUID && seqSet.Dynamic() && len(apiIDs) == 0 {
l.Debug("Requesting empty UID dynamic fetch, adding latest message")
apiID, err := im.storeMailbox.GetLatestAPIID()
if err != nil {
return nil
}
apiIDs = []string{apiID}
}
input := make([]interface{}, len(apiIDs))
for i, apiID := range apiIDs {
input[i] = apiID

View File

@ -36,23 +36,36 @@ import (
func (storeMailbox *Mailbox) GetAPIIDsFromUIDRange(start, stop uint32) (apiIDs []string, err error) {
err = storeMailbox.db().View(func(tx *bolt.Tx) error {
b := storeMailbox.txGetIMAPIDsBucket(tx)
c := b.Cursor()
// If the start range is a wildcard, the range can only refer to the last message in the mailbox.
if start == 0 {
_, apiID := c.Last()
apiIDs = append(apiIDs, string(apiID))
return nil
}
// Resolve the stop value to be the final UID in the mailbox.
if stop == 0 {
// A null stop means no stop.
stop = ^uint32(0)
stop = storeMailbox.txGetFinalUID(b)
}
// After resolving the stop value, it might be less than start so we sort it.
if start > stop {
start, stop = stop, start
}
startb := itob(start)
stopb := itob(stop)
c := b.Cursor()
for k, v := c.Seek(startb); k != nil && bytes.Compare(k, stopb) <= 0; k, v = c.Next() {
apiIDs = append(apiIDs, string(v))
}
return nil
})
return
return apiIDs, err
}
// GetAPIIDsFromSequenceRange returns API IDs by IMAP sequence number range.
@ -60,28 +73,47 @@ func (storeMailbox *Mailbox) GetAPIIDsFromSequenceRange(start, stop uint32) (api
err = storeMailbox.db().View(func(tx *bolt.Tx) error {
b := storeMailbox.txGetIMAPIDsBucket(tx)
c := b.Cursor()
// If the start range is a wildcard, the range can only refer to the last message in the mailbox.
if start == 0 {
_, apiID := c.Last()
apiIDs = append(apiIDs, string(apiID))
return nil
}
var i uint32
for k, v := c.First(); k != nil; k, v = c.Next() {
i++
if i < start {
continue
}
if stop > 0 && i > stop {
break
}
apiIDs = append(apiIDs, string(v))
}
if stop == 0 && len(apiIDs) == 0 {
if _, apiID := c.Last(); len(apiID) > 0 {
apiIDs = append(apiIDs, string(apiID))
}
}
return nil
})
return
return apiIDs, err
}
// GetLatestAPIID returns the latest message API ID which still exists.
// Info: not the latest IMAP UID which can be already removed.
func (storeMailbox *Mailbox) GetLatestAPIID() (apiID string, err error) {
err = storeMailbox.db().View(func(tx *bolt.Tx) error {
b := storeMailbox.txGetAPIIDsBucket(tx)
c := b.Cursor()
c := storeMailbox.txGetAPIIDsBucket(tx).Cursor()
lastAPIID, _ := c.Last()
apiID = string(lastAPIID)
if apiID == "" {
@ -283,3 +315,8 @@ func (storeMailbox *Mailbox) GetUIDByHeader(header *mail.Header) (foundUID uint3
return foundUID
}
func (storeMailbox *Mailbox) txGetFinalUID(b *bolt.Bucket) uint32 {
uid, _ := b.Cursor().Last()
return btoi(uid)
}

View File

@ -56,7 +56,7 @@ func checkMailboxMessageIDs(t *testing.T, m *mocksForStore, mailboxLabel string,
storeAddress := m.store.addresses[addrID1]
storeMailbox := storeAddress.mailboxes[mailboxLabel]
ids, err := storeMailbox.GetAPIIDsFromSequenceRange(0, uint32(len(wantIDs)))
ids, err := storeMailbox.GetAPIIDsFromSequenceRange(1, uint32(len(wantIDs)))
require.Nil(t, err)
idx := 0