mirror of
https://github.com/mjl-/mox.git
synced 2024-12-28 01:13:47 +03:00
when moving/copying messages in imapserve, also ensure the message keywords make it into the destination mailbox keywords list
This commit is contained in:
parent
f9e261e0fb
commit
2e5376d7eb
2 changed files with 35 additions and 6 deletions
|
@ -60,6 +60,7 @@ import (
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
"golang.org/x/text/unicode/norm"
|
"golang.org/x/text/unicode/norm"
|
||||||
|
|
||||||
"github.com/mjl-/bstore"
|
"github.com/mjl-/bstore"
|
||||||
|
@ -2083,6 +2084,7 @@ func (c *conn) cmdRename(tag, cmd string, p *parser) {
|
||||||
Name: dst,
|
Name: dst,
|
||||||
UIDValidity: uidval,
|
UIDValidity: uidval,
|
||||||
UIDNext: 1,
|
UIDNext: 1,
|
||||||
|
Keywords: srcMB.Keywords,
|
||||||
}
|
}
|
||||||
err = tx.Insert(&dstMB)
|
err = tx.Insert(&dstMB)
|
||||||
xcheckf(err, "create new destination mailbox")
|
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.
|
// Reserve the uids in the destination mailbox.
|
||||||
uidFirst := mbDst.UIDNext
|
uidFirst := mbDst.UIDNext
|
||||||
mbDst.UIDNext += store.UID(len(uidargs))
|
mbDst.UIDNext += store.UID(len(uidargs))
|
||||||
err := tx.Update(&mbDst)
|
|
||||||
xcheckf(err, "reserve uid in destination mailbox")
|
|
||||||
|
|
||||||
// Fetch messages from database.
|
// Fetch messages from database.
|
||||||
q := bstore.QueryTx[store.Message](tx)
|
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()
|
conf, _ := c.account.Conf()
|
||||||
|
|
||||||
|
mbKeywords := map[string]struct{}{}
|
||||||
|
|
||||||
// Insert new messages into database.
|
// Insert new messages into database.
|
||||||
var origMsgIDs, newMsgIDs []int64
|
var origMsgIDs, newMsgIDs []int64
|
||||||
for i, uid := range uids {
|
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)
|
newMsgIDs = append(newMsgIDs, m.ID)
|
||||||
flags = append(flags, m.Flags)
|
flags = append(flags, m.Flags)
|
||||||
keywords = append(keywords, m.Keywords)
|
keywords = append(keywords, m.Keywords)
|
||||||
|
for _, kw := range m.Keywords {
|
||||||
|
mbKeywords[kw] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
qmr := bstore.QueryTx[store.Recipient](tx)
|
qmr := bstore.QueryTx[store.Recipient](tx)
|
||||||
qmr.FilterNonzero(store.Recipient{MessageID: origID})
|
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.
|
// Copy message files to new message ID's.
|
||||||
syncDirs := map[string]struct{}{}
|
syncDirs := map[string]struct{}{}
|
||||||
for i := range origMsgIDs {
|
for i := range origMsgIDs {
|
||||||
|
@ -3146,8 +3161,6 @@ func (c *conn) cmdxMove(isUID bool, tag, cmd string, p *parser) {
|
||||||
uidFirst := mbDst.UIDNext
|
uidFirst := mbDst.UIDNext
|
||||||
uidnext := uidFirst
|
uidnext := uidFirst
|
||||||
mbDst.UIDNext += store.UID(len(uids))
|
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.
|
// Update UID and MailboxID in database for messages.
|
||||||
q := bstore.QueryTx[store.Message](tx)
|
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")
|
xserverErrorf("uid and message mismatch")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
keywords := map[string]struct{}{}
|
||||||
|
|
||||||
conf, _ := c.account.Conf()
|
conf, _ := c.account.Conf()
|
||||||
for i := range msgs {
|
for i := range msgs {
|
||||||
m := &msgs[i]
|
m := &msgs[i]
|
||||||
|
@ -3178,7 +3193,21 @@ func (c *conn) cmdxMove(isUID bool, tag, cmd string, p *parser) {
|
||||||
uidnext++
|
uidnext++
|
||||||
err := tx.Update(m)
|
err := tx.Update(m)
|
||||||
xcheckf(err, "updating moved message in database")
|
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)
|
err = c.account.RetrainMessages(context.TODO(), c.log, tx, msgs, false)
|
||||||
xcheckf(err, "retraining messages after move")
|
xcheckf(err, "retraining messages after move")
|
||||||
|
|
|
@ -292,7 +292,7 @@ type Message struct {
|
||||||
|
|
||||||
MessageHash []byte // Hash of message. For rejects delivery, so optional like MessageID.
|
MessageHash []byte // Hash of message. For rejects delivery, so optional like MessageID.
|
||||||
Flags
|
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
|
Size int64
|
||||||
TrainedJunk *bool // If nil, no training done yet. Otherwise, true is trained as junk, false trained as nonjunk.
|
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.
|
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) {
|
func MergeKeywords(l, add []string) ([]string, bool) {
|
||||||
var changed bool
|
var changed bool
|
||||||
for _, k := range add {
|
for _, k := range add {
|
||||||
if slices.Index(l, k) < 0 {
|
if !slices.Contains(l, k) {
|
||||||
l = append(l, k)
|
l = append(l, k)
|
||||||
changed = true
|
changed = true
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue