From e1d86dc587a1101c517b728ba491bbf2a653aefa Mon Sep 17 00:00:00 2001 From: Steve Cliff Date: Mon, 22 Jun 2026 00:06:26 +0100 Subject: [PATCH] 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 _. --- internal/cli/agent.go | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/internal/cli/agent.go b/internal/cli/agent.go index 24e0ab5..0a6a7e0 100644 --- a/internal/cli/agent.go +++ b/internal/cli/agent.go @@ -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 // 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) if err != nil { 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) if err != nil { 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) rule := policy.InboundRule{ @@ -56,20 +56,20 @@ func (d Deps) setup(account, folder string) (store.Account, policy.InboundRule, m, err := d.Dial(acc) if err != nil { 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) if err != nil { m.Logout() 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 { m.Logout() 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 { @@ -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 { - _, rule, m, done, fail := d.setup(account, folder) + _, rule, m, _, done, fail := d.setup(account, folder) if fail != nil { 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 { - _, rule, m, done, fail := d.setup(account, folder) + _, rule, m, _, done, fail := d.setup(account, folder) if fail != nil { 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 { - _, rule, m, done, fail := d.setup(account, folder) + _, rule, m, _, done, fail := d.setup(account, folder) if fail != nil { 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 { - _, rule, m, done, fail := d.setup(account, folder) + _, rule, m, uidv, done, fail := d.setup(account, folder) if fail != nil { return d.emit(*fail) } defer done() - uidv, _, _ := m.SelectFolder(folder) for _, uid := range uids { ok, err := d.visible(m, rule, folder, uid) if err != nil {