Skip to content

Commit 9452482

Browse files
committed
imapserver: add SupportedCaps
1 parent d72bdf6 commit 9452482

File tree

7 files changed

+74
-27
lines changed

7 files changed

+74
-27
lines changed

cmd/imapmemserver/main.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,9 @@ func main() {
114114
NewSession: func(conn *imapserver.Conn) (imapserver.Session, *imapserver.GreetingData, error) {
115115
return memServer.NewSession(), nil, nil
116116
},
117-
Caps: imap.CapSet{
118-
imap.CapIMAP4rev1: {},
119-
imap.CapIMAP4rev2: {},
117+
Caps: &imapserver.SupportedCaps{
118+
IMAP4rev1: true,
119+
IMAP4rev2: true,
120120
},
121121
TLSConfig: tlsConfig,
122122
InsecureAuth: insecureAuth,

imapclient/client_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,9 @@ func newMemClientServerPair(t *testing.T) (net.Conn, io.Closer) {
9999
Certificates: []tls.Certificate{cert},
100100
},
101101
InsecureAuth: true,
102-
Caps: imap.CapSet{
103-
imap.CapIMAP4rev1: {},
104-
imap.CapIMAP4rev2: {},
102+
Caps: &imapserver.SupportedCaps{
103+
IMAP4rev1: true,
104+
IMAP4rev2: true,
105105
},
106106
})
107107

imapserver/capability.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ func (c *Conn) handleCapability(dec *imapwire.Decoder) error {
2828
// Some extensions (e.g. SASL-IR, ENABLE) don't require backend support and
2929
// thus are always enabled.
3030
func (c *Conn) availableCaps() []imap.Cap {
31-
available := c.server.options.caps()
31+
available := c.server.options.caps().set()
3232

3333
var caps []imap.Cap
3434
addAvailableCaps(&caps, available, []imap.Cap{

imapserver/conn.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ func (c *Conn) serve() {
140140
}
141141
}()
142142

143-
caps := c.server.options.caps()
143+
caps := c.server.options.caps().set()
144144
if _, ok := c.session.(SessionIMAP4rev2); !ok && caps.Has(imap.CapIMAP4rev2) {
145145
panic("imapserver: server advertises IMAP4rev2 but session doesn't support it")
146146
}
@@ -401,7 +401,7 @@ func (c *Conn) checkBufferedLiteral(size int64, nonSync bool) error {
401401
}
402402

403403
func (c *Conn) acceptLiteral(size int64, nonSync bool) error {
404-
if nonSync && size > 4096 && !c.server.options.caps().Has(imap.CapLiteralPlus) {
404+
if nonSync && size > 4096 && !c.server.options.caps().set().Has(imap.CapLiteralPlus) {
405405
return &imap.Error{
406406
Type: imap.StatusResponseTypeBad,
407407
Text: "Non-synchronizing literals are limited to 4096 bytes",
@@ -595,7 +595,7 @@ func (w *UpdateWriter) WriteNumMessages(n uint32) error {
595595

596596
// WriteNumRecent writes an RECENT response (not used in IMAP4rev2, will be ignored).
597597
func (w *UpdateWriter) WriteNumRecent(n uint32) error {
598-
if w.conn.enabled.Has(imap.CapIMAP4rev2) || !w.conn.server.options.caps().Has(imap.CapIMAP4rev1) {
598+
if w.conn.enabled.Has(imap.CapIMAP4rev2) || !w.conn.server.options.caps().set().Has(imap.CapIMAP4rev1) {
599599
return nil
600600
}
601601
return w.conn.writeObsoleteRecent(n)

imapserver/select.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ func (c *Conn) handleSelect(tag string, dec *imapwire.Decoder, readOnly bool) er
4141
if err := c.writeExists(data.NumMessages); err != nil {
4242
return err
4343
}
44-
if !c.enabled.Has(imap.CapIMAP4rev2) && c.server.options.caps().Has(imap.CapIMAP4rev1) {
44+
if !c.enabled.Has(imap.CapIMAP4rev2) && c.server.options.caps().set().Has(imap.CapIMAP4rev1) {
4545
if err := c.writeObsoleteRecent(data.NumRecent); err != nil {
4646
return err
4747
}

imapserver/server.go

Lines changed: 62 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,64 @@ type Logger interface {
2121
Printf(format string, args ...interface{})
2222
}
2323

24+
// SupportedCaps describes capabilities supported by the server.
25+
type SupportedCaps struct {
26+
// IMAP protocol version
27+
IMAP4rev1 bool // RFC 3501
28+
IMAP4rev2 bool // RFC 9051
29+
30+
// Capabilities which are part of IMAP4rev2 and need to be explicitly
31+
// enabled by IMAP4rev1-only servers
32+
Namespace bool // RFC 2342
33+
UIDPlus bool // RFC 4315
34+
ESearch bool // RFC 4731
35+
SearchRes bool // RFC 5182
36+
ListExtended bool // RFC 5258
37+
ListStatus bool // RFC 5819
38+
Move bool // RFC 6851
39+
StatusSize bool // RFC 8438
40+
Binary bool // RFC 3516
41+
Children bool // RFC 3348
42+
43+
// Capabilities which need to be explicitly enabled on both IMAP4rev1 and
44+
// IMAP4rev2 servers
45+
SpecialUse bool // RFC 6154
46+
CreateSpecialUse bool // RFC 6154
47+
LiteralPlus bool // RFC 7888
48+
Unauthenticate bool // RFC 8437
49+
AppendLimit bool // RFC 7889
50+
}
51+
52+
func (caps *SupportedCaps) set() imap.CapSet {
53+
m := map[imap.Cap]bool{
54+
imap.CapIMAP4rev1: caps.IMAP4rev1,
55+
imap.CapIMAP4rev2: caps.IMAP4rev2,
56+
imap.CapNamespace: caps.Namespace,
57+
imap.CapUIDPlus: caps.UIDPlus,
58+
imap.CapESearch: caps.ESearch,
59+
imap.CapSearchRes: caps.SearchRes,
60+
imap.CapListExtended: caps.ListExtended,
61+
imap.CapListStatus: caps.ListStatus,
62+
imap.CapMove: caps.Move,
63+
imap.CapStatusSize: caps.StatusSize,
64+
imap.CapBinary: caps.Binary,
65+
imap.CapChildren: caps.Children,
66+
imap.CapSpecialUse: caps.SpecialUse,
67+
imap.CapCreateSpecialUse: caps.CreateSpecialUse,
68+
imap.CapLiteralPlus: caps.LiteralPlus,
69+
imap.CapUnauthenticate: caps.Unauthenticate,
70+
imap.CapAppendLimit: caps.AppendLimit,
71+
}
72+
73+
set := make(imap.CapSet, len(m))
74+
for name, ok := range m {
75+
if ok {
76+
set[name] = struct{}{}
77+
}
78+
}
79+
return set
80+
}
81+
2482
// Options contains server options.
2583
//
2684
// The only required field is NewSession.
@@ -29,18 +87,7 @@ type Options struct {
2987
NewSession func(*Conn) (Session, *GreetingData, error)
3088
// Supported capabilities. If nil, only IMAP4rev1 is advertised. This set
3189
// must contain at least IMAP4rev1 or IMAP4rev2.
32-
//
33-
// The following capabilities are part of IMAP4rev2 and need to be
34-
// explicitly enabled by IMAP4rev1-only servers:
35-
//
36-
// - NAMESPACE
37-
// - UIDPLUS
38-
// - ESEARCH
39-
// - LIST-EXTENDED
40-
// - LIST-STATUS
41-
// - MOVE
42-
// - STATUS=SIZE
43-
Caps imap.CapSet
90+
Caps *SupportedCaps
4491
// Logger is a logger to print error messages. If nil, log.Default is used.
4592
Logger Logger
4693
// TLSConfig is a TLS configuration for STARTTLS. If nil, STARTTLS is
@@ -68,11 +115,11 @@ func (options *Options) wrapReadWriter(rw io.ReadWriter) io.ReadWriter {
68115
}
69116
}
70117

71-
func (options *Options) caps() imap.CapSet {
118+
func (options *Options) caps() *SupportedCaps {
72119
if options.Caps != nil {
73120
return options.Caps
74121
}
75-
return imap.CapSet{imap.CapIMAP4rev1: {}}
122+
return &SupportedCaps{IMAP4rev1: true}
76123
}
77124

78125
// Server is an IMAP server.
@@ -89,7 +136,7 @@ type Server struct {
89136

90137
// New creates a new server.
91138
func New(options *Options) *Server {
92-
if caps := options.caps(); !caps.Has(imap.CapIMAP4rev2) && !caps.Has(imap.CapIMAP4rev1) {
139+
if caps := options.caps().set(); !caps.Has(imap.CapIMAP4rev2) && !caps.Has(imap.CapIMAP4rev1) {
93140
panic("imapserver: at least IMAP4rev1 must be supported")
94141
}
95142
return &Server{

imapserver/status.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ func (c *Conn) handleStatus(dec *imapwire.Decoder) error {
2929
return dec.Err()
3030
}
3131

32-
if options.NumRecent && !c.server.options.caps().Has(imap.CapIMAP4rev1) {
32+
if options.NumRecent && !c.server.options.caps().set().Has(imap.CapIMAP4rev1) {
3333
return &imap.Error{
3434
Type: imap.StatusResponseTypeBad,
3535
Text: "Unknown STATUS data item",

0 commit comments

Comments
 (0)