From 1a6d268e1dec2ef9d68e3662c4aa10c68b3011ba Mon Sep 17 00:00:00 2001 From: Mechiel Lukkien Date: Fri, 11 Apr 2025 18:22:29 +0200 Subject: [PATCH] imapserver: check for UIDNEXT overflow when adding a message to a mailbox Return an error, with instructions so a user may be able to work around the issue. --- imapserver/server.go | 8 +++++--- store/account.go | 14 +++++++++++++- webops/xops.go | 5 +++-- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/imapserver/server.go b/imapserver/server.go index 16e5057..c94bafe 100644 --- a/imapserver/server.go +++ b/imapserver/server.go @@ -4733,7 +4733,8 @@ func (c *conn) cmdxCopy(isUID bool, tag, cmd string, p *parser) { // Reserve the uids in the destination mailbox. uidFirst := mbDst.UIDNext - mbDst.UIDNext += store.UID(len(uids)) + err = mbDst.UIDNextAdd(len(uids)) + xcheckf(err, "adding uid") // Fetch messages from database. q := bstore.QueryTx[store.Message](tx) @@ -5043,7 +5044,8 @@ func (c *conn) xmoveMessages(tx *bstore.Tx, q *bstore.Query[store.Message], expe nm := om nm.MailboxID = mbDst.ID nm.UID = mbDst.UIDNext - mbDst.UIDNext++ + err := mbDst.UIDNextAdd(1) + xcheckf(err, "adding uid") nm.ModSeq = modseq nm.CreateSeq = modseq nm.SaveDate = &now @@ -5057,7 +5059,7 @@ func (c *conn) xmoveMessages(tx *bstore.Tx, q *bstore.Query[store.Message], expe nm.JunkFlagsForMailbox(*mbDst, accConf) - err := tx.Update(&nm) + err = tx.Update(&nm) xcheckf(err, "updating message with new mailbox") mbDst.Add(nm.MailboxCounts()) diff --git a/store/account.go b/store/account.go index f80f598..2406b2b 100644 --- a/store/account.go +++ b/store/account.go @@ -298,6 +298,16 @@ type SpecialUse struct { Trash bool } +// UIDNextAdd increases the UIDNext value by n, returning an error on overflow. +func (mb *Mailbox) UIDNextAdd(n int) error { + uidnext := mb.UIDNext + UID(n) + if uidnext < mb.UIDNext { + return fmt.Errorf("uid overflow on mailbox %q (id %d): uidnext %d, adding %d; consider recreating the mailbox and copying its messages to compact", mb.Name, mb.ID, mb.UIDNext, n) + } + mb.UIDNext = uidnext + return nil +} + // CalculateCounts calculates the full current counts for messages in the mailbox. func (mb *Mailbox) CalculateCounts(tx *bstore.Tx) (mc MailboxCounts, err error) { q := bstore.QueryTx[Message](tx) @@ -2218,7 +2228,9 @@ func (a *Account) MessageAdd(log mlog.Log, tx *bstore.Tx, mb *Mailbox, m *Messag } if m.UID == 0 { m.UID = mb.UIDNext - mb.UIDNext++ + if err := mb.UIDNextAdd(1); err != nil { + return fmt.Errorf("adding uid: %v", err) + } } if m.ModSeq == 0 { modseq, err := a.NextModSeq(tx) diff --git a/webops/xops.go b/webops/xops.go index b089eab..cf7c154 100644 --- a/webops/xops.go +++ b/webops/xops.go @@ -473,7 +473,8 @@ func (x XOps) MessageMoveTx(ctx context.Context, log mlog.Log, acc *store.Accoun nm := om nm.MailboxID = mbDst.ID nm.UID = mbDst.UIDNext - mbDst.UIDNext++ + err := mbDst.UIDNextAdd(1) + x.Checkf(ctx, err, "adding uid") nm.ModSeq = *modseq nm.CreateSeq = *modseq nm.SaveDate = &now @@ -490,7 +491,7 @@ func (x XOps) MessageMoveTx(ctx context.Context, log mlog.Log, acc *store.Accoun nm.JunkFlagsForMailbox(mbDst, accConf) - err := tx.Update(&nm) + err = tx.Update(&nm) x.Checkf(ctx, err, "updating message with new mailbox") mbDst.Add(nm.MailboxCounts())