Neues Paket mail/internal/smtp: SMTP-Server für eingehende Mails, von Grund auf implementiert, analog zu mail/internal/imap und mail/internal/pop3 — TCP-Listener mit einer Goroutine pro Verbindung, Session-Zustandsmaschine (Greeting/Ready/MailFromSet/RcptToSet), Kommandos HELO/EHLO, MAIL FROM, RCPT TO, DATA, RSET, NOOP, QUIT. Envelope wird schrittweise aufgebaut und validiert (503 bei übersprungenen Schritten, 553 bei ungültiger Absender-/Empfängeradresse), Nachrichtengröße wird während DATA laufend gegen eine konfigurierbare Höchstgröße geprüft (552 bei Überschreitung, Sink bekommt die Nachricht nicht). Dot-Stuffing beim Empfang korrekt rückgängig gemacht. Neues Paket mail/internal/mailer: Mailer-Komponente für ausgehende Nachrichten. headerWriter ist die einzige Stelle, an der Header geschrieben werden — jeder Feldwert wird hart gegen CR/LF/Steuerzeichen geprüft, bevor er in die Nachricht geschrieben wird. Behebt den bekannten archivmail-Fehler (Header-Injection durch Stringkonkatenation ohne CRLF-Prüfung, siehe known-issues-archivmail.md #1). Sender.Send überträgt per echtem net/smtp-Client (Standardbibliothek) — keine Zugangsdaten im Code, Zieladresse kommt vom Aufrufer. Alle drei Pflichtprüfungen mit echten Nachweisen durchgeführt: CRLF-/Steuerzeichen-Injection in Betreff und Anzeigenamen schlägt fehl (vier Testfälle), Ende-zu-Ende-Header-Integritätstest über echten SMTP-Dialog (Mailpit/MailHog nicht installierbar auf diesem Rechner — Ersatz durch den in dieser Kachel gebauten echten SMTP-Server, kein Mock, im Prüfprotokoll begründet), Lasttest mit 50 gleichzeitigen Verbindungen ohne Goroutine-/Verbindungsleck. go build/go vet/golangci-lint clean, gesamtes Mail-Modul (~26 Pakete) regressionsfrei getestet.
28 lines
726 B
Go
28 lines
726 B
Go
package smtp
|
|
|
|
import "fmt"
|
|
|
|
// reply schreibt eine einzeilige SMTP-Antwort "code text\r\n".
|
|
func (s *Session) reply(code int, text string) error {
|
|
if _, err := fmt.Fprintf(s.writer, "%d %s\r\n", code, text); err != nil {
|
|
return err
|
|
}
|
|
return s.writer.Flush()
|
|
}
|
|
|
|
// replyMultiline schreibt eine mehrzeilige SMTP-Antwort (z. B. EHLO-
|
|
// Capability-Liste): alle Zeilen außer der letzten mit "-" statt " "
|
|
// nach dem Code (RFC 5321 §4.2.1).
|
|
func (s *Session) replyMultiline(code int, lines []string) error {
|
|
for i, line := range lines {
|
|
sep := "-"
|
|
if i == len(lines)-1 {
|
|
sep = " "
|
|
}
|
|
if _, err := fmt.Fprintf(s.writer, "%d%s%s\r\n", code, sep, line); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return s.writer.Flush()
|
|
}
|