From 749d4bc4476627d60ebb0f77560a7dd3d8df7e40 Mon Sep 17 00:00:00 2001
From: ElectroNafta <atanas.janeshliev@proton.ch>
Date: Mon, 23 Dec 2024 15:46:47 +0100
Subject: [PATCH] feat(BRIDGE-288): handle previously existing message in
 create events as updates

---
 connector/dummy_simulate.go           |  2 +-
 imap/update_message_updated.go        | 29 +++++++++++---------
 internal/backend/connector_updates.go | 38 ++++++++++++++++++++++++---
 3 files changed, 52 insertions(+), 17 deletions(-)

diff --git a/connector/dummy_simulate.go b/connector/dummy_simulate.go
index 49735122..ac66a241 100644
--- a/connector/dummy_simulate.go
+++ b/connector/dummy_simulate.go
@@ -163,7 +163,7 @@ func (conn *Dummy) MessageUpdated(message imap.Message, literal []byte, mboxIDs
 		mboxIDs: mboxIDMap,
 	}
 
-	conn.pushUpdate(imap.NewMessageUpdated(message, literal, mboxIDs, parsedMessage, false))
+	conn.pushUpdate(imap.NewMessageUpdated(message, literal, mboxIDs, parsedMessage, false, false))
 
 	return nil
 }
diff --git a/imap/update_message_updated.go b/imap/update_message_updated.go
index 4315a767..cb9bd743 100644
--- a/imap/update_message_updated.go
+++ b/imap/update_message_updated.go
@@ -14,11 +14,12 @@ type MessageUpdated struct {
 	updateBase
 	*updateWaiter
 
-	Message       Message
-	Literal       []byte
-	MailboxIDs    []MailboxID
-	ParsedMessage *ParsedMessage
-	AllowCreate   bool
+	Message                 Message
+	Literal                 []byte
+	MailboxIDs              []MailboxID
+	ParsedMessage           *ParsedMessage
+	AllowCreate             bool
+	IgnoreUnknownMailboxIDs bool
 }
 
 func NewMessageUpdated(
@@ -26,25 +27,27 @@ func NewMessageUpdated(
 	literal []byte,
 	mailboxIDs []MailboxID,
 	parsedMessage *ParsedMessage,
-	allowCreate bool,
+	allowCreate, ignoreUnkownMailboxIDs bool,
 ) *MessageUpdated {
 	return &MessageUpdated{
-		updateWaiter:  newUpdateWaiter(),
-		Message:       message,
-		Literal:       literal,
-		MailboxIDs:    mailboxIDs,
-		ParsedMessage: parsedMessage,
-		AllowCreate:   allowCreate,
+		updateWaiter:            newUpdateWaiter(),
+		Message:                 message,
+		Literal:                 literal,
+		MailboxIDs:              mailboxIDs,
+		ParsedMessage:           parsedMessage,
+		AllowCreate:             allowCreate,
+		IgnoreUnknownMailboxIDs: ignoreUnkownMailboxIDs,
 	}
 }
 
 func (u *MessageUpdated) String() string {
-	return fmt.Sprintf("MessageUpdated: ID:%v Mailboxes:%v Flags:%s AllowCreate:%v",
+	return fmt.Sprintf("MessageUpdated: ID:%v Mailboxes:%v Flags:%s AllowCreate:%v IgnoreUnkownMailboxIDs:%v",
 		u.Message.ID.ShortID(),
 		xslices.Map(u.MailboxIDs, func(mboxID MailboxID) string {
 			return mboxID.ShortID()
 		}),
 		u.Message.Flags.ToSlice(),
 		u.AllowCreate,
+		u.IgnoreUnknownMailboxIDs,
 	)
 }
diff --git a/internal/backend/connector_updates.go b/internal/backend/connector_updates.go
index a6051677..98037ccf 100644
--- a/internal/backend/connector_updates.go
+++ b/internal/backend/connector_updates.go
@@ -204,6 +204,9 @@ func (user *user) applyMessagesCreated(ctx context.Context, update *imap.Message
 	messageForMBox := make(map[imap.InternalMailboxID][]db.MessageIDPair)
 	mboxInternalIDMap := make(map[imap.MailboxID]imap.InternalMailboxID)
 
+	// for existing messages - we fully update them
+	var messagesToUpdate []*imap.MessageCreated
+
 	err := userDBWrite(ctx, user, func(ctx context.Context, tx db.Transaction) ([]state.Update, error) {
 		for _, message := range update.Messages {
 			if slices.Contains(message.MailboxIDs, ids.GluonInternalRecoveryMailboxRemoteID) {
@@ -213,7 +216,7 @@ func (user *user) applyMessagesCreated(ctx context.Context, update *imap.Message
 
 			internalID, ok := messagesToCreateFilter[message.Message.ID]
 			if !ok {
-				messageID, err := tx.GetMessageIDFromRemoteID(ctx, message.Message.ID)
+				_, err := tx.GetMessageIDFromRemoteID(ctx, message.Message.ID)
 				if db.IsErrNotFound(err) {
 					internalID = imap.NewInternalMessageID()
 
@@ -237,7 +240,10 @@ func (user *user) applyMessagesCreated(ctx context.Context, update *imap.Message
 					messagesToCreate = append(messagesToCreate, request)
 					messagesToCreateFilter[message.Message.ID] = internalID
 				} else if err == nil {
-					internalID = messageID
+					user.log.WithField("MessageID", message.Message.ID.ShortID()).
+						Info("Found existing message in create event, will update instead")
+					messagesToUpdate = append(messagesToUpdate, message)
+					continue
 				} else {
 					return nil, err
 				}
@@ -339,9 +345,23 @@ func (user *user) applyMessagesCreated(ctx context.Context, update *imap.Message
 				}
 			}
 		}
+		return err
 	}
 
-	return err
+	for _, message := range messagesToUpdate {
+		if err := user.applyMessageUpdated(ctx, imap.NewMessageUpdated(
+			message.Message,
+			message.Literal,
+			message.MailboxIDs,
+			message.ParsedMessage,
+			false,
+			update.IgnoreUnknownMailboxIDs,
+		)); err != nil {
+			return fmt.Errorf("failed to update previously existing message during creation: %v", err)
+		}
+	}
+
+	return nil
 }
 
 // applyMessageMailboxesUpdated applies a MessageMailboxesUpdated update.
@@ -624,6 +644,12 @@ func (user *user) applyMessageUpdated(ctx context.Context, update *imap.MessageU
 			for _, mbox := range update.MailboxIDs {
 				internalMBoxID, err := tx.GetMailboxIDFromRemoteID(ctx, mbox)
 				if err != nil {
+					if update.IgnoreUnknownMailboxIDs {
+						user.log.WithField("MailboxID", mbox.ShortID()).
+							WithField("MessageID", update.Message.ID.ShortID()).
+							Warn("Unknown Mailbox ID during message update, skipping add to mailbox")
+						continue
+					}
 					return nil, err
 				}
 
@@ -702,6 +728,12 @@ func (user *user) applyMessageUpdated(ctx context.Context, update *imap.MessageU
 				for _, mbox := range update.MailboxIDs {
 					internalMBoxID, err := tx.GetMailboxIDFromRemoteID(ctx, mbox)
 					if err != nil {
+						if update.IgnoreUnknownMailboxIDs {
+							user.log.WithField("MailboxID", mbox.ShortID()).
+								WithField("MessageID", update.Message.ID.ShortID()).
+								Warn("Unknown Mailbox ID during message update, skipping add to mailbox")
+							continue
+						}
 						return nil, err
 					}