mirror of
https://github.com/mjl-/mox.git
synced 2025-07-13 06:14:38 +03:00
imapserver: implement rfc 9590, returning metadata in the extended list command
only with "return" including "metadata". so clients can quickly get certain metadata (eg for display, such as a color) for mailboxes. this also adds a protocol token type "mailboxt" that properly encodes to utf7 if required.
This commit is contained in:
@ -164,12 +164,13 @@ var authFailDelay = time.Second // After authentication failure.
|
||||
// WITHIN: ../rfc/5032
|
||||
// NAMESPACE: ../rfc/2342
|
||||
// COMPRESS=DEFLATE: ../rfc/4978
|
||||
// LIST-METADATA: ../rfc/9590
|
||||
//
|
||||
// We always announce support for SCRAM PLUS-variants, also on connections without
|
||||
// TLS. The client should not be selecting PLUS variants on non-TLS connections,
|
||||
// instead opting to do the bare SCRAM variant without indicating the server claims
|
||||
// to support the PLUS variant (skipping the server downgrade detection check).
|
||||
const serverCapabilities = "IMAP4rev2 IMAP4rev1 ENABLE LITERAL+ IDLE SASL-IR BINARY UNSELECT UIDPLUS ESEARCH SEARCHRES MOVE UTF8=ACCEPT LIST-EXTENDED SPECIAL-USE CREATE-SPECIAL-USE LIST-STATUS AUTH=SCRAM-SHA-256-PLUS AUTH=SCRAM-SHA-256 AUTH=SCRAM-SHA-1-PLUS AUTH=SCRAM-SHA-1 AUTH=CRAM-MD5 ID APPENDLIMIT=9223372036854775807 CONDSTORE QRESYNC STATUS=SIZE QUOTA QUOTA=RES-STORAGE METADATA SAVEDATE WITHIN NAMESPACE COMPRESS=DEFLATE"
|
||||
const serverCapabilities = "IMAP4rev2 IMAP4rev1 ENABLE LITERAL+ IDLE SASL-IR BINARY UNSELECT UIDPLUS ESEARCH SEARCHRES MOVE UTF8=ACCEPT LIST-EXTENDED SPECIAL-USE CREATE-SPECIAL-USE LIST-STATUS AUTH=SCRAM-SHA-256-PLUS AUTH=SCRAM-SHA-256 AUTH=SCRAM-SHA-1-PLUS AUTH=SCRAM-SHA-1 AUTH=CRAM-MD5 ID APPENDLIMIT=9223372036854775807 CONDSTORE QRESYNC STATUS=SIZE QUOTA QUOTA=RES-STORAGE METADATA SAVEDATE WITHIN NAMESPACE COMPRESS=DEFLATE LIST-METADATA"
|
||||
|
||||
type conn struct {
|
||||
cid int64
|
||||
@ -441,13 +442,6 @@ func (c *conn) utf8strings() bool {
|
||||
return c.enabled[capIMAP4rev2] || c.enabled[capUTF8Accept]
|
||||
}
|
||||
|
||||
func (c *conn) encodeMailbox(s string) string {
|
||||
if c.utf8strings() {
|
||||
return s
|
||||
}
|
||||
return utf7encode(s)
|
||||
}
|
||||
|
||||
func (c *conn) xdbwrite(fn func(tx *bstore.Tx)) {
|
||||
err := c.account.DB.Write(context.TODO(), func(tx *bstore.Tx) error {
|
||||
fn(tx)
|
||||
@ -1686,22 +1680,22 @@ func (c *conn) applyChanges(changes []store.Change, initial bool) {
|
||||
// unrecognized \NonExistent and interpret this as a newly created mailbox, while
|
||||
// the goal was to remove it...
|
||||
if c.enabled[capIMAP4rev2] {
|
||||
c.bwritelinef(`* LIST (\NonExistent) "/" %s`, astring(c.encodeMailbox(ch.Name)).pack(c))
|
||||
c.bwritelinef(`* LIST (\NonExistent) "/" %s`, mailboxt(ch.Name).pack(c))
|
||||
}
|
||||
case store.ChangeAddMailbox:
|
||||
c.bwritelinef(`* LIST (%s) "/" %s`, strings.Join(ch.Flags, " "), astring(c.encodeMailbox(ch.Mailbox.Name)).pack(c))
|
||||
c.bwritelinef(`* LIST (%s) "/" %s`, strings.Join(ch.Flags, " "), mailboxt(ch.Mailbox.Name).pack(c))
|
||||
case store.ChangeRenameMailbox:
|
||||
// OLDNAME only with IMAP4rev2 or NOTIFY ../rfc/9051:2726 ../rfc/5465:628
|
||||
var oldname string
|
||||
if c.enabled[capIMAP4rev2] {
|
||||
oldname = fmt.Sprintf(` ("OLDNAME" (%s))`, string0(c.encodeMailbox(ch.OldName)).pack(c))
|
||||
oldname = fmt.Sprintf(` ("OLDNAME" (%s))`, mailboxt(ch.OldName).pack(c))
|
||||
}
|
||||
c.bwritelinef(`* LIST (%s) "/" %s%s`, strings.Join(ch.Flags, " "), astring(c.encodeMailbox(ch.NewName)).pack(c), oldname)
|
||||
c.bwritelinef(`* LIST (%s) "/" %s%s`, strings.Join(ch.Flags, " "), mailboxt(ch.NewName).pack(c), oldname)
|
||||
case store.ChangeAddSubscription:
|
||||
c.bwritelinef(`* LIST (%s) "/" %s`, strings.Join(append([]string{`\Subscribed`}, ch.Flags...), " "), astring(c.encodeMailbox(ch.Name)).pack(c))
|
||||
c.bwritelinef(`* LIST (%s) "/" %s`, strings.Join(append([]string{`\Subscribed`}, ch.Flags...), " "), mailboxt(ch.Name).pack(c))
|
||||
case store.ChangeAnnotation:
|
||||
// ../rfc/5464:807 ../rfc/5464:788
|
||||
c.bwritelinef(`* METADATA %s %s`, astring(c.encodeMailbox(ch.MailboxName)).pack(c), astring(ch.Key).pack(c))
|
||||
c.bwritelinef(`* METADATA %s %s`, mailboxt(ch.MailboxName).pack(c), astring(ch.Key).pack(c))
|
||||
default:
|
||||
panic(fmt.Sprintf("internal error, missing case for %#v", change))
|
||||
}
|
||||
@ -2607,7 +2601,7 @@ func (c *conn) cmdSelectExamine(isselect bool, tag, cmd string, p *parser) {
|
||||
}
|
||||
c.bwritelinef(`* OK [UIDVALIDITY %d] x`, mb.UIDValidity)
|
||||
c.bwritelinef(`* OK [UIDNEXT %d] x`, mb.UIDNext)
|
||||
c.bwritelinef(`* LIST () "/" %s`, astring(c.encodeMailbox(mb.Name)).pack(c))
|
||||
c.bwritelinef(`* LIST () "/" %s`, mailboxt(mb.Name).pack(c))
|
||||
if c.enabled[capCondstore] {
|
||||
// ../rfc/7162:417
|
||||
// ../rfc/7162-eid5055 ../rfc/7162:484 ../rfc/7162:1167
|
||||
@ -2842,9 +2836,9 @@ func (c *conn) cmdCreate(tag, cmd string, p *parser) {
|
||||
var oldname string
|
||||
// OLDNAME only with IMAP4rev2 or NOTIFY ../rfc/9051:2726 ../rfc/5465:628
|
||||
if c.enabled[capIMAP4rev2] && n == name && name != origName && !(name == "Inbox" || strings.HasPrefix(name, "Inbox/")) {
|
||||
oldname = fmt.Sprintf(` ("OLDNAME" (%s))`, string0(c.encodeMailbox(origName)).pack(c))
|
||||
oldname = fmt.Sprintf(` ("OLDNAME" (%s))`, mailboxt(origName).pack(c))
|
||||
}
|
||||
c.bwritelinef(`* LIST (\Subscribed) "/" %s%s`, astring(c.encodeMailbox(n)).pack(c), oldname)
|
||||
c.bwritelinef(`* LIST (\Subscribed) "/" %s%s`, mailboxt(n).pack(c), oldname)
|
||||
}
|
||||
c.ok(tag, cmd)
|
||||
}
|
||||
@ -3152,7 +3146,7 @@ func (c *conn) cmdLsub(tag, cmd string, p *parser) {
|
||||
continue
|
||||
}
|
||||
have[name] = true
|
||||
line := fmt.Sprintf(`* LSUB () "/" %s`, astring(c.encodeMailbox(name)).pack(c))
|
||||
line := fmt.Sprintf(`* LSUB () "/" %s`, mailboxt(name).pack(c))
|
||||
lines = append(lines, line)
|
||||
|
||||
}
|
||||
@ -3167,7 +3161,7 @@ func (c *conn) cmdLsub(tag, cmd string, p *parser) {
|
||||
if have[mb.Name] || !subscribedKids[mb.Name] || !re.MatchString(mb.Name) {
|
||||
return nil
|
||||
}
|
||||
line := fmt.Sprintf(`* LSUB (\NoSelect) "/" %s`, astring(c.encodeMailbox(mb.Name)).pack(c))
|
||||
line := fmt.Sprintf(`* LSUB (\NoSelect) "/" %s`, mailboxt(mb.Name).pack(c))
|
||||
lines = append(lines, line)
|
||||
return nil
|
||||
})
|
||||
@ -3274,7 +3268,7 @@ func (c *conn) xstatusLine(tx *bstore.Tx, mb store.Mailbox, attrs []string) stri
|
||||
xsyntaxErrorf("unknown attribute %q", a)
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("* STATUS %s (%s)", astring(c.encodeMailbox(mb.Name)).pack(c), strings.Join(status, " "))
|
||||
return fmt.Sprintf("* STATUS %s (%s)", mailboxt(mb.Name).pack(c), strings.Join(status, " "))
|
||||
}
|
||||
|
||||
func flaglist(fl store.Flags, keywords []string) listspace {
|
||||
|
Reference in New Issue
Block a user