Compare commits

...

2 Commits

Author SHA1 Message Date
bfb71926a3
feat: reopen SMTP connection if closed 2024-07-06 16:59:18 +02:00
1e7871b5c0
fix: reorder mail options
* Client.WithTLSPortPolicy() overrides the any previously set SMTP port setting
2024-07-06 16:20:53 +02:00

View File

@ -19,6 +19,9 @@ package mail
import (
"context"
"crypto/tls"
"errors"
"fmt"
"strings"
"time"
"code.vikunja.io/api/pkg/config"
@ -46,15 +49,14 @@ func getClient() (*mail.Client, error) {
if config.MailerForceSSL.GetBool() {
tlsPolicy = mail.TLSMandatory
}
opts := []mail.Option{
mail.WithPort(config.MailerPort.GetInt()),
mail.WithTLSPortPolicy(tlsPolicy),
mail.WithTLSConfig(&tls.Config{
//#nosec G402
InsecureSkipVerify: config.MailerSkipTLSVerify.GetBool(),
ServerName: config.MailerHost.GetString(),
}),
mail.WithPort(config.MailerPort.GetInt()),
mail.WithTimeout((config.MailerQueueTimeout.GetDuration() + 3) * time.Second), // 3s more for us to close before mail server timeout
mail.WithLogger(log.NewMailLogger(config.LogEnabled.GetBool(), config.LogMail.GetString(), config.LogMailLevel.GetString())),
mail.WithDebugLog(),
@ -82,6 +84,38 @@ func getClient() (*mail.Client, error) {
)
}
func resilientSend(client *mail.Client, message *mail.Msg) error {
const maxRetries = 3
retries := 0
for retries < maxRetries {
log.Debug("Sending mail")
err := client.Send(message)
if err == nil {
log.Debug("Mail sent")
return nil
}
var sendError *mail.SendError
_ = errors.As(err, &sendError)
if sendError.Reason != mail.ErrConnCheck {
// only ErrConnCheck is retried, other exceptions are thrown
return err
}
log.Warning("Connection was closed, reconnecting before retrying")
err = client.DialWithContext(context.Background())
if err != nil {
return err
}
log.Debug("Connection re-established")
retries++
}
return errors.New(fmt.Sprintf("could not send mail after %d retries", retries))
}
// StartMailDaemon starts the mail daemon
func StartMailDaemon() {
Queue = make(chan *mail.Msg, config.MailerQueuelength.GetInt())
@ -110,25 +144,32 @@ func StartMailDaemon() {
return
}
if !open {
log.Debug("Opening connection to mail server")
err = c.DialWithContext(context.Background())
if err != nil {
log.Errorf("Error during connect to smtp server: %s", err)
break
}
log.Debug("Connected to mail server")
open = true
}
err = c.Send(m)
err = resilientSend(c, m)
if err != nil {
log.Errorf("Error when sending mail: %s", err)
log.Errorf("Error sending mail: %s", err)
break
}
// Close the connection to the SMTP server if no email was sent in
// the last 30 seconds.
case <-time.After(config.MailerQueueTimeout.GetDuration() * time.Second):
if open {
log.Debug("Closing connection to mail server due to queue timeout")
open = false
err = c.Close()
if err != nil {
if strings.HasPrefix(err.Error(), "not connected to SMTP server") {
log.Warning("Connection to mail server was already closed")
break
}
log.Errorf("Error closing the mail server connection: %s\n", err)
break
}