imapserver: implement the MULTISEARCH extension, with its ESEARCH command

This commit is contained in:
Mechiel Lukkien
2025-03-31 18:33:15 +02:00
parent 5dcf674761
commit 479bf29124
11 changed files with 969 additions and 188 deletions

View File

@ -1414,15 +1414,54 @@ func (c *Conn) xneedDisabled(msg string, caps ...Capability) {
// ../rfc/9051:6546
// Already consumed: "ESEARCH"
func (c *Conn) xesearchResponse() (r UntaggedEsearch) {
if !c.space() {
return
}
if c.take('(') {
// ../rfc/9051:6921
c.xtake("TAG")
c.xspace()
r.Correlator = c.xastring()
// ../rfc/9051:6921 ../rfc/7377:465
seen := map[string]bool{}
for {
var kind string
if c.peek('t') || c.peek('T') {
kind = "TAG"
c.xtake(kind)
c.xspace()
r.Tag = c.xastring()
} else if c.peek('m') || c.peek('M') {
kind = "MAILBOX"
c.xtake(kind)
c.xspace()
r.Mailbox = c.xastring()
if r.Mailbox == "" {
c.xerrorf("invalid empty mailbox in search correlator")
}
} else if c.peek('u') || c.peek('U') {
kind = "UIDVALIDITY"
c.xtake(kind)
c.xspace()
r.UIDValidity = c.xnzuint32()
} else {
c.xerrorf("expected tag/correlator, mailbox or uidvalidity")
}
if seen[kind] {
c.xerrorf("duplicate search correlator %q", kind)
}
seen[kind] = true
if !c.take(' ') {
break
}
}
if r.Tag == "" {
c.xerrorf("missing tag search correlator")
}
if (r.Mailbox != "") != (r.UIDValidity != 0) {
c.xerrorf("mailbox and uidvalidity correlators must both be absent or both be present")
}
c.xtake(")")
}
if !c.space() {

View File

@ -41,6 +41,7 @@ const (
CapMultiAppend Capability = "MULTIAPPEND" // ../rfc/3502:33
CapReplace Capability = "REPLACE" // ../rfc/8508:155
CapPreview Capability = "PREVIEW" // ../rfc/8970:114
CapMultiSearch Capability = "MULTISEARCH" // ../rfc/7377:187
)
// Status is the tagged final result of a command.
@ -314,15 +315,17 @@ type UntaggedLsub struct {
// Fields are optional and zero if absent.
type UntaggedEsearch struct {
// ../rfc/9051:6546
Correlator string
UID bool
Min uint32
Max uint32
All NumSet
Count *uint32
ModSeq int64
Exts []EsearchDataExt
Tag string // ../rfc/9051:6546
Mailbox string // For MULTISEARCH. ../rfc/7377:437
UIDValidity uint32 // For MULTISEARCH, ../rfc/7377:438
UID bool
Min uint32
Max uint32
All NumSet
Count *uint32
ModSeq int64
Exts []EsearchDataExt
}
// UntaggedVanished is used in QRESYNC to send UIDs that have been removed.