fix(cli): reuse folder uidvalidity from setup in AckCmd

Thread uidv through setup's return value (new uint32 before the cleanup
func) so AckCmd no longer makes a redundant SelectFolder round-trip that
silently returned 0 on failure and recorded acks under the wrong
UID-validity epoch. All four callers updated; read-only callers ignore
the value with _.
This commit is contained in:
2026-06-22 00:06:26 +01:00
parent ccf6fa0542
commit e1d86dc587
+11 -12
View File
@@ -36,16 +36,16 @@ func (d Deps) audit(account, action, target, result, reason string) {
// setup loads the account, builds the inbound rule, dials IMAP, and selects the // setup loads the account, builds the inbound rule, dials IMAP, and selects the
// folder (establishing the baseline). Returns a cleanup func. // folder (establishing the baseline). Returns a cleanup func.
func (d Deps) setup(account, folder string) (store.Account, policy.InboundRule, Mailer, func(), *Envelope) { func (d Deps) setup(account, folder string) (store.Account, policy.InboundRule, Mailer, uint32, func(), *Envelope) {
acc, err := d.Store.GetAccount(account) acc, err := d.Store.GetAccount(account)
if err != nil { if err != nil {
e := Failure(CodeNotFound, "account not found: "+account) e := Failure(CodeNotFound, "account not found: "+account)
return acc, policy.InboundRule{}, nil, nil, &e return acc, policy.InboundRule{}, nil, 0, nil, &e
} }
re, err := policy.CompileSubject(acc.SubjectRegex) re, err := policy.CompileSubject(acc.SubjectRegex)
if err != nil { if err != nil {
e := Failure(CodeConfig, "invalid subject_regex: "+err.Error()) e := Failure(CodeConfig, "invalid subject_regex: "+err.Error())
return acc, policy.InboundRule{}, nil, nil, &e return acc, policy.InboundRule{}, nil, 0, nil, &e
} }
wlIn, _ := d.Store.ListWhitelist(account, store.DirIn) wlIn, _ := d.Store.ListWhitelist(account, store.DirIn)
rule := policy.InboundRule{ rule := policy.InboundRule{
@@ -56,20 +56,20 @@ func (d Deps) setup(account, folder string) (store.Account, policy.InboundRule,
m, err := d.Dial(acc) m, err := d.Dial(acc)
if err != nil { if err != nil {
e := Failure(CodeNetwork, "imap connect failed: "+err.Error()) e := Failure(CodeNetwork, "imap connect failed: "+err.Error())
return acc, rule, nil, nil, &e return acc, rule, nil, 0, nil, &e
} }
uidv, maxUID, err := m.SelectFolder(folder) uidv, maxUID, err := m.SelectFolder(folder)
if err != nil { if err != nil {
m.Logout() m.Logout()
e := Failure(CodeNetwork, "select folder failed: "+err.Error()) e := Failure(CodeNetwork, "select folder failed: "+err.Error())
return acc, rule, nil, nil, &e return acc, rule, nil, 0, nil, &e
} }
if err := d.Store.EnsureFolderBaseline(account, folder, uidv, maxUID); err != nil { if err := d.Store.EnsureFolderBaseline(account, folder, uidv, maxUID); err != nil {
m.Logout() m.Logout()
e := Failure(CodeDB, err.Error()) e := Failure(CodeDB, err.Error())
return acc, rule, nil, nil, &e return acc, rule, nil, 0, nil, &e
} }
return acc, rule, m, func() { m.Logout() }, nil return acc, rule, m, uidv, func() { m.Logout() }, nil
} }
func headerMap(h mail.Header) map[string]any { func headerMap(h mail.Header) map[string]any {
@@ -80,7 +80,7 @@ func headerMap(h mail.Header) map[string]any {
} }
func ListCmd(d Deps, account, folder string, onlyNew bool, beforeUID, sinceUID uint32, limit int) error { func ListCmd(d Deps, account, folder string, onlyNew bool, beforeUID, sinceUID uint32, limit int) error {
_, rule, m, done, fail := d.setup(account, folder) _, rule, m, _, done, fail := d.setup(account, folder)
if fail != nil { if fail != nil {
return d.emit(*fail) return d.emit(*fail)
} }
@@ -134,7 +134,7 @@ func (d Deps) visible(m Mailer, rule policy.InboundRule, folder string, uid uint
} }
func GetCmd(d Deps, account, folder string, uid uint32) error { func GetCmd(d Deps, account, folder string, uid uint32) error {
_, rule, m, done, fail := d.setup(account, folder) _, rule, m, _, done, fail := d.setup(account, folder)
if fail != nil { if fail != nil {
return d.emit(*fail) return d.emit(*fail)
} }
@@ -169,7 +169,7 @@ func GetCmd(d Deps, account, folder string, uid uint32) error {
} }
func SearchCmd(d Deps, account, folder string, sc mail.SearchCriteria, limit int) error { func SearchCmd(d Deps, account, folder string, sc mail.SearchCriteria, limit int) error {
_, rule, m, done, fail := d.setup(account, folder) _, rule, m, _, done, fail := d.setup(account, folder)
if fail != nil { if fail != nil {
return d.emit(*fail) return d.emit(*fail)
} }
@@ -193,12 +193,11 @@ func SearchCmd(d Deps, account, folder string, sc mail.SearchCriteria, limit int
} }
func AckCmd(d Deps, account, folder string, uids []uint32) error { func AckCmd(d Deps, account, folder string, uids []uint32) error {
_, rule, m, done, fail := d.setup(account, folder) _, rule, m, uidv, done, fail := d.setup(account, folder)
if fail != nil { if fail != nil {
return d.emit(*fail) return d.emit(*fail)
} }
defer done() defer done()
uidv, _, _ := m.SelectFolder(folder)
for _, uid := range uids { for _, uid := range uids {
ok, err := d.visible(m, rule, folder, uid) ok, err := d.visible(m, rule, folder, uid)
if err != nil { if err != nil {