package pop3 import ( "bufio" "strings" ) // sanitizeResponseText entfernt eingebettete CR/LF aus text, BEVOR er in // eine Antwortzeile eingebettet wird (Bekannter Fehler vermeiden — gleiche // Konvention wie mail/internal/imap/response.go: archivmail erlaubte // Header-/Zeilen-Injection durch Stringkonkatenation ohne CRLF-Prüfung). func sanitizeResponseText(text string) string { text = strings.ReplaceAll(text, "\r", "") text = strings.ReplaceAll(text, "\n", "") return text } func writeOK(w *bufio.Writer, text string) error { _, err := w.WriteString("+OK " + sanitizeResponseText(text) + "\r\n") if err != nil { return err } return w.Flush() } func writeErr(w *bufio.Writer, text string) error { _, err := w.WriteString("-ERR " + sanitizeResponseText(text) + "\r\n") if err != nil { return err } return w.Flush() } // writeMultiline schreibt eine mehrzeilige POP3-Antwort (LIST/RETR): // "+OK ...\r\n" gefolgt von den Datenzeilen und einer abschließenden // "." -Zeile (RFC 1939 §3). content wird an "\n" in Zeilen zerlegt; jede // Zeile, die selbst mit "." beginnt, wird per "Byte-Stuffing" verdoppelt // (RFC-Pflicht UND zusätzlicher Schutz gegen eine vorzeitig wirkende // Terminierungszeile durch Nachrichteninhalt). func writeMultiline(w *bufio.Writer, okText, content string) error { if _, err := w.WriteString("+OK " + sanitizeResponseText(okText) + "\r\n"); err != nil { return err } normalized := strings.ReplaceAll(content, "\r\n", "\n") for _, line := range strings.Split(normalized, "\n") { line = strings.TrimSuffix(line, "\r") if strings.HasPrefix(line, ".") { line = "." + line } if _, err := w.WriteString(line + "\r\n"); err != nil { return err } } if _, err := w.WriteString(".\r\n"); err != nil { return err } return w.Flush() }