213 lines
6.4 KiB
Go
213 lines
6.4 KiB
Go
package imap
|
|
|
|
import (
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// Cap represents an IMAP capability.
|
|
type Cap string
|
|
|
|
// Registered capabilities.
|
|
//
|
|
// See: https://www.iana.org/assignments/imap-capabilities/
|
|
const (
|
|
CapIMAP4rev1 Cap = "IMAP4rev1" // RFC 3501
|
|
CapIMAP4rev2 Cap = "IMAP4rev2" // RFC 9051
|
|
|
|
CapStartTLS Cap = "STARTTLS"
|
|
CapLoginDisabled Cap = "LOGINDISABLED"
|
|
|
|
// Folded in IMAP4rev2
|
|
CapNamespace Cap = "NAMESPACE" // RFC 2342
|
|
CapUnselect Cap = "UNSELECT" // RFC 3691
|
|
CapUIDPlus Cap = "UIDPLUS" // RFC 4315
|
|
CapESearch Cap = "ESEARCH" // RFC 4731
|
|
CapSearchRes Cap = "SEARCHRES" // RFC 5182
|
|
CapEnable Cap = "ENABLE" // RFC 5161
|
|
CapIdle Cap = "IDLE" // RFC 2177
|
|
CapSASLIR Cap = "SASL-IR" // RFC 4959
|
|
CapListExtended Cap = "LIST-EXTENDED" // RFC 5258
|
|
CapListStatus Cap = "LIST-STATUS" // RFC 5819
|
|
CapMove Cap = "MOVE" // RFC 6851
|
|
CapLiteralMinus Cap = "LITERAL-" // RFC 7888
|
|
CapStatusSize Cap = "STATUS=SIZE" // RFC 8438
|
|
CapChildren Cap = "CHILDREN" // RFC 3348
|
|
|
|
CapACL Cap = "ACL" // RFC 4314
|
|
CapAppendLimit Cap = "APPENDLIMIT" // RFC 7889
|
|
CapBinary Cap = "BINARY" // RFC 3516
|
|
CapCatenate Cap = "CATENATE" // RFC 4469
|
|
CapCondStore Cap = "CONDSTORE" // RFC 7162
|
|
CapConvert Cap = "CONVERT" // RFC 5259
|
|
CapCreateSpecialUse Cap = "CREATE-SPECIAL-USE" // RFC 6154
|
|
CapESort Cap = "ESORT" // RFC 5267
|
|
CapFilters Cap = "FILTERS" // RFC 5466
|
|
CapID Cap = "ID" // RFC 2971
|
|
CapLanguage Cap = "LANGUAGE" // RFC 5255
|
|
CapListMyRights Cap = "LIST-MYRIGHTS" // RFC 8440
|
|
CapLiteralPlus Cap = "LITERAL+" // RFC 7888
|
|
CapLoginReferrals Cap = "LOGIN-REFERRALS" // RFC 2221
|
|
CapMailboxReferrals Cap = "MAILBOX-REFERRALS" // RFC 2193
|
|
CapMetadata Cap = "METADATA" // RFC 5464
|
|
CapMetadataServer Cap = "METADATA-SERVER" // RFC 5464
|
|
CapMultiAppend Cap = "MULTIAPPEND" // RFC 3502
|
|
CapMultiSearch Cap = "MULTISEARCH" // RFC 7377
|
|
CapNotify Cap = "NOTIFY" // RFC 5465
|
|
CapObjectID Cap = "OBJECTID" // RFC 8474
|
|
CapPreview Cap = "PREVIEW" // RFC 8970
|
|
CapQResync Cap = "QRESYNC" // RFC 7162
|
|
CapQuota Cap = "QUOTA" // RFC 9208
|
|
CapQuotaSet Cap = "QUOTASET" // RFC 9208
|
|
CapReplace Cap = "REPLACE" // RFC 8508
|
|
CapSaveDate Cap = "SAVEDATE" // RFC 8514
|
|
CapSearchFuzzy Cap = "SEARCH=FUZZY" // RFC 6203
|
|
CapSort Cap = "SORT" // RFC 5256
|
|
CapSortDisplay Cap = "SORT=DISPLAY" // RFC 5957
|
|
CapSpecialUse Cap = "SPECIAL-USE" // RFC 6154
|
|
CapUnauthenticate Cap = "UNAUTHENTICATE" // RFC 8437
|
|
CapURLPartial Cap = "URL-PARTIAL" // RFC 5550
|
|
CapURLAuth Cap = "URLAUTH" // RFC 4467
|
|
CapUTF8Accept Cap = "UTF8=ACCEPT" // RFC 6855
|
|
CapUTF8Only Cap = "UTF8=ONLY" // RFC 6855
|
|
CapWithin Cap = "WITHIN" // RFC 5032
|
|
CapUIDOnly Cap = "UIDONLY" // RFC 9586
|
|
CapListMetadata Cap = "LIST-METADATA" // RFC 9590
|
|
CapInProgress Cap = "INPROGRESS" // RFC 9585
|
|
)
|
|
|
|
var imap4rev2Caps = CapSet{
|
|
CapNamespace: {},
|
|
CapUnselect: {},
|
|
CapUIDPlus: {},
|
|
CapESearch: {},
|
|
CapSearchRes: {},
|
|
CapEnable: {},
|
|
CapIdle: {},
|
|
CapSASLIR: {},
|
|
CapListExtended: {},
|
|
CapListStatus: {},
|
|
CapMove: {},
|
|
CapLiteralMinus: {},
|
|
CapStatusSize: {},
|
|
CapChildren: {},
|
|
}
|
|
|
|
// AuthCap returns the capability name for an SASL authentication mechanism.
|
|
func AuthCap(mechanism string) Cap {
|
|
return Cap("AUTH=" + mechanism)
|
|
}
|
|
|
|
// CapSet is a set of capabilities.
|
|
type CapSet map[Cap]struct{}
|
|
|
|
func (set CapSet) has(c Cap) bool {
|
|
_, ok := set[c]
|
|
return ok
|
|
}
|
|
|
|
func (set CapSet) Copy() CapSet {
|
|
newSet := make(CapSet, len(set))
|
|
for c := range set {
|
|
newSet[c] = struct{}{}
|
|
}
|
|
return newSet
|
|
}
|
|
|
|
// Has checks whether a capability is supported.
|
|
//
|
|
// Some capabilities are implied by others, as such Has may return true even if
|
|
// the capability is not in the map.
|
|
func (set CapSet) Has(c Cap) bool {
|
|
if set.has(c) {
|
|
return true
|
|
}
|
|
|
|
if set.has(CapIMAP4rev2) && imap4rev2Caps.has(c) {
|
|
return true
|
|
}
|
|
|
|
if c == CapLiteralMinus && set.has(CapLiteralPlus) {
|
|
return true
|
|
}
|
|
if c == CapCondStore && set.has(CapQResync) {
|
|
return true
|
|
}
|
|
if c == CapUTF8Accept && set.has(CapUTF8Only) {
|
|
return true
|
|
}
|
|
if c == CapAppendLimit {
|
|
_, ok := set.AppendLimit()
|
|
return ok
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// AuthMechanisms returns the list of supported SASL mechanisms for
|
|
// authentication.
|
|
func (set CapSet) AuthMechanisms() []string {
|
|
var l []string
|
|
for c := range set {
|
|
if !strings.HasPrefix(string(c), "AUTH=") {
|
|
continue
|
|
}
|
|
mech := strings.TrimPrefix(string(c), "AUTH=")
|
|
l = append(l, mech)
|
|
}
|
|
return l
|
|
}
|
|
|
|
// AppendLimit checks the APPENDLIMIT capability.
|
|
//
|
|
// If the server supports APPENDLIMIT, ok is true. If the server doesn't have
|
|
// the same upload limit for all mailboxes, limit is nil and per-mailbox
|
|
// limits must be queried via STATUS.
|
|
func (set CapSet) AppendLimit() (limit *uint32, ok bool) {
|
|
if set.has(CapAppendLimit) {
|
|
return nil, true
|
|
}
|
|
|
|
for c := range set {
|
|
if !strings.HasPrefix(string(c), "APPENDLIMIT=") {
|
|
continue
|
|
}
|
|
|
|
limitStr := strings.TrimPrefix(string(c), "APPENDLIMIT=")
|
|
limit64, err := strconv.ParseUint(limitStr, 10, 32)
|
|
if err == nil && limit64 > 0 {
|
|
limit32 := uint32(limit64)
|
|
return &limit32, true
|
|
}
|
|
}
|
|
|
|
limit32 := ^uint32(0)
|
|
return &limit32, false
|
|
}
|
|
|
|
// QuotaResourceTypes returns the list of supported QUOTA resource types.
|
|
func (set CapSet) QuotaResourceTypes() []QuotaResourceType {
|
|
var l []QuotaResourceType
|
|
for c := range set {
|
|
if !strings.HasPrefix(string(c), "QUOTA=RES-") {
|
|
continue
|
|
}
|
|
t := strings.TrimPrefix(string(c), "QUOTA=RES-")
|
|
l = append(l, QuotaResourceType(t))
|
|
}
|
|
return l
|
|
}
|
|
|
|
// ThreadAlgorithms returns the list of supported threading algorithms.
|
|
func (set CapSet) ThreadAlgorithms() []ThreadAlgorithm {
|
|
var l []ThreadAlgorithm
|
|
for c := range set {
|
|
if !strings.HasPrefix(string(c), "THREAD=") {
|
|
continue
|
|
}
|
|
alg := strings.TrimPrefix(string(c), "THREAD=")
|
|
l = append(l, ThreadAlgorithm(alg))
|
|
}
|
|
return l
|
|
}
|