Unseen is first sequence number of unseen message not count of messages

This commit is contained in:
Michal Horejsek
2020-04-06 10:54:14 +02:00
parent ea0f3115a3
commit c939893131
11 changed files with 33 additions and 23 deletions

View File

@ -15,7 +15,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>. // along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
// Code generated by ./release-notes.sh at Mon Apr 6 08:14:14 CEST 2020. DO NOT EDIT. // Code generated by ./release-notes.sh at Mon Apr 6 10:56:36 CEST 2020. DO NOT EDIT.
package bridge package bridge

View File

@ -125,11 +125,12 @@ func (im *imapMailbox) Status(items []imap.StatusItem) (*imap.MailboxStatus, err
message.ThunderbirdNonJunkFlag, message.ThunderbirdNonJunkFlag,
} }
dbTotal, dbUnread, err := im.storeMailbox.GetCounts() dbTotal, dbUnread, dbUnreadSeqNum, err := im.storeMailbox.GetCounts()
l.Debugln("DB: total", dbTotal, "unread", dbUnread, "err", err) l.Debugln("DB: total", dbTotal, "unread", dbUnread, "unreadSeqNum", dbUnreadSeqNum, "err", err)
if err == nil { if err == nil {
status.Messages = uint32(dbTotal) status.Messages = uint32(dbTotal)
status.Unseen = uint32(dbUnread) status.Unseen = uint32(dbUnread)
status.UnseenSeqNum = uint32(dbUnreadSeqNum)
} }
if status.UidNext, err = im.storeMailbox.GetNextUID(); err != nil { if status.UidNext, err = im.storeMailbox.GetNextUID(); err != nil {

View File

@ -68,7 +68,7 @@ type storeMailboxProvider interface {
GetAPIIDsFromSequenceRange(start, stop uint32) ([]string, error) GetAPIIDsFromSequenceRange(start, stop uint32) ([]string, error)
GetLatestAPIID() (string, error) GetLatestAPIID() (string, error)
GetNextUID() (uint32, error) GetNextUID() (uint32, error)
GetCounts() (dbTotal, dbUnread uint, err error) GetCounts() (dbTotal, dbUnread, dbUnreadSeqNum uint, err error)
GetUIDList(apiIDs []string) *uidplus.OrderedSeq GetUIDList(apiIDs []string) *uidplus.OrderedSeq
GetUIDByHeader(header *mail.Header) uint32 GetUIDByHeader(header *mail.Header) uint32
GetDelimiter() string GetDelimiter() string

View File

@ -76,18 +76,20 @@ func (store *Store) imapDeleteMessage(address, mailboxName string, sequenceNumbe
store.imapSendUpdate(update) store.imapSendUpdate(update)
} }
func (store *Store) imapMailboxStatus(address, mailboxName string, total, unread uint) { func (store *Store) imapMailboxStatus(address, mailboxName string, total, unread, unreadSeqNum uint) {
store.log.WithFields(logrus.Fields{ store.log.WithFields(logrus.Fields{
"address": address, "address": address,
"mailbox": mailboxName, "mailbox": mailboxName,
"total": total, "total": total,
"unread": unread, "unread": unread,
"unreadSeqNum": unreadSeqNum,
}).Trace("IDLE status") }).Trace("IDLE status")
update := new(imapBackend.MailboxUpdate) update := new(imapBackend.MailboxUpdate)
update.Update = imapBackend.NewUpdate(address, mailboxName) update.Update = imapBackend.NewUpdate(address, mailboxName)
update.MailboxStatus = imap.NewMailboxStatus(mailboxName, []imap.StatusItem{imap.StatusMessages, imap.StatusUnseen}) update.MailboxStatus = imap.NewMailboxStatus(mailboxName, []imap.StatusItem{imap.StatusMessages, imap.StatusUnseen})
update.MailboxStatus.Messages = uint32(total) update.MailboxStatus.Messages = uint32(total)
update.MailboxStatus.Unseen = uint32(unread) update.MailboxStatus.Unseen = uint32(unread)
update.MailboxStatus.UnseenSeqNum = uint32(unreadSeqNum)
store.imapSendUpdate(update) store.imapSendUpdate(update)
} }

View File

@ -105,8 +105,8 @@ func checkMessageUpdate(username, mailbox string, seqNum, uid int) func(interfac
return func(update interface{}) bool { return func(update interface{}) bool {
switch u := update.(type) { switch u := update.(type) {
case *imapBackend.MessageUpdate: case *imapBackend.MessageUpdate:
return (u.Update.Username == username && return (u.Update.Username() == username &&
u.Update.Mailbox == mailbox && u.Update.Mailbox() == mailbox &&
u.Message.SeqNum == uint32(seqNum) && u.Message.SeqNum == uint32(seqNum) &&
u.Message.Uid == uint32(uid)) u.Message.Uid == uint32(uid))
default: default:
@ -119,8 +119,8 @@ func checkMessageDelete(username, mailbox string, seqNum int) func(interface{})
return func(update interface{}) bool { return func(update interface{}) bool {
switch u := update.(type) { switch u := update.(type) {
case *imapBackend.ExpungeUpdate: case *imapBackend.ExpungeUpdate:
return (u.Update.Username == username && return (u.Update.Username() == username &&
u.Update.Mailbox == mailbox && u.Update.Mailbox() == mailbox &&
u.SeqNum == uint32(seqNum)) u.SeqNum == uint32(seqNum))
default: default:
return false return false

View File

@ -81,7 +81,7 @@ func syncDraftsIfNecssary(tx *bolt.Tx, mb *Mailbox) { //nolint[funlen]
// If the drafts mailbox total is non-zero, it means it has already been used // If the drafts mailbox total is non-zero, it means it has already been used
// and there is no need to continue. Otherwise, we may need to do an initial sync. // and there is no need to continue. Otherwise, we may need to do an initial sync.
total, _, err := mb.txGetCounts(tx) total, _, _, err := mb.txGetCounts(tx)
if err != nil || total != 0 { if err != nil || total != 0 {
return return
} }

View File

@ -28,15 +28,15 @@ import (
) )
// GetCounts returns numbers of total and unread messages in this mailbox bucket. // GetCounts returns numbers of total and unread messages in this mailbox bucket.
func (storeMailbox *Mailbox) GetCounts() (total, unread uint, err error) { func (storeMailbox *Mailbox) GetCounts() (total, unread, unseenSeqNum uint, err error) {
err = storeMailbox.db().View(func(tx *bolt.Tx) error { err = storeMailbox.db().View(func(tx *bolt.Tx) error {
total, unread, err = storeMailbox.txGetCounts(tx) total, unread, unseenSeqNum, err = storeMailbox.txGetCounts(tx)
return err return err
}) })
return return
} }
func (storeMailbox *Mailbox) txGetCounts(tx *bolt.Tx) (total, unread uint, err error) { func (storeMailbox *Mailbox) txGetCounts(tx *bolt.Tx) (total, unread, unseenSeqNum uint, err error) {
// For total it would be enough to use `bolt.Bucket.Stats().KeyN` but // For total it would be enough to use `bolt.Bucket.Stats().KeyN` but
// we also need to retrieve the count of unread emails therefore we are // we also need to retrieve the count of unread emails therefore we are
// looping all messages in this mailbox by `bolt.Cursor` // looping all messages in this mailbox by `bolt.Cursor`
@ -48,16 +48,19 @@ func (storeMailbox *Mailbox) txGetCounts(tx *bolt.Tx) (total, unread uint, err e
total++ total++
rawMsg := metaBucket.Get(apiID) rawMsg := metaBucket.Get(apiID)
if rawMsg == nil { if rawMsg == nil {
return 0, 0, ErrNoSuchAPIID return 0, 0, 0, ErrNoSuchAPIID
} }
// Do not unmarshal whole JSON to speed up the looping. // Do not unmarshal whole JSON to speed up the looping.
// Instead, we assume it will contain JSON int field `Unread` // Instead, we assume it will contain JSON int field `Unread`
// where `1` means true (i.e. message is unread) // where `1` means true (i.e. message is unread)
if bytes.Contains(rawMsg, []byte(`"Unread":1`)) { if bytes.Contains(rawMsg, []byte(`"Unread":1`)) {
if unseenSeqNum == 0 {
unseenSeqNum = total
}
unread++ unread++
} }
} }
return total, unread, err return total, unread, unseenSeqNum, err
} }
type mailboxCounts struct { type mailboxCounts struct {

View File

@ -376,7 +376,7 @@ func (storeMailbox *Mailbox) txDeleteMessage(tx *bolt.Tx, apiID string) error {
} }
func (storeMailbox *Mailbox) txMailboxStatusUpdate(tx *bolt.Tx) error { func (storeMailbox *Mailbox) txMailboxStatusUpdate(tx *bolt.Tx) error {
total, unread, err := storeMailbox.txGetCounts(tx) total, unread, unreadSeqNum, err := storeMailbox.txGetCounts(tx)
if err != nil { if err != nil {
return errors.Wrap(err, "cannot get counts for mailbox status update") return errors.Wrap(err, "cannot get counts for mailbox status update")
} }
@ -385,6 +385,7 @@ func (storeMailbox *Mailbox) txMailboxStatusUpdate(tx *bolt.Tx) error {
storeMailbox.labelName, storeMailbox.labelName,
total, total,
unread, unread,
unreadSeqNum,
) )
return nil return nil
} }

View File

@ -75,7 +75,7 @@ func (store *Store) isSynced(countsOnAPI []*pmapi.MessagesCount) (bool, error) {
) )
} }
mboxTot, mboxUnread, err := mbox.GetCounts() mboxTot, mboxUnread, _, err := mbox.GetCounts()
if err != nil { if err != nil {
errW := errors.Wrap(err, "cannot count messages") errW := errors.Wrap(err, "cannot count messages")
store.log. store.log.

View File

@ -10,6 +10,9 @@ Feature: IMAP get mailbox info
Scenario: Mailbox info contains mailbox name Scenario: Mailbox info contains mailbox name
When IMAP client gets info of "INBOX" When IMAP client gets info of "INBOX"
Then IMAP response contains "2 EXISTS" Then IMAP response contains "2 EXISTS"
And IMAP response contains "UNSEEN 1" # Messages are inserted in opposite way to keep increasing UID.
# Sequence numbers are then opposite than listed above.
# Unseen should have first unseen message.
And IMAP response contains "UNSEEN 2"
And IMAP response contains "UIDNEXT 3" And IMAP response contains "UIDNEXT 3"
And IMAP response contains "UIDVALIDITY" And IMAP response contains "UIDVALIDITY"

View File

@ -93,7 +93,7 @@ func mailboxForAddressOfUserHasNumberOfMessages(mailboxName, bddAddressID, bddUs
start := time.Now() start := time.Now()
for { for {
afterLimit := time.Since(start) > ctx.EventLoopTimeout() afterLimit := time.Since(start) > ctx.EventLoopTimeout()
total, _, _ := mailbox.GetCounts() total, _, _, _ := mailbox.GetCounts()
if total == uint(countOfMessages) { if total == uint(countOfMessages) {
break break
} }