BREAKING: update mautrix to 0.15.x
This commit is contained in:
@@ -6,7 +6,7 @@ import (
|
||||
"net/smtp"
|
||||
"strings"
|
||||
|
||||
"gitlab.com/etke.cc/go/logger"
|
||||
"github.com/rs/zerolog"
|
||||
"gitlab.com/etke.cc/go/trysmtp"
|
||||
)
|
||||
|
||||
@@ -17,10 +17,10 @@ type MailSender interface {
|
||||
// SMTP client
|
||||
type Client struct {
|
||||
config *RelayConfig
|
||||
log *logger.Logger
|
||||
log *zerolog.Logger
|
||||
}
|
||||
|
||||
func newClient(cfg *RelayConfig, log *logger.Logger) *Client {
|
||||
func newClient(cfg *RelayConfig, log *zerolog.Logger) *Client {
|
||||
return &Client{
|
||||
config: cfg,
|
||||
log: log,
|
||||
@@ -29,7 +29,7 @@ func newClient(cfg *RelayConfig, log *logger.Logger) *Client {
|
||||
|
||||
// Send email
|
||||
func (c Client) Send(from string, to string, data string) error {
|
||||
c.log.Debug("Sending email from %s to %s", from, to)
|
||||
c.log.Debug().Str("from", from).Str("to", to).Msg("sending email")
|
||||
|
||||
var conn *smtp.Client
|
||||
var err error
|
||||
@@ -40,29 +40,29 @@ func (c Client) Send(from string, to string, data string) error {
|
||||
}
|
||||
|
||||
if conn == nil {
|
||||
c.log.Error("cannot connect to SMTP server of %s: %v", to, err)
|
||||
c.log.Error().Err(err).Str("server_of", to).Msg("cannot connect to SMTP server")
|
||||
return err
|
||||
}
|
||||
if err != nil {
|
||||
c.log.Warn("connection to the SMTP server of %s returned the following non-fatal error(-s): %v", err)
|
||||
c.log.Warn().Err(err).Str("server_of", to).Msg("connection to the SMTP server returned non-fatal error(-s)")
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
var w io.WriteCloser
|
||||
w, err = conn.Data()
|
||||
if err != nil {
|
||||
c.log.Error("cannot send DATA command: %v", err)
|
||||
c.log.Error().Err(err).Msg("cannot send DATA command")
|
||||
return err
|
||||
}
|
||||
defer w.Close()
|
||||
c.log.Debug("sending DATA:\n%s", data)
|
||||
c.log.Debug().Str("DATA", data).Msg("sending command")
|
||||
_, err = strings.NewReader(data).WriteTo(w)
|
||||
if err != nil {
|
||||
c.log.Debug("cannot write DATA: %v", err)
|
||||
c.log.Error().Err(err).Msg("cannot write DATA")
|
||||
return err
|
||||
}
|
||||
|
||||
c.log.Debug("email has been sent")
|
||||
c.log.Debug().Msg("email has been sent")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -5,12 +5,12 @@ import (
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"gitlab.com/etke.cc/go/logger"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
// Listener that rejects connections from banned hosts
|
||||
type Listener struct {
|
||||
log *logger.Logger
|
||||
log *zerolog.Logger
|
||||
done chan struct{}
|
||||
tls *tls.Config
|
||||
tlsMu sync.Mutex
|
||||
@@ -18,7 +18,7 @@ type Listener struct {
|
||||
isBanned func(net.Addr) bool
|
||||
}
|
||||
|
||||
func NewListener(port string, tlsConfig *tls.Config, isBanned func(net.Addr) bool, log *logger.Logger) (*Listener, error) {
|
||||
func NewListener(port string, tlsConfig *tls.Config, isBanned func(net.Addr) bool, log *zerolog.Logger) (*Listener, error) {
|
||||
actual, err := net.Listen("tcp", ":"+port)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -48,17 +48,17 @@ func (l *Listener) Accept() (net.Conn, error) {
|
||||
case <-l.done:
|
||||
return conn, err
|
||||
default:
|
||||
l.log.Warn("cannot accept connection: %v", err)
|
||||
l.log.Warn().Err(err).Msg("cannot accept connection")
|
||||
continue
|
||||
}
|
||||
}
|
||||
if l.isBanned(conn.RemoteAddr()) {
|
||||
conn.Close()
|
||||
l.log.Info("rejected connection from %q (already banned)", conn.RemoteAddr())
|
||||
l.log.Info().Str("addr", conn.RemoteAddr().String()).Msg("rejected connection (already banned)")
|
||||
continue
|
||||
}
|
||||
|
||||
l.log.Info("accepted connection from %q", conn.RemoteAddr())
|
||||
l.log.Info().Str("addr", conn.RemoteAddr().String()).Msg("accepted connection")
|
||||
|
||||
if l.tls != nil {
|
||||
return l.acceptTLS(conn)
|
||||
|
||||
@@ -2,9 +2,24 @@ package smtp
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
// loggerWrapper is a wrapper around logger.Logger to implement smtp.Logger interface
|
||||
// validatorLoggerWrapper is a wrapper around zerolog.Logger to implement validator.Logger interface
|
||||
type validatorLoggerWrapper struct {
|
||||
log *zerolog.Logger
|
||||
}
|
||||
|
||||
func (l validatorLoggerWrapper) Info(msg string, args ...interface{}) {
|
||||
l.log.Info().Msgf(msg, args...)
|
||||
}
|
||||
|
||||
func (l validatorLoggerWrapper) Error(msg string, args ...interface{}) {
|
||||
l.log.Error().Msgf(msg, args...)
|
||||
}
|
||||
|
||||
// loggerWrapper is a wrapper around any logger to implement smtp.Logger interface
|
||||
type loggerWrapper struct {
|
||||
log func(string, ...interface{})
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
|
||||
"github.com/emersion/go-smtp"
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/rs/zerolog"
|
||||
"gitlab.com/etke.cc/go/fswatcher"
|
||||
"gitlab.com/etke.cc/go/logger"
|
||||
"maunium.net/go/mautrix/id"
|
||||
|
||||
"gitlab.com/etke.cc/postmoogle/email"
|
||||
@@ -25,11 +25,11 @@ type Config struct {
|
||||
TLSPort string
|
||||
TLSRequired bool
|
||||
|
||||
LogLevel string
|
||||
MaxSize int
|
||||
Bot matrixbot
|
||||
Callers []Caller
|
||||
Relay *RelayConfig
|
||||
Logger *zerolog.Logger
|
||||
MaxSize int
|
||||
Bot matrixbot
|
||||
Callers []Caller
|
||||
Relay *RelayConfig
|
||||
}
|
||||
|
||||
type TLSConfig struct {
|
||||
@@ -49,7 +49,7 @@ type RelayConfig struct {
|
||||
}
|
||||
|
||||
type Manager struct {
|
||||
log *logger.Logger
|
||||
log *zerolog.Logger
|
||||
bot matrixbot
|
||||
fsw *fswatcher.Watcher
|
||||
smtp *smtp.Server
|
||||
@@ -78,19 +78,18 @@ type Caller interface {
|
||||
|
||||
// NewManager creates new SMTP server manager
|
||||
func NewManager(cfg *Config) *Manager {
|
||||
log := logger.New("smtp.", cfg.LogLevel)
|
||||
mailsrv := &mailServer{
|
||||
log: log,
|
||||
log: cfg.Logger,
|
||||
bot: cfg.Bot,
|
||||
domains: cfg.Domains,
|
||||
sender: newClient(cfg.Relay, log),
|
||||
sender: newClient(cfg.Relay, cfg.Logger),
|
||||
}
|
||||
for _, caller := range cfg.Callers {
|
||||
caller.SetSendmail(mailsrv.sender.Send)
|
||||
}
|
||||
|
||||
s := smtp.NewServer(mailsrv)
|
||||
s.ErrorLog = loggerWrapper{func(s string, i ...interface{}) { log.Error(s, i...) }}
|
||||
s.ErrorLog = loggerWrapper{func(s string, i ...interface{}) { cfg.Logger.Error().Msgf(s, i...) }}
|
||||
s.ReadTimeout = 10 * time.Second
|
||||
s.WriteTimeout = 10 * time.Second
|
||||
s.MaxMessageBytes = cfg.MaxSize * 1024 * 1024
|
||||
@@ -101,19 +100,20 @@ func NewManager(cfg *Config) *Manager {
|
||||
if len(cfg.Domains) == 1 {
|
||||
s.Domain = cfg.Domains[0]
|
||||
}
|
||||
if log.GetLevel() == "INFO" || log.GetLevel() == "DEBUG" || log.GetLevel() == "TRACE" {
|
||||
s.Debug = loggerWriter{func(s string) { log.Info(s) }}
|
||||
loglevel := cfg.Logger.GetLevel()
|
||||
if loglevel == zerolog.InfoLevel || loglevel == zerolog.DebugLevel || loglevel == zerolog.TraceLevel {
|
||||
s.Debug = loggerWriter{func(s string) { cfg.Logger.Info().Msg(s) }}
|
||||
}
|
||||
|
||||
fsw, err := fswatcher.New(append(cfg.TLSCerts, cfg.TLSKeys...), 0)
|
||||
if err != nil {
|
||||
log.Error("cannot start FS watcher: %v", err)
|
||||
cfg.Logger.Error().Err(err).Msg("cannot start FS watcher")
|
||||
}
|
||||
|
||||
m := &Manager{
|
||||
smtp: s,
|
||||
bot: cfg.Bot,
|
||||
log: log,
|
||||
log: cfg.Logger,
|
||||
fsw: fsw,
|
||||
port: cfg.Port,
|
||||
tls: TLSConfig{
|
||||
@@ -156,32 +156,32 @@ func (m *Manager) Start() error {
|
||||
func (m *Manager) Stop() {
|
||||
err := m.fsw.Stop()
|
||||
if err != nil {
|
||||
m.log.Error("cannot stop filesystem watcher properly: %v", err)
|
||||
m.log.Error().Err(err).Msg("cannot stop filesystem watcher properly")
|
||||
}
|
||||
|
||||
err = m.smtp.Close()
|
||||
if err != nil {
|
||||
m.log.Error("cannot stop SMTP server properly: %v", err)
|
||||
m.log.Error().Err(err).Msg("cannot stop SMTP server properly")
|
||||
}
|
||||
|
||||
m.log.Info("SMTP server has been stopped")
|
||||
m.log.Info().Msg("SMTP server has been stopped")
|
||||
}
|
||||
|
||||
func (m *Manager) listen(port string, tlsConfig *tls.Config) {
|
||||
lwrapper, err := NewListener(port, tlsConfig, m.bot.IsBanned, m.log)
|
||||
if err != nil {
|
||||
m.log.Error("cannot start listener on %s: %v", port, err)
|
||||
m.log.Error().Err(err).Str("port", port).Msg("cannot start listener")
|
||||
m.errs <- err
|
||||
return
|
||||
}
|
||||
if tlsConfig != nil {
|
||||
m.tls.Listener = lwrapper
|
||||
}
|
||||
m.log.Info("Starting SMTP server on port %s", port)
|
||||
m.log.Info().Str("port", port).Msg("Starting SMTP server")
|
||||
|
||||
err = m.smtp.Serve(lwrapper)
|
||||
if err != nil {
|
||||
m.log.Error("cannot start SMTP server on %s: %v", port, err)
|
||||
m.log.Error().Str("port", port).Err(err).Msg("cannot start SMTP server")
|
||||
m.errs <- err
|
||||
close(m.errs)
|
||||
}
|
||||
@@ -189,9 +189,9 @@ func (m *Manager) listen(port string, tlsConfig *tls.Config) {
|
||||
|
||||
// loadTLSConfig returns true if certs were loaded and false if not
|
||||
func (m *Manager) loadTLSConfig() bool {
|
||||
m.log.Info("(re)loading TLS config")
|
||||
m.log.Info().Msg("(re)loading TLS config")
|
||||
if len(m.tls.Certs) == 0 || len(m.tls.Keys) == 0 {
|
||||
m.log.Warn("SSL certificates are not provided")
|
||||
m.log.Warn().Msg("SSL certificates are not provided")
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -199,7 +199,7 @@ func (m *Manager) loadTLSConfig() bool {
|
||||
for i, path := range m.tls.Certs {
|
||||
tlsCert, err := tls.LoadX509KeyPair(path, m.tls.Keys[i])
|
||||
if err != nil {
|
||||
m.log.Error("cannot load SSL certificate: %v", err)
|
||||
m.log.Error().Err(err).Msg("cannot load SSL certificate")
|
||||
continue
|
||||
}
|
||||
certificates = append(certificates, tlsCert)
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
|
||||
"github.com/emersion/go-smtp"
|
||||
"github.com/getsentry/sentry-go"
|
||||
"gitlab.com/etke.cc/go/logger"
|
||||
"github.com/rs/zerolog"
|
||||
|
||||
"gitlab.com/etke.cc/postmoogle/email"
|
||||
)
|
||||
@@ -27,27 +27,27 @@ var (
|
||||
|
||||
type mailServer struct {
|
||||
bot matrixbot
|
||||
log *logger.Logger
|
||||
log *zerolog.Logger
|
||||
domains []string
|
||||
sender MailSender
|
||||
}
|
||||
|
||||
// Login used for outgoing mail submissions only (when you use postmoogle as smtp server in your scripts)
|
||||
func (m *mailServer) Login(state *smtp.ConnectionState, username, password string) (smtp.Session, error) {
|
||||
m.log.Debug("Login state=%+v username=%+v", state, username)
|
||||
m.log.Debug().Str("username", username).Any("state", state).Msg("Login")
|
||||
if m.bot.IsBanned(state.RemoteAddr) {
|
||||
return nil, ErrBanned
|
||||
}
|
||||
|
||||
if !email.AddressValid(username) {
|
||||
m.log.Debug("address %s is invalid", username)
|
||||
m.log.Debug().Str("address", username).Msg("address is invalid")
|
||||
m.bot.Ban(state.RemoteAddr)
|
||||
return nil, ErrBanned
|
||||
}
|
||||
|
||||
roomID, allow := m.bot.AllowAuth(username, password)
|
||||
if !allow {
|
||||
m.log.Debug("username=%s or password=<redacted> is invalid", username)
|
||||
m.log.Debug().Str("username", username).Msg("username or password is invalid")
|
||||
m.bot.Ban(state.RemoteAddr)
|
||||
return nil, ErrBanned
|
||||
}
|
||||
@@ -67,7 +67,7 @@ func (m *mailServer) Login(state *smtp.ConnectionState, username, password strin
|
||||
|
||||
// AnonymousLogin used for incoming mail submissions only
|
||||
func (m *mailServer) AnonymousLogin(state *smtp.ConnectionState) (smtp.Session, error) {
|
||||
m.log.Debug("AnonymousLogin state=%+v", state)
|
||||
m.log.Debug().Any("state", state).Msg("AnonymousLogin")
|
||||
if m.bot.IsBanned(state.RemoteAddr) {
|
||||
return nil, ErrBanned
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/emersion/go-smtp"
|
||||
"github.com/getsentry/sentry-go"
|
||||
"github.com/jhillyerd/enmime"
|
||||
"gitlab.com/etke.cc/go/logger"
|
||||
"github.com/rs/zerolog"
|
||||
"gitlab.com/etke.cc/go/validator"
|
||||
"maunium.net/go/mautrix/id"
|
||||
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
|
||||
// incomingSession represents an SMTP-submission session receiving emails from remote servers
|
||||
type incomingSession struct {
|
||||
log *logger.Logger
|
||||
log *zerolog.Logger
|
||||
getRoomID func(string) (id.RoomID, bool)
|
||||
getFilters func(id.RoomID) email.IncomingFilteringOptions
|
||||
receiveEmail func(context.Context, *email.Email) error
|
||||
@@ -41,12 +41,12 @@ type incomingSession struct {
|
||||
func (s *incomingSession) Mail(from string, opts smtp.MailOptions) error {
|
||||
sentry.GetHubFromContext(s.ctx).Scope().SetTag("from", from)
|
||||
if !email.AddressValid(from) {
|
||||
s.log.Debug("address %s is invalid", from)
|
||||
s.log.Debug().Str("from", from).Msg("address is invalid")
|
||||
s.ban(s.addr)
|
||||
return ErrBanned
|
||||
}
|
||||
s.from = from
|
||||
s.log.Debug("mail from %s, options: %+v", from, opts)
|
||||
s.log.Debug().Str("from", from).Any("options", opts).Msg("incoming mail")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -62,18 +62,18 @@ func (s *incomingSession) Rcpt(to string) error {
|
||||
}
|
||||
}
|
||||
if !domainok {
|
||||
s.log.Debug("wrong domain of %s", to)
|
||||
s.log.Debug().Str("to", to).Msg("wrong domain")
|
||||
return ErrNoUser
|
||||
}
|
||||
|
||||
var ok bool
|
||||
s.roomID, ok = s.getRoomID(utils.Mailbox(to))
|
||||
if !ok {
|
||||
s.log.Debug("mapping for %s not found", to)
|
||||
s.log.Debug().Str("to", to).Msg("mapping not found")
|
||||
return ErrNoUser
|
||||
}
|
||||
|
||||
s.log.Debug("mail to %s", to)
|
||||
s.log.Debug().Str("to", to).Msg("mail")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -98,14 +98,14 @@ func (s *incomingSession) getAddr(envelope *enmime.Envelope) net.Addr {
|
||||
port, _ = strconv.Atoi(portString) //nolint:errcheck
|
||||
|
||||
realAddr := &net.TCPAddr{IP: net.ParseIP(host), Port: port}
|
||||
s.log.Info("real address: %s", realAddr.String())
|
||||
s.log.Info().Str("addr", realAddr.String()).Msg("real address")
|
||||
return realAddr
|
||||
}
|
||||
|
||||
func (s *incomingSession) Data(r io.Reader) error {
|
||||
data, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
s.log.Error("cannot read DATA: %v", err)
|
||||
s.log.Error().Err(err).Msg("cannot read DATA")
|
||||
return err
|
||||
}
|
||||
reader := bytes.NewReader(data)
|
||||
@@ -131,12 +131,12 @@ func (s *incomingSession) Data(r io.Reader) error {
|
||||
if validations.SpamcheckDKIM() {
|
||||
results, verr := dkim.Verify(reader)
|
||||
if verr != nil {
|
||||
s.log.Error("cannot verify DKIM: %v", verr)
|
||||
s.log.Error().Err(verr).Msg("cannot verify DKIM")
|
||||
return verr
|
||||
}
|
||||
for _, result := range results {
|
||||
if result.Err != nil {
|
||||
s.log.Info("DKIM verification of %q failed: %v", result.Domain, result.Err)
|
||||
s.log.Info().Str("domain", result.Domain).Err(result.Err).Msg("DKIM verification failed")
|
||||
return result.Err
|
||||
}
|
||||
}
|
||||
@@ -158,7 +158,7 @@ func (s *incomingSession) Logout() error { return nil }
|
||||
|
||||
// outgoingSession represents an SMTP-submission session sending emails from external scripts, using postmoogle as SMTP server
|
||||
type outgoingSession struct {
|
||||
log *logger.Logger
|
||||
log *zerolog.Logger
|
||||
sendmail func(string, string, string) error
|
||||
privkey string
|
||||
domains []string
|
||||
@@ -184,17 +184,17 @@ func (s *outgoingSession) Mail(from string, opts smtp.MailOptions) error {
|
||||
}
|
||||
}
|
||||
if !domainok {
|
||||
s.log.Debug("wrong domain of %s", from)
|
||||
s.log.Debug().Str("from", from).Msg("wrong domain")
|
||||
return ErrNoUser
|
||||
}
|
||||
|
||||
roomID, ok := s.getRoomID(utils.Mailbox(from))
|
||||
if !ok {
|
||||
s.log.Debug("mapping for %s not found", from)
|
||||
s.log.Debug().Str("from", from).Msg("mapping not found")
|
||||
return ErrNoUser
|
||||
}
|
||||
if s.fromRoom != roomID {
|
||||
s.log.Warn("sender from %q tries to impersonate %q", s.fromRoom, roomID)
|
||||
s.log.Warn().Str("from_roomID", s.fromRoom.String()).Str("roomID", roomID.String()).Msg("sender from different room tries to impersonate another mailbox")
|
||||
return ErrNoUser
|
||||
}
|
||||
return nil
|
||||
@@ -204,7 +204,7 @@ func (s *outgoingSession) Rcpt(to string) error {
|
||||
sentry.GetHubFromContext(s.ctx).Scope().SetTag("to", to)
|
||||
s.tos = append(s.tos, to)
|
||||
|
||||
s.log.Debug("mail to %s", to)
|
||||
s.log.Debug().Str("to", to).Msg("mail")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -228,7 +228,7 @@ func (s *outgoingSession) Data(r io.Reader) error {
|
||||
func (s *outgoingSession) Reset() {}
|
||||
func (s *outgoingSession) Logout() error { return nil }
|
||||
|
||||
func validateIncoming(from, to string, senderAddr net.Addr, log *logger.Logger, options email.IncomingFilteringOptions) bool {
|
||||
func validateIncoming(from, to string, senderAddr net.Addr, log *zerolog.Logger, options email.IncomingFilteringOptions) bool {
|
||||
var sender net.IP
|
||||
switch netaddr := senderAddr.(type) {
|
||||
case *net.TCPAddr:
|
||||
@@ -244,7 +244,7 @@ func validateIncoming(from, to string, senderAddr net.Addr, log *logger.Logger,
|
||||
SPF: options.SpamcheckSPF(),
|
||||
SMTP: options.SpamcheckSMTP(),
|
||||
}
|
||||
v := validator.New(options.Spamlist(), enforce, to, log)
|
||||
v := validator.New(options.Spamlist(), enforce, to, &validatorLoggerWrapper{log: log})
|
||||
|
||||
return v.Email(from, sender)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user