implement IMAP CREATE-SPECIAL-USE extension for the mailbox create command, part of rfc 6154

we already supported special-use flags. settable through the webmail interface,
and new accounts already got standard mailboxes with special-use flags
predefined. but now the IMAP "CREATE" command implements creating mailboxes
with special-use flags.
This commit is contained in:
Mechiel Lukkien
2025-02-19 20:39:26 +01:00
parent 7288e038e6
commit dcaa99a85c
15 changed files with 167 additions and 58 deletions

View File

@ -34,8 +34,8 @@ type Conn struct {
Preauth bool
LastTag string
CapAvailable map[Capability]struct{} // Capabilities available at server, from CAPABILITY command or response code.
CapEnabled map[Capability]struct{} // Capabilities enabled through ENABLE command.
CapAvailable map[Capability]struct{} // Capabilities available at server, from CAPABILITY command or response code. All uppercase.
CapEnabled map[Capability]struct{} // Capabilities enabled through ENABLE command. All uppercase.
}
// Error is a parse or other protocol error.

View File

@ -149,9 +149,18 @@ func (c *Conn) Examine(mailbox string) (untagged []Untagged, result Result, rerr
}
// Create makes a new mailbox on the server.
func (c *Conn) Create(mailbox string) (untagged []Untagged, result Result, rerr error) {
// SpecialUse can only be used on servers that announced the CREATE-SPECIAL-USE
// capability. Specify flags like \Archive, \Draft, \Junk, \Sent, \Trash, \All.
func (c *Conn) Create(mailbox string, specialUse []string) (untagged []Untagged, result Result, rerr error) {
defer c.recover(&rerr)
return c.Transactf("create %s", astring(mailbox))
if _, ok := c.CapAvailable[CapCreateSpecialUse]; !ok && len(specialUse) > 0 {
c.xerrorf("server does not implement create-special-use extension")
}
var useStr string
if len(specialUse) > 0 {
useStr = fmt.Sprintf(" USE (%s)", strings.Join(specialUse, " "))
}
return c.Transactf("create %s%s", astring(mailbox), useStr)
}
// Delete removes an entire mailbox and its messages.

View File

@ -181,6 +181,7 @@ func (c *Conn) xrespCode() (string, CodeArg) {
}
c.CapAvailable = map[Capability]struct{}{}
for _, cap := range caps {
cap = strings.ToUpper(cap)
c.CapAvailable[Capability(cap)] = struct{}{}
}
codeArg = CodeWords{W, caps}
@ -343,6 +344,7 @@ func (c *Conn) xuntagged() Untagged {
}
c.CapAvailable = map[Capability]struct{}{}
for _, cap := range caps {
cap = strings.ToUpper(cap)
c.CapAvailable[Capability(cap)] = struct{}{}
}
r := UntaggedCapability(caps)
@ -356,6 +358,7 @@ func (c *Conn) xuntagged() Untagged {
caps = append(caps, c.xnonspace())
}
for _, cap := range caps {
cap = strings.ToUpper(cap)
c.CapEnabled[Capability(cap)] = struct{}{}
}
r := UntaggedEnabled(caps)

View File

@ -11,29 +11,31 @@ 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
CapMetadata Capability = "METADATA" // ../rfc/5464:124
CapMetadataServer Capability = "METADATA-SERVER" // ../rfc/5464:124
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
CapSaveDate Capability = "SAVEDATE" // ../rfc/8514
CapCreateSpecialUse Capability = "CREATE-SPECIAL-USE" // ../rfc/6154:296
)
// Status is the tagged final result of a command.