wg-portal/internal/common/email.go
2022-11-11 18:17:38 +01:00

117 lines
3.2 KiB
Go

package common
import (
"crypto/tls"
"io"
"time"
"github.com/pkg/errors"
mail "github.com/xhit/go-simple-mail/v2"
)
type MailEncryption string
const (
MailEncryptionNone MailEncryption = "none"
MailEncryptionTLS MailEncryption = "tls"
MailEncryptionStartTLS MailEncryption = "starttls"
)
type MailAuthType string
const (
MailAuthPlain MailAuthType = "plain"
MailAuthLogin MailAuthType = "login"
MailAuthCramMD5 MailAuthType = "crammd5"
)
type MailConfig struct {
Host string `yaml:"host" envconfig:"EMAIL_HOST"`
Port int `yaml:"port" envconfig:"EMAIL_PORT"`
TLS bool `yaml:"tls" envconfig:"EMAIL_TLS"` // Deprecated, use MailConfig.Encryption instead.
Encryption MailEncryption `yaml:"encryption" envconfig:"EMAIL_ENCRYPTION"`
CertValidation bool `yaml:"certcheck" envconfig:"EMAIL_CERT_VALIDATION"`
Username string `yaml:"user" envconfig:"EMAIL_USERNAME"`
Password string `yaml:"pass" envconfig:"EMAIL_PASSWORD"`
AuthType MailAuthType `yaml:"auth" envconfig:"EMAIL_AUTHTYPE"`
}
type MailAttachment struct {
Name string
ContentType string
Data io.Reader
Embedded bool
}
// SendEmailWithAttachments sends a mail with optional attachments.
func SendEmailWithAttachments(cfg MailConfig, sender, replyTo, subject, body, htmlBody string, receivers []string, attachments []MailAttachment) error {
srv := mail.NewSMTPClient()
srv.ConnectTimeout = 30 * time.Second
srv.SendTimeout = 30 * time.Second
srv.Host = cfg.Host
srv.Port = cfg.Port
srv.Username = cfg.Username
srv.Password = cfg.Password
// TODO: remove this once the deprecated MailConfig.TLS config option has been removed
if cfg.TLS {
cfg.Encryption = MailEncryptionStartTLS
}
switch cfg.Encryption {
case MailEncryptionTLS:
srv.Encryption = mail.EncryptionSSLTLS
case MailEncryptionStartTLS:
srv.Encryption = mail.EncryptionSTARTTLS
default: // MailEncryptionNone
srv.Encryption = mail.EncryptionNone
}
srv.TLSConfig = &tls.Config{ServerName: srv.Host, InsecureSkipVerify: !cfg.CertValidation}
switch cfg.AuthType {
case MailAuthPlain:
srv.Authentication = mail.AuthPlain
case MailAuthLogin:
srv.Authentication = mail.AuthLogin
case MailAuthCramMD5:
srv.Authentication = mail.AuthCRAMMD5
}
client, err := srv.Connect()
if err != nil {
return errors.Wrap(err, "failed to connect via SMTP")
}
if replyTo == "" {
replyTo = sender
}
email := mail.NewMSG()
email.SetFrom(sender).
AddTo(receivers...).
SetReplyTo(replyTo).
SetSubject(subject)
email.SetBody(mail.TextHTML, htmlBody)
email.AddAlternative(mail.TextPlain, body)
for _, attachment := range attachments {
attachmentData, err := io.ReadAll(attachment.Data)
if err != nil {
return errors.Wrapf(err, "failed to read attachment data for %s", attachment.Name)
}
if attachment.Embedded {
email.AddInlineData(attachmentData, attachment.Name, attachment.ContentType)
} else {
email.AddAttachmentData(attachmentData, attachment.Name, attachment.ContentType)
}
}
// Call Send and pass the client
err = email.Send(client)
if err != nil {
return errors.Wrapf(err, "failed to send email")
}
return nil
}