package smtpserver import ( "crypto/tls" "strings" "time" "github.com/emersion/go-smtp" "gopkg.in/ini.v1" ) // ResolveBanner mirrors CustomSMTP's server_banner handling (the '""' literal-quotes // convention for "explicitly empty"). go-smtp's greeting is always // "220 ESMTP Service Ready" with no hook to drop the " ESMTP Service Ready" // suffix the way aiosmtpd's raw __ident__ override can — so when no custom banner is // configured, this falls back to heloHostname (a normal, protocol-correct greeting) // rather than Python's degenerate literally-empty banner. This is a disclosed, cosmetic // interface deviation: no test tooling in this project inspects the SMTP banner text. func ResolveBanner(cfg *ini.File, heloHostname string) string { raw := cfg.Section("Server").Key("server_banner").String() if raw == `""` { raw = "" } raw = strings.TrimSpace(raw) if raw == "" { return heloHostname } return raw } // NewPlainServer mirrors server_runner.py's PlainController: no TLS context at all, so // STARTTLS is never offered, and AUTH is advertised and usable in plaintext // (auth_require_tls=False). func NewPlainServer(backend *Backend, addr, banner string) *smtp.Server { s := smtp.NewServer(backend) s.Addr = addr s.Domain = banner s.AllowInsecureAuth = true s.ReadTimeout = 5 * time.Minute s.WriteTimeout = 5 * time.Minute return s } // NewTLSServer mirrors server_runner.py's TLSController: implicit/direct TLS (like // SMTPS on port 465) — the whole connection is encrypted from the first byte, not // STARTTLS-negotiated. Call ListenAndServeTLS (not ListenAndServe) to run it. func NewTLSServer(backend *Backend, addr, banner string, tlsConfig *tls.Config) *smtp.Server { s := smtp.NewServer(backend) s.Addr = addr s.Domain = banner s.TLSConfig = tlsConfig // The session is always already TLS on this listener, so AUTH is always allowed // either way (auth_require_tls=True in Python, which is trivially satisfied here). s.AllowInsecureAuth = true s.ReadTimeout = 5 * time.Minute s.WriteTimeout = 5 * time.Minute return s }