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 != "" }