forked from Silverfish/proton-bridge
GODT-1152: Correctly resolve wildcard sequence/UID set
This commit is contained in:
committed by
Jakub Cuth
parent
323303a98b
commit
27cfda680d
@ -526,18 +526,6 @@ func (im *imapMailbox) listMessages(isUID bool, seqSet *imap.SeqSet, items []ima
|
|||||||
return err
|
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))
|
input := make([]interface{}, len(apiIDs))
|
||||||
for i, apiID := range apiIDs {
|
for i, apiID := range apiIDs {
|
||||||
input[i] = apiID
|
input[i] = apiID
|
||||||
|
|||||||
@ -36,23 +36,36 @@ import (
|
|||||||
func (storeMailbox *Mailbox) GetAPIIDsFromUIDRange(start, stop uint32) (apiIDs []string, err error) {
|
func (storeMailbox *Mailbox) GetAPIIDsFromUIDRange(start, stop uint32) (apiIDs []string, err error) {
|
||||||
err = storeMailbox.db().View(func(tx *bolt.Tx) error {
|
err = storeMailbox.db().View(func(tx *bolt.Tx) error {
|
||||||
b := storeMailbox.txGetIMAPIDsBucket(tx)
|
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 {
|
if stop == 0 {
|
||||||
// A null stop means no stop.
|
stop = storeMailbox.txGetFinalUID(b)
|
||||||
stop = ^uint32(0)
|
}
|
||||||
|
|
||||||
|
// 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)
|
startb := itob(start)
|
||||||
stopb := itob(stop)
|
stopb := itob(stop)
|
||||||
|
|
||||||
c := b.Cursor()
|
|
||||||
for k, v := c.Seek(startb); k != nil && bytes.Compare(k, stopb) <= 0; k, v = c.Next() {
|
for k, v := c.Seek(startb); k != nil && bytes.Compare(k, stopb) <= 0; k, v = c.Next() {
|
||||||
apiIDs = append(apiIDs, string(v))
|
apiIDs = append(apiIDs, string(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
return
|
|
||||||
|
return apiIDs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAPIIDsFromSequenceRange returns API IDs by IMAP sequence number range.
|
// 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 {
|
err = storeMailbox.db().View(func(tx *bolt.Tx) error {
|
||||||
b := storeMailbox.txGetIMAPIDsBucket(tx)
|
b := storeMailbox.txGetIMAPIDsBucket(tx)
|
||||||
c := b.Cursor()
|
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
|
var i uint32
|
||||||
|
|
||||||
for k, v := c.First(); k != nil; k, v = c.Next() {
|
for k, v := c.First(); k != nil; k, v = c.Next() {
|
||||||
i++
|
i++
|
||||||
|
|
||||||
if i < start {
|
if i < start {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if stop > 0 && i > stop {
|
if stop > 0 && i > stop {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
apiIDs = append(apiIDs, string(v))
|
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 nil
|
||||||
})
|
})
|
||||||
return
|
|
||||||
|
return apiIDs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLatestAPIID returns the latest message API ID which still exists.
|
// GetLatestAPIID returns the latest message API ID which still exists.
|
||||||
// Info: not the latest IMAP UID which can be already removed.
|
// Info: not the latest IMAP UID which can be already removed.
|
||||||
func (storeMailbox *Mailbox) GetLatestAPIID() (apiID string, err error) {
|
func (storeMailbox *Mailbox) GetLatestAPIID() (apiID string, err error) {
|
||||||
err = storeMailbox.db().View(func(tx *bolt.Tx) error {
|
err = storeMailbox.db().View(func(tx *bolt.Tx) error {
|
||||||
b := storeMailbox.txGetAPIIDsBucket(tx)
|
c := storeMailbox.txGetAPIIDsBucket(tx).Cursor()
|
||||||
c := b.Cursor()
|
|
||||||
lastAPIID, _ := c.Last()
|
lastAPIID, _ := c.Last()
|
||||||
apiID = string(lastAPIID)
|
apiID = string(lastAPIID)
|
||||||
if apiID == "" {
|
if apiID == "" {
|
||||||
@ -283,3 +315,8 @@ func (storeMailbox *Mailbox) GetUIDByHeader(header *mail.Header) (foundUID uint3
|
|||||||
|
|
||||||
return foundUID
|
return foundUID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (storeMailbox *Mailbox) txGetFinalUID(b *bolt.Bucket) uint32 {
|
||||||
|
uid, _ := b.Cursor().Last()
|
||||||
|
return btoi(uid)
|
||||||
|
}
|
||||||
|
|||||||
@ -56,7 +56,7 @@ func checkMailboxMessageIDs(t *testing.T, m *mocksForStore, mailboxLabel string,
|
|||||||
storeAddress := m.store.addresses[addrID1]
|
storeAddress := m.store.addresses[addrID1]
|
||||||
storeMailbox := storeAddress.mailboxes[mailboxLabel]
|
storeMailbox := storeAddress.mailboxes[mailboxLabel]
|
||||||
|
|
||||||
ids, err := storeMailbox.GetAPIIDsFromSequenceRange(0, uint32(len(wantIDs)))
|
ids, err := storeMailbox.GetAPIIDsFromSequenceRange(1, uint32(len(wantIDs)))
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
idx := 0
|
idx := 0
|
||||||
|
|||||||
@ -11,7 +11,15 @@ Feature: IMAP fetch messages
|
|||||||
Then IMAP response is "OK"
|
Then IMAP response is "OK"
|
||||||
And IMAP response has 10 messages
|
And IMAP response has 10 messages
|
||||||
|
|
||||||
Scenario: Fetch first few message of inbox
|
Scenario: Fetch of inbox by UID
|
||||||
|
Given there are 10 messages in mailbox "INBOX" for "user"
|
||||||
|
And there is IMAP client logged in as "user"
|
||||||
|
And there is IMAP client selected in "INBOX"
|
||||||
|
When IMAP client fetches by UID "1:*"
|
||||||
|
Then IMAP response is "OK"
|
||||||
|
And IMAP response has 10 messages
|
||||||
|
|
||||||
|
Scenario: Fetch first few messages of inbox
|
||||||
Given there are 10 messages in mailbox "INBOX" for "user"
|
Given there are 10 messages in mailbox "INBOX" for "user"
|
||||||
And there is IMAP client logged in as "user"
|
And there is IMAP client logged in as "user"
|
||||||
And there is IMAP client selected in "INBOX"
|
And there is IMAP client selected in "INBOX"
|
||||||
@ -19,6 +27,78 @@ Feature: IMAP fetch messages
|
|||||||
Then IMAP response is "OK"
|
Then IMAP response is "OK"
|
||||||
And IMAP response has 5 messages
|
And IMAP response has 5 messages
|
||||||
|
|
||||||
|
Scenario: Fetch first few messages of inbox
|
||||||
|
Given there are 10 messages in mailbox "INBOX" for "user"
|
||||||
|
And there is IMAP client logged in as "user"
|
||||||
|
And there is IMAP client selected in "INBOX"
|
||||||
|
When IMAP client fetches by UID "1:5"
|
||||||
|
Then IMAP response is "OK"
|
||||||
|
And IMAP response has 5 messages
|
||||||
|
|
||||||
|
Scenario: Fetch last few messages of inbox using wildcard
|
||||||
|
Given there are 10 messages in mailbox "INBOX" for "user"
|
||||||
|
And there is IMAP client logged in as "user"
|
||||||
|
And there is IMAP client selected in "INBOX"
|
||||||
|
When IMAP client fetches "6:*"
|
||||||
|
Then IMAP response is "OK"
|
||||||
|
And IMAP response has 5 messages
|
||||||
|
|
||||||
|
Scenario: Fetch last few messages of inbox using wildcard by UID
|
||||||
|
Given there are 10 messages in mailbox "INBOX" for "user"
|
||||||
|
And there is IMAP client logged in as "user"
|
||||||
|
And there is IMAP client selected in "INBOX"
|
||||||
|
When IMAP client fetches by UID "6:*"
|
||||||
|
Then IMAP response is "OK"
|
||||||
|
And IMAP response has 5 messages
|
||||||
|
|
||||||
|
Scenario: Fetch last message of inbox using wildcard
|
||||||
|
Given there are 10 messages in mailbox "INBOX" for "user"
|
||||||
|
And there is IMAP client logged in as "user"
|
||||||
|
And there is IMAP client selected in "INBOX"
|
||||||
|
When IMAP client fetches "*"
|
||||||
|
Then IMAP response is "OK"
|
||||||
|
And IMAP response has 1 message
|
||||||
|
|
||||||
|
Scenario: Fetch last message of inbox using wildcard by UID
|
||||||
|
Given there are 10 messages in mailbox "INBOX" for "user"
|
||||||
|
And there is IMAP client logged in as "user"
|
||||||
|
And there is IMAP client selected in "INBOX"
|
||||||
|
When IMAP client fetches by UID "*"
|
||||||
|
Then IMAP response is "OK"
|
||||||
|
And IMAP response has 1 message
|
||||||
|
|
||||||
|
Scenario: Fetch backwards range using wildcard
|
||||||
|
Given there are 10 messages in mailbox "INBOX" for "user"
|
||||||
|
And there is IMAP client logged in as "user"
|
||||||
|
And there is IMAP client selected in "INBOX"
|
||||||
|
When IMAP client fetches "*:1"
|
||||||
|
Then IMAP response is "OK"
|
||||||
|
And IMAP response has 10 messages
|
||||||
|
|
||||||
|
Scenario: Fetch backwards range using wildcard by UID
|
||||||
|
Given there are 10 messages in mailbox "INBOX" for "user"
|
||||||
|
And there is IMAP client logged in as "user"
|
||||||
|
And there is IMAP client selected in "INBOX"
|
||||||
|
When IMAP client fetches by UID "*:1"
|
||||||
|
Then IMAP response is "OK"
|
||||||
|
And IMAP response has 10 messages
|
||||||
|
|
||||||
|
Scenario: Fetch overshot range using wildcard returns last message
|
||||||
|
Given there are 10 messages in mailbox "INBOX" for "user"
|
||||||
|
And there is IMAP client logged in as "user"
|
||||||
|
And there is IMAP client selected in "INBOX"
|
||||||
|
When IMAP client fetches "20:*"
|
||||||
|
Then IMAP response is "OK"
|
||||||
|
And IMAP response has 1 message
|
||||||
|
|
||||||
|
Scenario: Fetch overshot range using wildcard by UID returns last message
|
||||||
|
Given there are 10 messages in mailbox "INBOX" for "user"
|
||||||
|
And there is IMAP client logged in as "user"
|
||||||
|
And there is IMAP client selected in "INBOX"
|
||||||
|
When IMAP client fetches by UID "20:*"
|
||||||
|
Then IMAP response is "OK"
|
||||||
|
And IMAP response has 1 message
|
||||||
|
|
||||||
Scenario: Fetch of custom mailbox
|
Scenario: Fetch of custom mailbox
|
||||||
Given there are 10 messages in mailbox "Folders/mbox" for "user"
|
Given there are 10 messages in mailbox "Folders/mbox" for "user"
|
||||||
And there is IMAP client logged in as "user"
|
And there is IMAP client logged in as "user"
|
||||||
@ -27,7 +107,8 @@ Feature: IMAP fetch messages
|
|||||||
Then IMAP response is "OK"
|
Then IMAP response is "OK"
|
||||||
And IMAP response has 10 messages
|
And IMAP response has 10 messages
|
||||||
|
|
||||||
Scenario: Fetch of emtpy mailbox
|
# This test is wrong! RFC says it should return "BAD" (GODT-1153).
|
||||||
|
Scenario: Fetch of empty mailbox
|
||||||
Given there is IMAP client logged in as "user"
|
Given there is IMAP client logged in as "user"
|
||||||
And there is IMAP client selected in "Folders/mbox"
|
And there is IMAP client selected in "Folders/mbox"
|
||||||
When IMAP client fetches "1:*"
|
When IMAP client fetches "1:*"
|
||||||
@ -42,14 +123,6 @@ Feature: IMAP fetch messages
|
|||||||
Then IMAP response is "OK"
|
Then IMAP response is "OK"
|
||||||
And IMAP response has 100 messages
|
And IMAP response has 100 messages
|
||||||
|
|
||||||
Scenario: Fetch returns alsways latest messages
|
|
||||||
Given there are 10 messages in mailbox "Folders/mbox" for "user"
|
|
||||||
And there is IMAP client logged in as "user"
|
|
||||||
And there is IMAP client selected in "Folders/mbox"
|
|
||||||
When IMAP client fetches by UID "11:*"
|
|
||||||
Then IMAP response is "OK"
|
|
||||||
And IMAP response has 1 message
|
|
||||||
|
|
||||||
Scenario: Fetch returns also messages that are marked as deleted
|
Scenario: Fetch returns also messages that are marked as deleted
|
||||||
Given there are messages in mailbox "Folders/mbox" for "user"
|
Given there are messages in mailbox "Folders/mbox" for "user"
|
||||||
| from | to | subject | body | read | starred | deleted |
|
| from | to | subject | body | read | starred | deleted |
|
||||||
|
|||||||
@ -128,7 +128,7 @@ func mailboxForAddressOfUserHasMessages(mailboxName, bddAddressID, bddUserID str
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return internalError(err, "getting store mailbox")
|
return internalError(err, "getting store mailbox")
|
||||||
}
|
}
|
||||||
apiIDs, err := mailbox.GetAPIIDsFromSequenceRange(0, 1000)
|
apiIDs, err := mailbox.GetAPIIDsFromSequenceRange(1, 1000)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return internalError(err, "getting API IDs from sequence range")
|
return internalError(err, "getting API IDs from sequence range")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user