mirror of
https://github.com/mjl-/mox.git
synced 2025-07-12 17:04:39 +03:00
implement the imap metadata extension, rfc 5464
this allows setting per-mailbox and per-server annotations (metadata). we have a fixed maximum for total number of annotations (1000) and their total size (1000000 bytes). this size isn't held against the regular quota for simplicity. we send unsolicited metadata responses when a connection is in the idle command and a change to a metadata item is made. we currently only implement the /private/ namespace. we should implement the /shared/ namespace, for mox-global metadata annotations. only the admin should be able to configure those, probably through the config file, cli, or admin web interface. for issue #290
This commit is contained in:
@ -427,6 +427,50 @@ func (c *Conn) xuntagged() Untagged {
|
||||
c.xcrlf()
|
||||
return r
|
||||
|
||||
case "METADATA":
|
||||
// ../rfc/5464:807
|
||||
c.xspace()
|
||||
mailbox := c.xastring()
|
||||
c.xspace()
|
||||
if !c.take('(') {
|
||||
// Unsolicited form, with only annotation keys, not values.
|
||||
var keys []string
|
||||
for {
|
||||
key := c.xastring()
|
||||
keys = append(keys, key)
|
||||
if !c.space() {
|
||||
break
|
||||
}
|
||||
}
|
||||
c.xcrlf()
|
||||
return UntaggedMetadataKeys{mailbox, keys}
|
||||
}
|
||||
|
||||
// Form with values, in response to GETMETADATA command.
|
||||
r := UntaggedMetadataAnnotations{Mailbox: mailbox}
|
||||
for {
|
||||
key := c.xastring()
|
||||
c.xspace()
|
||||
var value []byte
|
||||
var isString bool
|
||||
if c.take('~') {
|
||||
value = c.xliteral()
|
||||
} else {
|
||||
value = []byte(c.xstring())
|
||||
isString = true
|
||||
// note: the abnf also allows nstring, but that only makes sense when the
|
||||
// production rule is used in the setmetadata command. ../rfc/5464:831
|
||||
}
|
||||
r.Annotations = append(r.Annotations, Annotation{key, isString, value})
|
||||
|
||||
if c.take(')') {
|
||||
break
|
||||
}
|
||||
c.xspace()
|
||||
}
|
||||
c.xcrlf()
|
||||
return r
|
||||
|
||||
case "NAMESPACE":
|
||||
// ../rfc/9051:6778
|
||||
c.xspace()
|
||||
|
@ -10,27 +10,29 @@ import (
|
||||
type Capability string
|
||||
|
||||
const (
|
||||
CapIMAP4rev1 Capability = "IMAP4rev1"
|
||||
CapIMAP4rev2 Capability = "IMAP4rev2"
|
||||
CapLoginDisabled Capability = "LOGINDISABLED"
|
||||
CapStarttls Capability = "STARTTLS"
|
||||
CapAuthPlain Capability = "AUTH=PLAIN"
|
||||
CapLiteralPlus Capability = "LITERAL+"
|
||||
CapLiteralMinus Capability = "LITERAL-"
|
||||
CapIdle Capability = "IDLE"
|
||||
CapNamespace Capability = "NAMESPACE"
|
||||
CapBinary Capability = "BINARY"
|
||||
CapUnselect Capability = "UNSELECT"
|
||||
CapUidplus Capability = "UIDPLUS"
|
||||
CapEsearch Capability = "ESEARCH"
|
||||
CapEnable Capability = "ENABLE"
|
||||
CapSave Capability = "SAVE"
|
||||
CapListExtended Capability = "LIST-EXTENDED"
|
||||
CapSpecialUse Capability = "SPECIAL-USE"
|
||||
CapMove Capability = "MOVE"
|
||||
CapUTF8Only Capability = "UTF8=ONLY"
|
||||
CapUTF8Accept Capability = "UTF8=ACCEPT"
|
||||
CapID Capability = "ID" // ../rfc/2971:80
|
||||
CapIMAP4rev1 Capability = "IMAP4rev1"
|
||||
CapIMAP4rev2 Capability = "IMAP4rev2"
|
||||
CapLoginDisabled Capability = "LOGINDISABLED"
|
||||
CapStarttls Capability = "STARTTLS"
|
||||
CapAuthPlain Capability = "AUTH=PLAIN"
|
||||
CapLiteralPlus Capability = "LITERAL+"
|
||||
CapLiteralMinus Capability = "LITERAL-"
|
||||
CapIdle Capability = "IDLE"
|
||||
CapNamespace Capability = "NAMESPACE"
|
||||
CapBinary Capability = "BINARY"
|
||||
CapUnselect Capability = "UNSELECT"
|
||||
CapUidplus Capability = "UIDPLUS"
|
||||
CapEsearch Capability = "ESEARCH"
|
||||
CapEnable Capability = "ENABLE"
|
||||
CapSave Capability = "SAVE"
|
||||
CapListExtended Capability = "LIST-EXTENDED"
|
||||
CapSpecialUse Capability = "SPECIAL-USE"
|
||||
CapMove Capability = "MOVE"
|
||||
CapUTF8Only Capability = "UTF8=ONLY"
|
||||
CapUTF8Accept Capability = "UTF8=ACCEPT"
|
||||
CapID Capability = "ID" // ../rfc/2971:80
|
||||
CapMetadata Capability = "METADATA" // ../rfc/5464:124
|
||||
CapMetadataServer Capability = "METADATA-SERVER" // ../rfc/5464:124
|
||||
)
|
||||
|
||||
// Status is the tagged final result of a command.
|
||||
@ -227,6 +229,27 @@ type UntaggedStatus struct {
|
||||
Attrs map[StatusAttr]int64 // Upper case status attributes.
|
||||
}
|
||||
|
||||
// ../rfc/5464:716 Unsolicited response, indicating an annotation has changed.
|
||||
type UntaggedMetadataKeys struct {
|
||||
Mailbox string // Empty means not specific to mailbox.
|
||||
|
||||
// Keys that have changed. To get values (or determine absence), the server must be
|
||||
// queried.
|
||||
Keys []string
|
||||
}
|
||||
|
||||
type Annotation struct {
|
||||
Key string
|
||||
IsString bool
|
||||
Value []byte
|
||||
}
|
||||
|
||||
// ../rfc/5464:683
|
||||
type UntaggedMetadataAnnotations struct {
|
||||
Mailbox string // Empty means not specific to mailbox.
|
||||
Annotations []Annotation
|
||||
}
|
||||
|
||||
// ../rfc/9051:7059 ../9208:712
|
||||
type StatusAttr string
|
||||
|
||||
|
Reference in New Issue
Block a user