diff --git a/backend.go b/backend.go index e2af9af..98a087b 100644 --- a/backend.go +++ b/backend.go @@ -21,6 +21,26 @@ type Backend interface { AnonymousLogin(state *ConnectionState) (Session, error) } +type Feature uint32 + +const ( + // SMTPUTF8 (RFC 6531) extension. + FeatureSMTPUTF8 Feature = 2 << iota + // BINARYMIME (RFC 3030) extension. CHUNKING extension is always supported. + FeatureBINARYMIME + // REQUIRETLS (RFC 8689) extension. + FeatureREQUIRETLS +) + +func (f Feature) Contains(feat Feature) bool { + return f&feat != 0 +} + +type FeatureBackend interface { + Backend + Features() Feature +} + type BodyType string const ( diff --git a/conn.go b/conn.go index 462e38e..2b30f22 100644 --- a/conn.go +++ b/conn.go @@ -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 { @@ -346,13 +347,13 @@ 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 } @@ -360,7 +361,7 @@ func (c *Conn) handleMail(arg string) { 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 } @@ -430,7 +431,7 @@ func decodeXtext(val string) (string, error) { return "" } - return string(char) + return string(rune(char)) }) if replaceErr != nil { return "", replaceErr diff --git a/server.go b/server.go index 52506b5..ee92934 100644 --- a/server.go +++ b/server.go @@ -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 @@ -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() +} diff --git a/server_test.go b/server_test.go index 48ad654..50a6639 100644 --- a/server_test.go +++ b/server_test.go @@ -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 @@ -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: SMTPUTF8\r\n") scanner.Scan() @@ -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: BODY=BINARYMIME\r\n") scanner.Scan()