Files
bamort/backend/mail/smtp.go
T

164 lines
4.2 KiB
Go
Raw Normal View History

2026-02-17 23:12:58 +01:00
package mail
import (
"crypto/tls"
"fmt"
"net/smtp"
"bamort/config"
"bamort/logger"
)
// Client represents an SMTP mail client
type Client struct {
host string
port int
username string
password string
from string
}
// Message represents an email message
type Message struct {
To string
Subject string
Body string
}
// NewClient creates a new SMTP mail client from config
func NewClient() *Client {
cfg := config.Cfg
return &Client{
host: cfg.MailHost,
port: cfg.MailPort,
username: cfg.MailUsername,
password: cfg.MailPassword,
from: cfg.MailFrom,
}
}
// Send sends an email message via SMTP
func (c *Client) Send(msg Message) error {
if c.host == "" {
logger.Warn("SMTP Host nicht konfiguriert - E-Mail-Versand übersprungen")
return fmt.Errorf("SMTP host not configured")
}
logger.Debug("Sende E-Mail an %s via SMTP %s:%d", msg.To, c.host, c.port)
// Prepare email headers and body
headers := make(map[string]string)
headers["From"] = c.from
headers["To"] = msg.To
headers["Subject"] = msg.Subject
headers["MIME-Version"] = "1.0"
headers["Content-Type"] = "text/html; charset=\"UTF-8\""
// Build message
message := ""
for key, value := range headers {
message += fmt.Sprintf("%s: %s\r\n", key, value)
}
message += "\r\n" + msg.Body
// Connect to SMTP server
serverAddr := fmt.Sprintf("%s:%d", c.host, c.port)
// Create TLS config
tlsConfig := &tls.Config{
ServerName: c.host,
InsecureSkipVerify: false,
}
var err error
var client *smtp.Client
// Port 465 requires direct TLS connection, port 587 uses STARTTLS
if c.port == 465 {
// Direct TLS connection (implicit SSL)
conn, err := tls.Dial("tcp", serverAddr, tlsConfig)
if err != nil {
logger.Error("Fehler beim Verbinden mit SMTP-Server (TLS): %s", err.Error())
return fmt.Errorf("failed to connect to SMTP server: %w", err)
}
defer conn.Close()
client, err = smtp.NewClient(conn, c.host)
if err != nil {
logger.Error("Fehler beim Erstellen des SMTP-Clients: %s", err.Error())
return fmt.Errorf("failed to create SMTP client: %w", err)
}
} else {
// Port 587 or other - use STARTTLS
client, err = smtp.Dial(serverAddr)
if err != nil {
logger.Error("Fehler beim Verbinden mit SMTP-Server: %s", err.Error())
return fmt.Errorf("failed to dial SMTP server: %w", err)
}
defer client.Close()
// Start TLS if available
if ok, _ := client.Extension("STARTTLS"); ok {
if err = client.StartTLS(tlsConfig); err != nil {
logger.Error("Fehler beim STARTTLS: %s", err.Error())
return fmt.Errorf("failed to start TLS: %w", err)
}
}
}
// Authenticate
if c.username != "" && c.password != "" {
auth := smtp.PlainAuth("", c.username, c.password, c.host)
if err = client.Auth(auth); err != nil {
logger.Error("Fehler bei der SMTP-Authentifizierung: %s", err.Error())
return fmt.Errorf("failed to authenticate: %w", err)
}
}
// Set sender
if err = client.Mail(c.from); err != nil {
logger.Error("Fehler beim Setzen des Absenders: %s", err.Error())
return fmt.Errorf("failed to set sender: %w", err)
}
// Set recipient
if err = client.Rcpt(msg.To); err != nil {
logger.Error("Fehler beim Setzen des Empfängers: %s", err.Error())
return fmt.Errorf("failed to set recipient: %w", err)
}
// Send message body
writer, err := client.Data()
if err != nil {
logger.Error("Fehler beim Öffnen des Data-Writers: %s", err.Error())
return fmt.Errorf("failed to open data writer: %w", err)
}
_, err = writer.Write([]byte(message))
if err != nil {
logger.Error("Fehler beim Schreiben der Nachricht: %s", err.Error())
writer.Close()
return fmt.Errorf("failed to write message: %w", err)
}
err = writer.Close()
if err != nil {
logger.Error("Fehler beim Schließen des Data-Writers: %s", err.Error())
return fmt.Errorf("failed to close data writer: %w", err)
}
// Quit
if err = client.Quit(); err != nil {
logger.Error("Fehler beim Beenden der SMTP-Verbindung: %s", err.Error())
return fmt.Errorf("failed to quit SMTP connection: %w", err)
}
logger.Info("E-Mail erfolgreich an %s versendet", msg.To)
return nil
}
// IsConfigured returns true if the mail client is properly configured
func (c *Client) IsConfigured() bool {
return c.host != "" && c.port > 0 && c.from != ""
}