add !pm relay - per-mailbox relay config

This commit is contained in:
Aine
2024-05-02 11:28:37 +03:00
parent 6a63e44bfc
commit ea1533acae
13 changed files with 135 additions and 32 deletions

View File

@@ -6,13 +6,14 @@ import (
"io"
"net"
"net/smtp"
"net/url"
"strings"
"github.com/rs/zerolog"
)
type MailSender interface {
Send(from, to, data string) error
Send(from, to, data string, relayOverride *url.URL) error
}
// SMTP client
@@ -30,16 +31,35 @@ func newClient(cfg *RelayConfig, log *zerolog.Logger) *Client {
}
}
// relayFromURL creates a RelayConfig from a URL
func relayFromURL(relayURL *url.URL) *RelayConfig {
if relayURL == nil {
return nil
}
password, _ := relayURL.User.Password()
return &RelayConfig{
Host: relayURL.Hostname(),
Port: relayURL.Port(),
Username: relayURL.User.Username(),
Password: password,
}
}
// Send email
func (c Client) Send(from, to, data string) error {
func (c Client) Send(from, to, data string, relayOverride *url.URL) error {
log := c.log.With().Str("from", from).Str("to", to).Logger()
log.Debug().Msg("sending email")
relay := c.config
if relayOverrideCfg := relayFromURL(relayOverride); relayOverrideCfg != nil {
relay = relayOverrideCfg
}
var conn *smtp.Client
var err error
if c.config.Host != "" {
if relay != nil && relay.Host != "" {
log.Debug().Msg("creating relay client...")
conn, err = c.createRelayClient(from, to)
conn, err = c.createRelayClient(relay, from, to)
} else {
log.Debug().Msg("trying direct SMTP connection...")
conn, err = c.createDirectClient(from, to)
@@ -73,9 +93,9 @@ func (c Client) Send(from, to, data string) error {
}
// createRelayClientconnects directly to the provided smtp host
func (c *Client) createRelayClient(from, to string) (*smtp.Client, error) {
func (c *Client) createRelayClient(config *RelayConfig, from, to string) (*smtp.Client, error) {
localname := strings.SplitN(from, "@", 2)[1]
target := c.config.Host + ":" + c.config.Port
target := config.Host + ":" + config.Port
conn, err := smtp.Dial(target)
if err != nil {
return nil, err
@@ -87,12 +107,12 @@ func (c *Client) createRelayClient(from, to string) (*smtp.Client, error) {
}
if ok, _ := conn.Extension("STARTTLS"); ok {
config := &tls.Config{ServerName: c.config.Host} //nolint:gosec // it's smtp, even that is too strict sometimes
conn.StartTLS(config) //nolint:errcheck // if it doesn't work - we can't do anything anyway
tlsConfig := &tls.Config{ServerName: config.Host} //nolint:gosec // it's smtp, even that is too strict sometimes
conn.StartTLS(tlsConfig) //nolint:errcheck // if it doesn't work - we can't do anything anyway
}
if c.config.Usename != "" {
err = conn.Auth(smtp.PlainAuth("", c.config.Usename, c.config.Password, c.config.Host))
if config.Username != "" {
err = conn.Auth(smtp.PlainAuth("", config.Username, config.Password, config.Host))
if err != nil {
conn.Close()
return nil, err

View File

@@ -4,6 +4,7 @@ import (
"context"
"crypto/tls"
"net"
"net/url"
"sync"
"time"
@@ -44,7 +45,7 @@ type TLSConfig struct {
type RelayConfig struct {
Host string
Port string
Usename string
Username string
Password string
}
@@ -70,11 +71,12 @@ type matrixbot interface {
GetIFOptions(context.Context, id.RoomID) email.IncomingFilteringOptions
IncomingEmail(context.Context, *email.Email) error
GetDKIMprivkey(context.Context) string
GetRelayConfig(context.Context, id.RoomID) *url.URL
}
// Caller is Sendmail caller
type Caller interface {
SetSendmail(func(string, string, string) error)
SetSendmail(func(string, string, string, *url.URL) error)
}
// NewManager creates new SMTP server manager

View File

@@ -6,6 +6,7 @@ import (
"errors"
"io"
"net"
"net/url"
"strconv"
"github.com/emersion/go-msgauth/dkim"
@@ -40,7 +41,7 @@ type session struct {
ctx context.Context //nolint:containedctx // that's session
conn *smtp.Conn
domains []string
sendmail func(string, string, string) error
sendmail func(string, string, string, *url.URL) error
dir string
tos []string
@@ -124,7 +125,7 @@ func (s *session) outgoingData(r io.Reader) error {
eml := email.FromEnvelope(s.tos[0], envelope)
for _, to := range s.tos {
eml.RcptTo = to
err := s.sendmail(eml.From, to, eml.Compose(s.privkey))
err := s.sendmail(eml.From, to, eml.Compose(s.privkey), s.bot.GetRelayConfig(s.ctx, s.fromRoom))
if err != nil {
return err
}