package backend import ( "crypto/tls" "fmt" "net" "net/smtp" "strings" ) // SMTP wird AUSSCHLIESSLICH fuers manuelle Weiterleiten einzelner Mails und // App-Benachrichtigungen wie Passwort-Reset benutzt. Der Postfach-Umzug laeuft // NIE ueber SMTP, sondern ueber IMAP APPEND (05-imap-target.go). func SendPlainMail(to []string, subject, body string) error { cfg := Cfg.ForwardSMTP if strings.TrimSpace(cfg.Host) == "" || cfg.Port == 0 || strings.TrimSpace(cfg.From) == "" { return fmt.Errorf("forward_smtp ist nicht vollstaendig konfiguriert") } fromHeader, err := cleanMailHeader(cfg.From) if err != nil { return fmt.Errorf("ungueltiger Absender: %w", err) } subjectHeader, err := cleanMailHeader(subject) if err != nil { return fmt.Errorf("ungueltiger Betreff: %w", err) } toHeader, err := cleanMailHeader(strings.Join(to, ", ")) if err != nil { return fmt.Errorf("ungueltiger Empfaenger: %w", err) } from := cfg.From if strings.TrimSpace(cfg.User) != "" { from = cfg.User } message := []byte("From: " + fromHeader + "\r\n" + "To: " + toHeader + "\r\n" + "Subject: " + subjectHeader + "\r\n" + "Content-Type: text/plain; charset=utf-8\r\n" + "\r\n" + body) addr := net.JoinHostPort(cfg.Host, fmt.Sprint(cfg.Port)) auth := smtp.Auth(nil) if cfg.User != "" || cfg.Pass != "" { auth = smtp.PlainAuth("", cfg.User, cfg.Pass, cfg.Host) } if cfg.Port == 465 { return sendTLS(addr, cfg.Host, auth, from, to, message) } return sendStartTLS(addr, cfg.Host, auth, from, to, message) } func cleanMailHeader(value string) (string, error) { cleaned := strings.TrimSpace(value) if cleaned == "" { return "", nil } if strings.ContainsAny(cleaned, "\r\n") { return "", fmt.Errorf("Header darf keine Zeilenumbrueche enthalten") } return cleaned, nil } func sendStartTLS(addr, host string, auth smtp.Auth, from string, to []string, message []byte) error { c, err := smtp.Dial(addr) if err != nil { return err } defer c.Close() if ok, _ := c.Extension("STARTTLS"); ok { if err := c.StartTLS(&tls.Config{ServerName: host, MinVersion: tls.VersionTLS12}); err != nil { return err } } return smtpClientSend(c, auth, from, to, message) } func sendTLS(addr, host string, auth smtp.Auth, from string, to []string, message []byte) error { conn, err := tls.Dial("tcp", addr, &tls.Config{ServerName: host, MinVersion: tls.VersionTLS12}) if err != nil { return err } c, err := smtp.NewClient(conn, host) if err != nil { _ = conn.Close() return err } defer c.Close() return smtpClientSend(c, auth, from, to, message) } func smtpClientSend(c *smtp.Client, auth smtp.Auth, from string, to []string, message []byte) error { if auth != nil { if err := c.Auth(auth); err != nil { return err } } if err := c.Mail(from); err != nil { return err } for _, rcpt := range to { if err := c.Rcpt(rcpt); err != nil { return err } } w, err := c.Data() if err != nil { return err } if _, err := w.Write(message); err != nil { _ = w.Close() return err } if err := w.Close(); err != nil { return err } return c.Quit() } // ForwardMessage leitet den mbox-Rohtext an einen Empfaenger weiter. func ForwardMessage(rawMessage []byte, to []string, extraNote string) error { body := extraNote + "\n\n" + string(rawMessage) return SendPlainMail(to, "Weitergeleitete Nachricht aus Mail-Graveyard", body) }