Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

server: Move to using Backend.Features() instead of Enable* flags #112

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,26 @@ type Backend interface {
AnonymousLogin(state *ConnectionState) (Session, error)
}

type Feature uint32

const (
// SMTPUTF8 (RFC 6531) extension.
FeatureSMTPUTF8 Feature = 2 << iota
emersion marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this 2 instead of 1?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ouch, most likely a typo

// BINARYMIME (RFC 3030) extension. CHUNKING extension is always supported.
FeatureBINARYMIME
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rest of the code seems to use the Go capitalization: "BinaryMIME"

(Same for "RequireTLS" I suppose)

// REQUIRETLS (RFC 8689) extension.
FeatureREQUIRETLS
)

func (f Feature) Contains(feat Feature) bool {
return f&feat != 0
}

type FeatureBackend interface {
emersion marked this conversation as resolved.
Show resolved Hide resolved
Backend
Features() Feature
}

type BodyType string

const (
Expand Down
15 changes: 8 additions & 7 deletions conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,13 +255,14 @@ func (c *Conn) handleGreet(enhanced bool, arg string) {

caps = append(caps, authCap)
}
if c.server.EnableSMTPUTF8 {
feat := c.server.backendFeatures()
if feat.Contains(FeatureSMTPUTF8) {
caps = append(caps, "SMTPUTF8")
}
if _, isTLS := c.TLSConnectionState(); isTLS && c.server.EnableREQUIRETLS {
if _, isTLS := c.TLSConnectionState(); isTLS && feat.Contains(FeatureREQUIRETLS) {
caps = append(caps, "REQUIRETLS")
}
if c.server.EnableBINARYMIME {
if feat.Contains(FeatureBINARYMIME) {
caps = append(caps, "BINARYMIME")
}
if c.server.MaxMessageBytes > 0 {
Expand Down Expand Up @@ -346,21 +347,21 @@ func (c *Conn) handleMail(arg string) {

opts.Size = int(size)
case "SMTPUTF8":
if !c.server.EnableSMTPUTF8 {
if !c.server.backendFeatures().Contains(FeatureSMTPUTF8) {
c.WriteResponse(504, EnhancedCode{5, 5, 4}, "SMTPUTF8 is not implemented")
return
}
opts.UTF8 = true
case "REQUIRETLS":
if !c.server.EnableREQUIRETLS {
if !c.server.backendFeatures().Contains(FeatureREQUIRETLS) {
c.WriteResponse(504, EnhancedCode{5, 5, 4}, "REQUIRETLS is not implemented")
return
}
opts.RequireTLS = true
case "BODY":
switch value {
case "BINARYMIME":
if !c.server.EnableBINARYMIME {
if !c.server.backendFeatures().Contains(FeatureBINARYMIME) {
c.WriteResponse(504, EnhancedCode{5, 5, 4}, "BINARYMIME is not implemented")
return
}
Expand Down Expand Up @@ -430,7 +431,7 @@ func decodeXtext(val string) (string, error) {
return ""
}

return string(char)
return string(rune(char))
})
if replaceErr != nil {
return "", replaceErr
Expand Down
20 changes: 8 additions & 12 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,6 @@ type Server struct {
ReadTimeout time.Duration
WriteTimeout time.Duration

// Advertise SMTPUTF8 (RFC 6531) capability.
// Should be used only if backend supports it.
EnableSMTPUTF8 bool

// Advertise REQUIRETLS (RFC 8689) capability.
// Should be used only if backend supports it.
EnableREQUIRETLS bool

// Advertise BINARYMIME (RFC 3030) capability.
// Should be used only if backend supports it.
EnableBINARYMIME bool

// If set, the AUTH command will not be advertised and authentication
// attempts will be rejected. This setting overrides AllowInsecureAuth.
AuthDisabled bool
Expand Down Expand Up @@ -262,3 +250,11 @@ func (s *Server) ForEachConn(f func(*Conn)) {
f(conn)
}
}

func (s *Server) backendFeatures() Feature {
fb, ok := s.Backend.(FeatureBackend)
if !ok {
return 0
}
return fb.Features()
}
12 changes: 9 additions & 3 deletions server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,16 @@ type backend struct {
// Read N bytes of message before returning dataErr.
dataErrOffset int64

features smtp.Feature

panicOnMail bool
userErr error
}

func (be *backend) Features() smtp.Feature {
return be.features
}

func (be *backend) Login(_ *smtp.ConnectionState, username, password string) (smtp.Session, error) {
if be.userErr != nil {
return &session{}, be.userErr
Expand Down Expand Up @@ -338,10 +344,10 @@ func TestServerPanicRecover(t *testing.T) {
}

func TestServerSMTPUTF8(t *testing.T) {
_, s, c, scanner := testServerAuthenticated(t)
s.EnableSMTPUTF8 = true
be, s, c, scanner := testServerAuthenticated(t)
defer s.Close()
defer c.Close()
be.features = smtp.FeatureSMTPUTF8

io.WriteString(c, "MAIL FROM:<alice@wonderland.book> SMTPUTF8\r\n")
scanner.Scan()
Expand Down Expand Up @@ -1050,7 +1056,7 @@ func TestServer_Chunking_Binarymime(t *testing.T) {
be, s, c, scanner := testServerAuthenticated(t)
defer s.Close()
defer c.Close()
s.EnableBINARYMIME = true
be.features = smtp.FeatureBINARYMIME

io.WriteString(c, "MAIL FROM:<root@nsa.gov> BODY=BINARYMIME\r\n")
scanner.Scan()
Expand Down