From 3bec7010fbb9c0fe559d526d2417b1dfb82ba236 Mon Sep 17 00:00:00 2001 From: Robert Sesek Date: Sun, 9 Nov 2025 16:11:27 -0500 Subject: [PATCH] Implement POP3 `LIST ` to get the scan listing for a single message by ID. --- pkg/pop3/conn.go | 29 ++++++++++++++++++++----- pkg/pop3/conn_test.go | 50 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 5 deletions(-) diff --git a/pkg/pop3/conn.go b/pkg/pop3/conn.go index 2b0d15a..e3737bf 100644 --- a/pkg/pop3/conn.go +++ b/pkg/pop3/conn.go @@ -212,11 +212,30 @@ func (conn *connection) doLIST() { return } - msgs, err := conn.mb.ListMessages() - if err != nil { - conn.log.Error("failed to list messages", zap.Error(err)) - conn.err(err.Error()) - return + var msgs []Message + + var cmd string + var id int + n, _ := fmt.Sscanf(conn.line, "%s %d", &cmd, &id) + if n == 2 { + msg := conn.mb.GetMessage(id) + if msg == nil { + conn.err("No message with that ID") + return + } + if msg.Deleted() { + conn.err(errDeletedMsg) + return + } + msgs = []Message{msg} + } else { + var err error + msgs, err = conn.mb.ListMessages() + if err != nil { + conn.log.Error("failed to list messages", zap.Error(err)) + conn.err(err.Error()) + return + } } conn.ok("scan listing") diff --git a/pkg/pop3/conn_test.go b/pkg/pop3/conn_test.go index c5ad731..c90ee0d 100644 --- a/pkg/pop3/conn_test.go +++ b/pkg/pop3/conn_test.go @@ -309,6 +309,56 @@ func TestCaseSensitivty(t *testing.T) { }) } +func TestList(t *testing.T) { + s := newTestServer() + s.mb.msgs[1] = &testMessage{1, 5, false, "hello"} + s.mb.msgs[2] = &testMessage{2, 5, false, "world"} + + clientServerTest(t, s, []requestResponse{ + {"USER u", responseOK}, + {"PASS p", responseOK}, + {"LIST", func(t testing.TB, tp *textproto.Conn) string { + responseOK(t, tp) + lines, err := tp.ReadDotLines() + if err != nil { + t.Error(err) + return "" + } + if want, got := 2, len(lines); want != got { + t.Errorf("Expected %d lines, got %d", want, got) + return "" + } + if want, got := "1 5", lines[0]; want != got { + t.Errorf("Expected first line to be %q, got %q", want, got) + return "" + } + if want, got := "2 5", lines[1]; want != got { + t.Errorf("Expected second line to be %q, got %q", want, got) + return "" + } + return "" + }}, + {"LIST 2", func(t testing.TB, tp *textproto.Conn) string { + responseOK(t, tp) + lines, err := tp.ReadDotLines() + if err != nil { + t.Error(err) + return "" + } + if want, got := 1, len(lines); want != got { + t.Errorf("Expected %d lines, got %d", want, got) + return "" + } + if want, got := "2 5", lines[0]; want != got { + t.Errorf("Expected line to be %q, got %q", want, got) + return "" + } + return "" + }}, + {"LIST 5", responseERR}, + }) +} + func TestRetr(t *testing.T) { s := newTestServer() s.mb.msgs[1] = &testMessage{1, 5, false, "hello"} -- 2.43.5