when moving/copying messages in imapserve, also ensure the message keywords make it into the destination mailbox keywords list

This commit is contained in:
Mechiel Lukkien 2023-07-24 08:49:19 +02:00
parent f9e261e0fb
commit 2e5376d7eb
No known key found for this signature in database
2 changed files with 35 additions and 6 deletions

View file

@ -60,6 +60,7 @@ import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"golang.org/x/exp/slices"
"golang.org/x/text/unicode/norm"
"github.com/mjl-/bstore"
@ -2083,6 +2084,7 @@ func (c *conn) cmdRename(tag, cmd string, p *parser) {
Name: dst,
UIDValidity: uidval,
UIDNext: 1,
Keywords: srcMB.Keywords,
}
err = tx.Insert(&dstMB)
xcheckf(err, "create new destination mailbox")
@ -3001,8 +3003,6 @@ 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(uidargs))
err := tx.Update(&mbDst)
xcheckf(err, "reserve uid in destination mailbox")
// Fetch messages from database.
q := bstore.QueryTx[store.Message](tx)
@ -3023,6 +3023,8 @@ func (c *conn) cmdxCopy(isUID bool, tag, cmd string, p *parser) {
conf, _ := c.account.Conf()
mbKeywords := map[string]struct{}{}
// Insert new messages into database.
var origMsgIDs, newMsgIDs []int64
for i, uid := range uids {
@ -3051,6 +3053,9 @@ func (c *conn) cmdxCopy(isUID bool, tag, cmd string, p *parser) {
newMsgIDs = append(newMsgIDs, m.ID)
flags = append(flags, m.Flags)
keywords = append(keywords, m.Keywords)
for _, kw := range m.Keywords {
mbKeywords[kw] = struct{}{}
}
qmr := bstore.QueryTx[store.Recipient](tx)
qmr.FilterNonzero(store.Recipient{MessageID: origID})
@ -3064,6 +3069,16 @@ func (c *conn) cmdxCopy(isUID bool, tag, cmd string, p *parser) {
}
}
// Ensure destination mailbox has keywords of the moved messages.
for kw := range mbKeywords {
if !slices.Contains(mbDst.Keywords, kw) {
mbDst.Keywords = append(mbDst.Keywords, kw)
}
}
err = tx.Update(&mbDst)
xcheckf(err, "updating destination mailbox for uids and keywords")
// Copy message files to new message ID's.
syncDirs := map[string]struct{}{}
for i := range origMsgIDs {
@ -3146,8 +3161,6 @@ func (c *conn) cmdxMove(isUID bool, tag, cmd string, p *parser) {
uidFirst := mbDst.UIDNext
uidnext := uidFirst
mbDst.UIDNext += store.UID(len(uids))
err := tx.Update(&mbDst)
xcheckf(err, "reserve uids in destination mailbox")
// Update UID and MailboxID in database for messages.
q := bstore.QueryTx[store.Message](tx)
@ -3161,6 +3174,8 @@ func (c *conn) cmdxMove(isUID bool, tag, cmd string, p *parser) {
xserverErrorf("uid and message mismatch")
}
keywords := map[string]struct{}{}
conf, _ := c.account.Conf()
for i := range msgs {
m := &msgs[i]
@ -3178,8 +3193,22 @@ func (c *conn) cmdxMove(isUID bool, tag, cmd string, p *parser) {
uidnext++
err := tx.Update(m)
xcheckf(err, "updating moved message in database")
for _, kw := range m.Keywords {
keywords[kw] = struct{}{}
}
}
// Ensure destination mailbox has keywords of the moved messages.
for kw := range keywords {
if !slices.Contains(mbDst.Keywords, kw) {
mbDst.Keywords = append(mbDst.Keywords, kw)
}
}
err = tx.Update(&mbDst)
xcheckf(err, "updating destination mailbox for uids and keywords")
err = c.account.RetrainMessages(context.TODO(), c.log, tx, msgs, false)
xcheckf(err, "retraining messages after move")

View file

@ -292,7 +292,7 @@ type Message struct {
MessageHash []byte // Hash of message. For rejects delivery, so optional like MessageID.
Flags
Keywords []string `bstore:"index"` // Non-system or well-known $-flags. Only in "atom" syntax, stored in lower case.
Keywords []string `bstore:"index"` // For keywords other than system flags or the basic well-known $-flags. Only in "atom" syntax, stored in lower case.
Size int64
TrainedJunk *bool // If nil, no training done yet. Otherwise, true is trained as junk, false trained as nonjunk.
MsgPrefix []byte // Typically holds received headers and/or header separator.
@ -1345,7 +1345,7 @@ func RemoveKeywords(l, remove []string) []string {
func MergeKeywords(l, add []string) ([]string, bool) {
var changed bool
for _, k := range add {
if slices.Index(l, k) < 0 {
if !slices.Contains(l, k) {
l = append(l, k)
changed = true
}