Fix bug gathering "changes" to broadcast during a mailbox rename in certain situations

We weren't appending the individual changes to the slice, but the entire slice.
Since "Change" is an "any", this isn't a type error. So make a Change a
non-empty interface (I had seen an issue like this coming, should have made it
an interface then, at least now we have a reasonable method, to get the modseq
of a change).

Found while working on an imap webpush prototype.
This commit is contained in:
Mechiel Lukkien
2025-03-15 10:45:35 +01:00
parent 0cf0bfb8a6
commit eadbda027c
2 changed files with 28 additions and 2 deletions

View File

@ -3578,7 +3578,7 @@ func (a *Account) MailboxRename(tx *bstore.Tx, mbsrc *Mailbox, dst string, modse
// If we were moved from a/b to a/b/x, we mention the creation of a/b after we mentioned the rename. // If we were moved from a/b to a/b/x, we mention the creation of a/b after we mentioned the rename.
if strings.HasPrefix(dst, origName+"/") { if strings.HasPrefix(dst, origName+"/") {
changes = append(changes, parentChanges) changes = append(changes, parentChanges...)
} else { } else {
changes = slices.Concat(parentChanges, changes) changes = slices.Concat(parentChanges, changes)
} }

View File

@ -37,7 +37,9 @@ type UID uint32 // IMAP UID.
// Change to mailboxes/subscriptions/messages in an account. One of the Change* // Change to mailboxes/subscriptions/messages in an account. One of the Change*
// types in this package. // types in this package.
type Change any type Change interface {
ChangeModSeq() ModSeq // returns -1 for "modseq not applicable"
}
// ChangeAddUID is sent for a new message in a mailbox. // ChangeAddUID is sent for a new message in a mailbox.
type ChangeAddUID struct { type ChangeAddUID struct {
@ -48,6 +50,8 @@ type ChangeAddUID struct {
Keywords []string // Other flags. Keywords []string // Other flags.
} }
func (c ChangeAddUID) ChangeModSeq() ModSeq { return c.ModSeq }
// ChangeRemoveUIDs is sent for removal of one or more messages from a mailbox. // ChangeRemoveUIDs is sent for removal of one or more messages from a mailbox.
type ChangeRemoveUIDs struct { type ChangeRemoveUIDs struct {
MailboxID int64 MailboxID int64
@ -56,6 +60,8 @@ type ChangeRemoveUIDs struct {
MsgIDs []int64 // Message.ID, for erasing, order does not necessarily correspond with UIDs! MsgIDs []int64 // Message.ID, for erasing, order does not necessarily correspond with UIDs!
} }
func (c ChangeRemoveUIDs) ChangeModSeq() ModSeq { return c.ModSeq }
// ChangeFlags is sent for an update to flags for a message, e.g. "Seen". // ChangeFlags is sent for an update to flags for a message, e.g. "Seen".
type ChangeFlags struct { type ChangeFlags struct {
MailboxID int64 MailboxID int64
@ -66,6 +72,8 @@ type ChangeFlags struct {
Keywords []string // Non-system/well-known flags/keywords/labels. Keywords []string // Non-system/well-known flags/keywords/labels.
} }
func (c ChangeFlags) ChangeModSeq() ModSeq { return c.ModSeq }
// ChangeThread is sent when muted/collapsed changes. // ChangeThread is sent when muted/collapsed changes.
type ChangeThread struct { type ChangeThread struct {
MessageIDs []int64 MessageIDs []int64
@ -73,6 +81,8 @@ type ChangeThread struct {
Collapsed bool Collapsed bool
} }
func (c ChangeThread) ChangeModSeq() ModSeq { return -1 }
// ChangeRemoveMailbox is sent for a removed mailbox. // ChangeRemoveMailbox is sent for a removed mailbox.
type ChangeRemoveMailbox struct { type ChangeRemoveMailbox struct {
MailboxID int64 MailboxID int64
@ -80,6 +90,8 @@ type ChangeRemoveMailbox struct {
ModSeq ModSeq ModSeq ModSeq
} }
func (c ChangeRemoveMailbox) ChangeModSeq() ModSeq { return c.ModSeq }
// ChangeAddMailbox is sent for a newly created mailbox. // ChangeAddMailbox is sent for a newly created mailbox.
type ChangeAddMailbox struct { type ChangeAddMailbox struct {
Mailbox Mailbox Mailbox Mailbox
@ -87,6 +99,8 @@ type ChangeAddMailbox struct {
ModSeq ModSeq ModSeq ModSeq
} }
func (c ChangeAddMailbox) ChangeModSeq() ModSeq { return c.ModSeq }
// ChangeRenameMailbox is sent for a rename mailbox. // ChangeRenameMailbox is sent for a rename mailbox.
type ChangeRenameMailbox struct { type ChangeRenameMailbox struct {
MailboxID int64 MailboxID int64
@ -96,12 +110,16 @@ type ChangeRenameMailbox struct {
ModSeq ModSeq ModSeq ModSeq
} }
func (c ChangeRenameMailbox) ChangeModSeq() ModSeq { return c.ModSeq }
// ChangeAddSubscription is sent for an added subscription to a mailbox. // ChangeAddSubscription is sent for an added subscription to a mailbox.
type ChangeAddSubscription struct { type ChangeAddSubscription struct {
Name string Name string
Flags []string // For additional IMAP flags like \NonExistent. Flags []string // For additional IMAP flags like \NonExistent.
} }
func (c ChangeAddSubscription) ChangeModSeq() ModSeq { return -1 }
// ChangeMailboxCounts is sent when the number of total/deleted/unseen/unread messages changes. // ChangeMailboxCounts is sent when the number of total/deleted/unseen/unread messages changes.
type ChangeMailboxCounts struct { type ChangeMailboxCounts struct {
MailboxID int64 MailboxID int64
@ -109,6 +127,8 @@ type ChangeMailboxCounts struct {
MailboxCounts MailboxCounts
} }
func (c ChangeMailboxCounts) ChangeModSeq() ModSeq { return -1 }
// ChangeMailboxSpecialUse is sent when a special-use flag changes. // ChangeMailboxSpecialUse is sent when a special-use flag changes.
type ChangeMailboxSpecialUse struct { type ChangeMailboxSpecialUse struct {
MailboxID int64 MailboxID int64
@ -117,6 +137,8 @@ type ChangeMailboxSpecialUse struct {
ModSeq ModSeq ModSeq ModSeq
} }
func (c ChangeMailboxSpecialUse) ChangeModSeq() ModSeq { return c.ModSeq }
// ChangeMailboxKeywords is sent when keywords are changed for a mailbox. For // ChangeMailboxKeywords is sent when keywords are changed for a mailbox. For
// example, when a message is added with a previously unseen keyword. // example, when a message is added with a previously unseen keyword.
type ChangeMailboxKeywords struct { type ChangeMailboxKeywords struct {
@ -125,6 +147,8 @@ type ChangeMailboxKeywords struct {
Keywords []string Keywords []string
} }
func (c ChangeMailboxKeywords) ChangeModSeq() ModSeq { return -1 }
// ChangeAnnotation is sent when an annotation is added/updated/removed, either for // ChangeAnnotation is sent when an annotation is added/updated/removed, either for
// a mailbox or a global per-account annotation. The value is not included. // a mailbox or a global per-account annotation. The value is not included.
type ChangeAnnotation struct { type ChangeAnnotation struct {
@ -134,6 +158,8 @@ type ChangeAnnotation struct {
ModSeq ModSeq ModSeq ModSeq
} }
func (c ChangeAnnotation) ChangeModSeq() ModSeq { return c.ModSeq }
func messageEraser(donec chan struct{}, cleanc chan map[*Account][]int64) { func messageEraser(donec chan struct{}, cleanc chan map[*Account][]int64) {
log := mlog.New("store", nil) log := mlog.New("store", nil)