diff --git a/internal/bridge/release_notes.go b/internal/bridge/release_notes.go index 534057af..fcca40d4 100644 --- a/internal/bridge/release_notes.go +++ b/internal/bridge/release_notes.go @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Proton Technologies AG +// Copyright (c) 2021 Proton Technologies AG // // This file is part of ProtonMail Bridge. // @@ -15,7 +15,7 @@ // You should have received a copy of the GNU General Public License // along with ProtonMail Bridge. If not, see . -// Code generated by ./release-notes.sh at 'Wed Jan 13 03:17:24 PM CET 2021'. DO NOT EDIT. +// Code generated by ./release-notes.sh at 'Thu Jan 14 04:51:03 PM CET 2021'. DO NOT EDIT. package bridge diff --git a/internal/imap/mailbox.go b/internal/imap/mailbox.go index c964247c..a64f5c66 100644 --- a/internal/imap/mailbox.go +++ b/internal/imap/mailbox.go @@ -177,6 +177,17 @@ func (im *imapMailbox) Check() error { // Expunge permanently removes all messages that have the \Deleted flag set // from the currently selected mailbox. func (im *imapMailbox) Expunge() error { + // Wait for any APPENDS to finish in order to avoid data loss when + // Outlook sends commands too quickly STORE \Deleted, APPEND, EXPUNGE, + // APPEND FINISHED: + // + // Based on Outlook APPEND request we will not create new message but + // move the original to desired mailbox. If the message is currently + // in Trash or Spam and EXPUNGE happens before APPEND processing is + // finished the message is deleted from Proton instead of moved to + // the desired mailbox. + im.user.waitForAppend() + im.user.backend.setUpdatesBeBlocking(im.user.currentAddressLowercase, im.name, operationDeleteMessage) defer im.user.backend.unsetUpdatesBeBlocking(im.user.currentAddressLowercase, im.name, operationDeleteMessage) diff --git a/internal/imap/mailbox_message.go b/internal/imap/mailbox_message.go index c66e254e..0de841f1 100644 --- a/internal/imap/mailbox_message.go +++ b/internal/imap/mailbox_message.go @@ -70,6 +70,9 @@ func (im *imapMailbox) CreateMessage(flags []string, date time.Time, body imap.L // Called from go-imap in goroutines - we need to handle panics for each function. defer im.panicHandler.HandlePanic() + im.user.appendStarted() + defer im.user.appendFinished() + m, _, _, readers, err := message.Parse(body) if err != nil { return err diff --git a/internal/imap/user.go b/internal/imap/user.go index 8c36d9c5..5053e4e5 100644 --- a/internal/imap/user.go +++ b/internal/imap/user.go @@ -20,6 +20,7 @@ package imap import ( "errors" "strings" + "sync" "github.com/ProtonMail/proton-bridge/pkg/pmapi" imapquota "github.com/emersion/go-imap-quota" @@ -39,6 +40,8 @@ type imapUser struct { storeAddress storeAddressProvider currentAddressLowercase string + + appendInProcess sync.WaitGroup } // This method should eventually no longer be necessary. Everything should go via store. @@ -238,3 +241,15 @@ func (iu *imapUser) CreateMessageLimit() *uint32 { upload := uint32(maxUpload) return &upload } + +func (iu *imapUser) appendStarted() { + iu.appendInProcess.Add(1) +} + +func (iu *imapUser) appendFinished() { + iu.appendInProcess.Done() +} + +func (iu *imapUser) waitForAppend() { + iu.appendInProcess.Wait() +} diff --git a/unreleased.md b/unreleased.md index 4bde40bf..fb49972f 100644 --- a/unreleased.md +++ b/unreleased.md @@ -5,6 +5,7 @@ Changelog [format](http://keepachangelog.com/en/1.0.0/) ## Unreleased ### Added +* GODT-797 EXPUNGE waits for APPEND to prevent data loss when Outlook moves from Spam to Inbox ### Removed diff --git a/utils/license_header.txt b/utils/license_header.txt index b1cc405f..6d5d7749 100644 --- a/utils/license_header.txt +++ b/utils/license_header.txt @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Proton Technologies AG +// Copyright (c) 2021 Proton Technologies AG // // This file is part of ProtonMail Bridge. //