Vollständiger POP3-Server von Grund auf implementiert, analog zum bestehenden IMAP-Server (ING-01): TCP-Listener mit einer Goroutine pro Verbindung, CRLF/Byte-Stuffing-sichere Response-Writer, Zustandsmaschine (Authorization/Transaction/Update), Kommandos USER, PASS, STAT, LIST, RETR, DELE, QUIT. Zentrale Designentscheidungen: - USER antwortet immer +OK (RFC-konform), Prüfung erst bei PASS - Fehlgeschlagene Anmeldung liefert für unbekannten Benutzer und falsches Passwort denselben generischen Text (keine Informationspreisgabe, Akzeptanzkriterium 3) - DELE markiert Nachrichten nur sitzungslokal; store.Delete wird strukturell ausschließlich in QUIT (Transaction -> Update) aufgerufen, wodurch ein Verbindungsabbruch ohne QUIT nichts endgültig löscht (Pflichtprüfung 3) Alle drei Pflichtprüfungen mit echten Nachweisen durchgeführt: Zustandsübergangs-Tests gegen realen TCP-Server, manuelle Session mit Python-Standardbibliothek poplib (echtes Transkript im Prüfprotokoll), automatisierter Test für DELE-ohne-QUIT. Zusätzlich: 20 parallele reale Sessions (Akzeptanzkriterium 1), vollständiger RETR+DELE+QUIT-Zyklus (Akzeptanzkriterium 2). go build/go vet/golangci-lint clean, gesamtes Mail-Modul (~24 Pakete) regressionsfrei getestet. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HhgFcLS8tYMhDJpP74C6AQ
46 lines
997 B
Go
46 lines
997 B
Go
package pop3
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
)
|
|
|
|
// Server nimmt POP3-Verbindungen an und bedient jede in einer eigenen
|
|
// Goroutine (Akzeptanzkriterium 1) — gleiches Muster wie
|
|
// mail/internal/imap.Server. TLS/STARTTLS ist Sache von ING-06, nicht
|
|
// dieser Kachel.
|
|
type Server struct {
|
|
auth Authenticator
|
|
store MailboxStore
|
|
}
|
|
|
|
func NewServer(auth Authenticator, store MailboxStore) *Server {
|
|
return &Server{auth: auth, store: store}
|
|
}
|
|
|
|
// Serve nimmt Verbindungen auf listener an, bis ctx beendet wird.
|
|
func (srv *Server) Serve(ctx context.Context, listener net.Listener) error {
|
|
go func() {
|
|
<-ctx.Done()
|
|
_ = listener.Close()
|
|
}()
|
|
|
|
for {
|
|
conn, err := listener.Accept()
|
|
if err != nil {
|
|
if ctx.Err() != nil {
|
|
return nil
|
|
}
|
|
var netErr net.Error
|
|
if errors.As(err, &netErr) && netErr.Timeout() {
|
|
continue
|
|
}
|
|
return fmt.Errorf("pop3: verbindung annehmen: %w", err)
|
|
}
|
|
session := newSession(conn, srv.auth, srv.store)
|
|
go session.Serve(ctx)
|
|
}
|
|
}
|