forked from Silverfish/proton-bridge
Support of UID EXPUNGE
This commit is contained in:
@ -191,7 +191,20 @@ func (im *imapMailbox) Expunge() error {
|
|||||||
im.user.backend.setUpdatesBeBlocking(im.user.currentAddressLowercase, im.name, operationDeleteMessage)
|
im.user.backend.setUpdatesBeBlocking(im.user.currentAddressLowercase, im.name, operationDeleteMessage)
|
||||||
defer im.user.backend.unsetUpdatesBeBlocking(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) {
|
func (im *imapMailbox) ListQuotas() ([]string, error) {
|
||||||
|
|||||||
@ -89,7 +89,7 @@ type storeMailboxProvider interface {
|
|||||||
MarkMessagesDeleted(apiID []string) error
|
MarkMessagesDeleted(apiID []string) error
|
||||||
MarkMessagesUndeleted(apiID []string) error
|
MarkMessagesUndeleted(apiID []string) error
|
||||||
ImportMessage(msg *pmapi.Message, body []byte, labelIDs []string) error
|
ImportMessage(msg *pmapi.Message, body []byte, labelIDs []string) error
|
||||||
RemoveDeleted() error
|
RemoveDeleted(apiIDs []string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type storeMessageProvider interface {
|
type storeMessageProvider interface {
|
||||||
|
|||||||
@ -116,40 +116,70 @@ func (os *OrderedSeq) String() string {
|
|||||||
|
|
||||||
// UIDExpunge implements server.Handler but Bridge is not supporting
|
// UIDExpunge implements server.Handler but Bridge is not supporting
|
||||||
// UID EXPUNGE with specific UIDs.
|
// UID EXPUNGE with specific UIDs.
|
||||||
|
|
||||||
|
type UIDExpungeMailbox interface {
|
||||||
|
Expunge() error
|
||||||
|
UIDExpunge(*imap.SeqSet) error
|
||||||
|
}
|
||||||
|
|
||||||
type UIDExpunge struct {
|
type UIDExpunge struct {
|
||||||
expunge *server.Expunge
|
SeqSet *imap.SeqSet
|
||||||
}
|
}
|
||||||
|
|
||||||
func newUIDExpunge() *UIDExpunge {
|
func newUIDExpunge() *UIDExpunge {
|
||||||
return &UIDExpunge{expunge: &server.Expunge{}}
|
return &UIDExpunge{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *UIDExpunge) Parse(fields []interface{}) error {
|
func (e *UIDExpunge) Parse(fields []interface{}) error {
|
||||||
if len(fields) < 1 {
|
if len(fields) == 0 {
|
||||||
return e.expunge.Parse(fields)
|
return nil // It could be regular EXPUNGE without arguments.
|
||||||
|
}
|
||||||
|
if len(fields) > 1 {
|
||||||
|
return errors.New("too many arguments")
|
||||||
}
|
}
|
||||||
|
|
||||||
// RFC4315#section-2.1
|
seqset, ok := fields[0].(string)
|
||||||
// The UID EXPUNGE command permanently removes all messages that both
|
if !ok {
|
||||||
// have the \Deleted flag set and have a UID that is included in the
|
return errors.New("sequence set must be an atom")
|
||||||
// specified sequence set from the currently selected mailbox. If a
|
}
|
||||||
// message either does not have the \Deleted flag set or has a UID
|
var err error
|
||||||
// that is not included in the specified sequence set, it is not
|
e.SeqSet, err = imap.ParseSeqSet(seqset)
|
||||||
// affected.
|
return err
|
||||||
//
|
|
||||||
// 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")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *UIDExpunge) Handle(conn server.Conn) error {
|
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]
|
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{}
|
type extension struct{}
|
||||||
|
|||||||
@ -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 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.
|
// 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.
|
// 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")
|
storeMailbox.log.Trace("Deleting messages")
|
||||||
|
|
||||||
apiIDs, err := storeMailbox.GetDeletedAPIIDs()
|
deletedAPIIDs, err := storeMailbox.GetDeletedAPIIDs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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 {
|
if len(apiIDs) == 0 {
|
||||||
storeMailbox.log.Debug("List to expunge is empty")
|
storeMailbox.log.Debug("List to expunge is empty")
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@ -99,3 +99,16 @@ Feature: IMAP remove messages from mailbox
|
|||||||
And there is IMAP client selected in "All Mail"
|
And there is IMAP client selected in "All Mail"
|
||||||
When IMAP client marks message seq "1" as deleted
|
When IMAP client marks message seq "1" as deleted
|
||||||
Then IMAP response is "IMAP error: NO operation not allowed for 'All Mail' folder"
|
Then IMAP response is "IMAP error: NO operation not allowed for 'All Mail' folder"
|
||||||
|
|
||||||
|
Scenario: Expunge specific message only
|
||||||
|
Given there are 5 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 marks message seq "1" as deleted
|
||||||
|
Then IMAP response is "OK"
|
||||||
|
When IMAP client marks message seq "2" as deleted
|
||||||
|
Then IMAP response is "OK"
|
||||||
|
When IMAP client sends command "UID EXPUNGE 1"
|
||||||
|
Then IMAP response is "OK"
|
||||||
|
And mailbox "INBOX" for "user" has 4 messages
|
||||||
|
And message "2" in "INBOX" for "user" is marked as deleted
|
||||||
|
|||||||
@ -13,6 +13,7 @@ Changelog [format](http://keepachangelog.com/en/1.0.0/)
|
|||||||
* GODT-804 Added GUI notification on silent update installed (promt to restart).
|
* GODT-804 Added GUI notification on silent update installed (promt to restart).
|
||||||
* GODT-275 Added option to disable autoupdates in settings (default autoupdate is enabled).
|
* GODT-275 Added option to disable autoupdates in settings (default autoupdate is enabled).
|
||||||
* GODT-874 Added manual triggers to Updater module.
|
* GODT-874 Added manual triggers to Updater module.
|
||||||
|
* GODT-851 Added support of UID EXPUNGE.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
* GODT-97 Don't log errors caused by SELECT "".
|
* GODT-97 Don't log errors caused by SELECT "".
|
||||||
|
|||||||
Reference in New Issue
Block a user