\Deleted flag support finish

This commit is contained in:
Michal Horejsek
2020-09-02 15:02:20 +02:00
parent 66e04dd5ed
commit c7578cf53c
8 changed files with 56 additions and 47 deletions

View File

@ -222,6 +222,20 @@ func (im *imapMailbox) labelMessages(uid bool, seqSet *imap.SeqSet, targetLabel
return err return err
} }
deletedIDs := []string{}
allDeletedIDs, err := im.storeMailbox.GetDeletedAPIIDs()
if err != nil {
log.WithError(err).Warn("Problem to get deleted API IDs")
} else {
for _, messageID := range messageIDs {
for _, deletedID := range allDeletedIDs {
if messageID == deletedID {
deletedIDs = append(deletedIDs, deletedID)
}
}
}
}
// Label messages first to not lose them. If message is only in trash and we unlabel // Label messages first to not lose them. If message is only in trash and we unlabel
// it, it will be removed completely and we cannot label it back. // it, it will be removed completely and we cannot label it back.
if err := targetStoreMailbox.LabelMessages(messageIDs); err != nil { if err := targetStoreMailbox.LabelMessages(messageIDs); err != nil {
@ -233,6 +247,13 @@ func (im *imapMailbox) labelMessages(uid bool, seqSet *imap.SeqSet, targetLabel
} }
} }
// Preserve \Deleted flag at target location.
if len(deletedIDs) > 0 {
if err := targetStoreMailbox.MarkMessagesDeleted(deletedIDs); err != nil {
log.WithError(err).Warn("Problem to preserve deleted flag for copied messages")
}
}
targetSeqSet := targetStoreMailbox.GetUIDList(messageIDs) targetSeqSet := targetStoreMailbox.GetUIDList(messageIDs)
return uidplus.CopyResponse(targetStoreMailbox.UIDValidity(), sourceSeqSet, targetSeqSet) return uidplus.CopyResponse(targetStoreMailbox.UIDValidity(), sourceSeqSet, targetSeqSet)
} }

View File

