Support of UID EXPUNGE

This commit is contained in:
Michal Horejsek
2021-01-20 13:16:27 +01:00
parent 07c100bd66
commit 1909ceed67
6 changed files with 101 additions and 23 deletions

View File

@ -191,7 +191,20 @@ func (im *imapMailbox) Expunge() error {
im.user.backend.setUpdatesBeBlocking(im.user.currentAddressLowercase, im.name, operationDeleteMessage)
defer im.user.backend.unsetUpdatesBeBlocking(im.user.currentAddressLowercase, im.name, operationDeleteMessage)
return im.storeMailbox.RemoveDeleted()
return im.storeMailbox.RemoveDeleted(nil)
}
// UIDExpunge permanently removes messages that have the \Deleted flag set
// and UID passed from SeqSet from the currently selected mailbox.
func (im *imapMailbox) UIDExpunge(seqSet *imap.SeqSet) error {
im.user.backend.setUpdatesBeBlocking(im.user.currentAddressLowercase, im.name, operationDeleteMessage)
defer im.user.backend.unsetUpdatesBeBlocking(im.user.currentAddressLowercase, im.name, operationDeleteMessage)
messageIDs, err := im.apiIDsFromSeqSet(true, seqSet)
if err != nil || len(messageIDs) == 0 {
return err
}
return im.storeMailbox.RemoveDeleted(messageIDs)
}
func (im *imapMailbox) ListQuotas() ([]string, error) {

View File

@ -89,7 +89,7 @@ type storeMailboxProvider interface {
MarkMessagesDeleted(apiID []string) error
MarkMessagesUndeleted(apiID []string) error
ImportMessage(msg *pmapi.Message, body []byte, labelIDs []string) error
RemoveDeleted() error
RemoveDeleted(apiIDs []string) error
}
type storeMessageProvider interface {

View File

@ -116,40 +116,70 @@ func (os *OrderedSeq) String() string {
// UIDExpunge implements server.Handler but Bridge is not supporting
// UID EXPUNGE with specific UIDs.
type UIDExpungeMailbox interface {
Expunge() error
UIDExpunge(*imap.SeqSet) error
}
type UIDExpunge struct {
expunge *server.Expunge
SeqSet *imap.SeqSet
}
func newUIDExpunge() *UIDExpunge {
return &UIDExpunge{expunge: &server.Expunge{}}
return &UIDExpunge{}
}
func (e *UIDExpunge) Parse(fields []interface{}) error {
if len(fields) < 1 {
return e.expunge.Parse(fields)
if len(fields) == 0 {
return nil // It could be regular EXPUNGE without arguments.
}
if len(fields) > 1 {
return errors.New("too many arguments")
}
// RFC4315#section-2.1
// The UID EXPUNGE command permanently removes all messages that both
// have the \Deleted flag set and have a UID that is included in the
// specified sequence set from the currently selected mailbox. If a
// message either does not have the \Deleted flag set or has a UID
// that is not included in the specified sequence set, it is not
// affected.
//
// Current implementation supports only deletion of all messages
// marked as deleted. It will probably need mailbox interface change:
// ExpungeUIDs(seqSet). Not sure how to combine with original
// e.expunge.Handle().
return errors.New("UID EXPUNGE with UIDs is not supported")
seqset, ok := fields[0].(string)
if !ok {
return errors.New("sequence set must be an atom")
}
var err error
e.SeqSet, err = imap.ParseSeqSet(seqset)
return err
}
func (e *UIDExpunge) Handle(conn server.Conn) error {
return e.expunge.Handle(conn)
mailbox, err := e.getMailbox(conn)
if err != nil {
return err
}
return mailbox.Expunge()
}
func (e *UIDExpunge) UidHandle(conn server.Conn) error { //nolint[golint]
return e.expunge.Handle(conn)
if e.SeqSet == nil {
return errors.New("missing sequence set")
}
mailbox, err := e.getMailbox(conn)
if err != nil {
return err
}
return mailbox.UIDExpunge(e.SeqSet)
}
func (e *UIDExpunge) getMailbox(conn server.Conn) (UIDExpungeMailbox, error) {
ctx := conn.Context()
if ctx.Mailbox == nil {
return nil, server.ErrNoMailboxSelected
}
if ctx.MailboxReadOnly {
return nil, server.ErrMailboxReadOnly
}
mailbox, ok := ctx.Mailbox.(UIDExpungeMailbox)
if !ok {
return nil, errors.New("UID EXPUNGE is not implemented")
}
return mailbox, nil
}
type extension struct{}

View File

@ -208,14 +208,35 @@ func (storeMailbox *Mailbox) MarkMessagesUndeleted(apiIDs []string) error {
// If the mailbox is All Mail or All Sent, it does nothing.
// If the mailbox is Trash or Spam and message is not in any other mailbox, messages is deleted.
// In all other cases the message is only removed from the mailbox.
func (storeMailbox *Mailbox) RemoveDeleted() error {
// If nil is passed, all messages with \Deleted flag are removed.
// In other cases only messages with \Deleted flag and included in the passed list.
func (storeMailbox *Mailbox) RemoveDeleted(apiIDs []string) error {
storeMailbox.log.Trace("Deleting messages")
apiIDs, err := storeMailbox.GetDeletedAPIIDs()
deletedAPIIDs, err := storeMailbox.GetDeletedAPIIDs()
if err != nil {
return err
}
if apiIDs == nil {
apiIDs = deletedAPIIDs
} else {
filteredAPIIDs := []string{}
for _, apiID := range apiIDs {
found := false
for _, deletedAPIID := range deletedAPIIDs {
if apiID == deletedAPIID {
found = true
break
}
}
if found {
filteredAPIIDs = append(filteredAPIIDs, apiID)
}
}
apiIDs = filteredAPIIDs
}
if len(apiIDs) == 0 {
storeMailbox.log.Debug("List to expunge is empty")
return nil