@ -70,6 +70,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)
GetDeletedAPIIDs() ([]string, error)
GetCounts() (dbTotal, dbUnread, dbUnreadSeqNum 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

View File

@ -114,16 +114,10 @@ func (os *OrderedSeq) String() string {
return out return out
} }
// UIDExpunge implements server.Handler but has no effect because Bridge is not // UIDExpunge implements server.Handler but Bridge is not supporting
// using EXPUNGE at all. The message is deleted right after it was flagged as // UID EXPUNGE with specific UIDs.
// \Deleted Bridge should simply ignore this command with empty `OK` response.
//
// If not implemented it would cause harmless IMAP error.
//
// This overrides the standard EXPUNGE functionality.
type UIDExpunge struct { type UIDExpunge struct {
expunge *server.Expunge expunge *server.Expunge
seqset *imap.SeqSet
} }
func newUIDExpunge() *UIDExpunge { func newUIDExpunge() *UIDExpunge {
@ -131,26 +125,10 @@ func newUIDExpunge() *UIDExpunge {
} }
func (e *UIDExpunge) Parse(fields []interface{}) error { func (e *UIDExpunge) Parse(fields []interface{}) error {
if len(fields) < 1 { // asuming no UID if len(fields) < 1 {
return e.expunge.Parse(fields) return e.expunge.Parse(fields)
} }
var err error
if seqset, ok := fields[0].(string); !ok {
return errors.New("sequence set must be an atom")
} else if e.seqset, err = imap.ParseSeqSet(seqset); err != nil {
return err
}
return nil
}
func (e *UIDExpunge) Handle(conn server.Conn) error {
log.Traceln("handle")
return e.expunge.Handle(conn)
}
func (e *UIDExpunge) UidHandle(conn server.Conn) error { //nolint[golint]
log.Traceln("uid handle")
// RFC4315#section-2.1 // RFC4315#section-2.1
// The UID EXPUNGE command permanently removes all messages that both // The UID EXPUNGE command permanently removes all messages that both
// have the \Deleted flag set and have a UID that is included in the // have the \Deleted flag set and have a UID that is included in the
@ -159,11 +137,18 @@ func (e *UIDExpunge) UidHandle(conn server.Conn) error { //nolint[golint]
// that is not included in the specified sequence set, it is not // that is not included in the specified sequence set, it is not
// affected. // affected.
// //
// NOTE missing implementation: It will probably need mailbox interface // Current implementation supports only deletion of all messages
// change: ExpungeUIDs(seqSet) not sure how to combine with original // marked as deleted. It will probably need mailbox interface change:
// ExpungeUIDs(seqSet). Not sure how to combine with original
// e.expunge.Handle(). // e.expunge.Handle().
// return errors.New("UID EXPUNGE with UIDs is not supported")
// Current implementation deletes all marked as deleted. }
func (e *UIDExpunge) Handle(conn server.Conn) error {
return e.expunge.Handle(conn)
}
func (e *UIDExpunge) UidHandle(conn server.Conn) error { //nolint[golint]
return e.expunge.Handle(conn) return e.expunge.Handle(conn)
} }

View File

@ -141,8 +141,8 @@ func (storeMailbox *Mailbox) txGetUIDFromBucket(b *bolt.Bucket, apiID string) (u
return btoi(v), nil return btoi(v), nil
} }
// getUID returns IMAP UID in this mailbox for message ID. // GetDeletedAPIIDs returns API IDs in this mailbox for message ID.
func (storeMailbox *Mailbox) getDeletedAPIIDs() (apiIDs []string, err error) { func (storeMailbox *Mailbox) GetDeletedAPIIDs() (apiIDs []string, err error) {
err = storeMailbox.db().Update(func(tx *bolt.Tx) error { err = storeMailbox.db().Update(func(tx *bolt.Tx) error {
b := storeMailbox.txGetDeletedIDsBucket(tx) b := storeMailbox.txGetDeletedIDsBucket(tx)
c := b.Cursor() c := b.Cursor()

View File

@ -205,7 +205,7 @@ func (storeMailbox *Mailbox) MarkMessagesUndeleted(apiIDs []string) error {
func (storeMailbox *Mailbox) RemoveDeleted() error { func (storeMailbox *Mailbox) RemoveDeleted() error {
storeMailbox.log.Trace("Deleting messages") storeMailbox.log.Trace("Deleting messages")
apiIDs, err := storeMailbox.getDeletedAPIIDs() apiIDs, err := storeMailbox.GetDeletedAPIIDs()
if err != nil { if err != nil {
return err return err
} }

View File

@ -3,6 +3,8 @@ Feature: IMAP copy messages
Given there is connected user "user" Given there is connected user "user"
And there is "user" with mailbox "Folders/mbox" And there is "user" with mailbox "Folders/mbox"
And there is "user" with mailbox "Labels/label" And there is "user" with mailbox "Labels/label"
# Messages are inserted in opposite way to keep increasing ID.
# Sequence numbers are then opposite than listed above.
And there are messages in mailbox "INBOX" for "user" And there are messages in mailbox "INBOX" for "user"
| from | to | subject | body | read | deleted | | from | to | subject | body | read | deleted |
| john.doe@mail.com | user@pm.me | foo | hello | true | false | | john.doe@mail.com | user@pm.me | foo | hello | true | false |
@ -18,8 +20,8 @@ Feature: IMAP copy messages
| john.doe@mail.com | user@pm.me | foo | hello | true | false | | john.doe@mail.com | user@pm.me | foo | hello | true | false |
| jane.doe@mail.com | name@pm.me | bar | world | false | true | | jane.doe@mail.com | name@pm.me | bar | world | false | true |
And mailbox "Labels/label" for "user" has messages And mailbox "Labels/label" for "user" has messages
| from | to | subject | body | read | deleted | | from | to | subject | body | read | deleted |
| jane.doe@mail.com | name@pm.me | bar | world | false | true | | john.doe@mail.com | user@pm.me | foo | hello | true | false |
Scenario: Copy all messages to label Scenario: Copy all messages to label
When IMAP client copies messages "1:*" to "Labels/label" When IMAP client copies messages "1:*" to "Labels/label"
@ -37,11 +39,11 @@ Feature: IMAP copy messages
When IMAP client copies messages "2" to "Folders/mbox" When IMAP client copies messages "2" to "Folders/mbox"
Then IMAP response is "OK" Then IMAP response is "OK"
And mailbox "INBOX" for "user" has messages And mailbox "INBOX" for "user" has messages
| from | to | subject | body | read | deleted |
| john.doe@mail.com | user@pm.me | foo | hello | true | false |
And mailbox "Folders/mbox" for "user" has messages
| from | to | subject | body | read | deleted | | from | to | subject | body | read | deleted |
| jane.doe@mail.com | name@pm.me | bar | world | false | true | | jane.doe@mail.com | name@pm.me | bar | world | false | true |
And mailbox "Folders/mbox" for "user" has messages
| from | to | subject | body | read | deleted |
| john.doe@mail.com | user@pm.me | foo | hello | true | false |
Scenario: Copy all messages to folder does move Scenario: Copy all messages to folder does move
When IMAP client copies messages "1:*" to "Folders/mbox" When IMAP client copies messages "1:*" to "Folders/mbox"

View File

@ -15,7 +15,7 @@ Feature: IMAP remove messages from mailbox
And IMAP response contains "\* 2 FETCH[ (]*FLAGS \([^)]*\\Deleted" And IMAP response contains "\* 2 FETCH[ (]*FLAGS \([^)]*\\Deleted"
When IMAP client sends expunge When IMAP client sends expunge
Then IMAP response is "OK" Then IMAP response is "OK"
And IMAP response contains "* 2 EXPUNGE" And IMAP response contains "\* 2 EXPUNGE"
And mailbox "<mailbox>" for "user" has 9 messages And mailbox "<mailbox>" for "user" has 9 messages
Examples: Examples:
@ -34,11 +34,11 @@ Feature: IMAP remove messages from mailbox
Then IMAP response is "OK" Then IMAP response is "OK"
When IMAP client sends expunge When IMAP client sends expunge
Then IMAP response is "OK" Then IMAP response is "OK"
And IMAP response contains "* 1 EXPUNGE" And IMAP response contains "\* 1 EXPUNGE"
And IMAP response contains "* 2 EXPUNGE" And IMAP response contains "\* 2 EXPUNGE"
And IMAP response contains "* 3 EXPUNGE" And IMAP response contains "\* 3 EXPUNGE"
And IMAP response contains "* 4 EXPUNGE" And IMAP response contains "\* 4 EXPUNGE"
And IMAP response contains "* 5 EXPUNGE" And IMAP response contains "\* 5 EXPUNGE"
And mailbox "<mailbox>" for "user" has 0 messages And mailbox "<mailbox>" for "user" has 0 messages
Examples: Examples:
@ -59,8 +59,8 @@ Feature: IMAP remove messages from mailbox
Then IMAP response is "OK" Then IMAP response is "OK"
When IMAP client sends expunge When IMAP client sends expunge
Then IMAP response is "OK" Then IMAP response is "OK"
And IMAP response contains "* 4 EXPUNGE" And IMAP response contains "\* 4 EXPUNGE"
And IMAP response contains "* 5 EXPUNGE" And IMAP response contains "\* 5 EXPUNGE"
And mailbox "<mailbox>" for "user" has 3 messages And mailbox "<mailbox>" for "user" has 3 messages
Examples: Examples:

View File

@ -74,12 +74,12 @@ Feature: IMAP search messages
Scenario: Search deleted messages Scenario: Search deleted messages
When IMAP client searches for "DELETED" When IMAP client searches for "DELETED"
Then IMAP response is "OK" Then IMAP response is "OK"
And IMAP response contains "SEARCH 1 2[^0-9]*$" And IMAP response contains "SEARCH 1[^0-9]*$"
Scenario: Search undeleted messages Scenario: Search undeleted messages
When IMAP client searches for "UNDELETED" When IMAP client searches for "UNDELETED"
Then IMAP response is "OK" Then IMAP response is "OK"
And IMAP response contains "SEARCH 3[^0-9]*$" And IMAP response contains "SEARCH 2 3[^0-9]*$"
Scenario: Search recent messages Scenario: Search recent messages
When IMAP client searches for "RECENT" When IMAP client searches for "RECENT"