216 lines
6.2 KiB
Go
216 lines
6.2 KiB
Go
package smtpserver
|
|
|
|
import (
|
|
"net/smtp"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestBlockedSenderRejectedAtRcpt(t *testing.T) {
|
|
backend, mailboxID := newTestBackendWithMailbox(t)
|
|
if _, err := backend.DB.AddAllowBlockEntry(mailboxID, "block", "test@example.com"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
addr := startTestServer(t, backend)
|
|
|
|
c, err := smtp.Dial(addr)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer c.Close()
|
|
|
|
if err := c.Auth(smtp.PlainAuth("", "test@example.com", "testpass123", "127.0.0.1")); err != nil {
|
|
t.Fatalf("auth: %v", err)
|
|
}
|
|
if err := c.Mail("test@example.com"); err != nil {
|
|
t.Fatalf("MAIL FROM: %v", err)
|
|
}
|
|
err = c.Rcpt("inbox@example.com")
|
|
if err == nil {
|
|
t.Fatal("expected RCPT to be rejected for a blocked sender")
|
|
}
|
|
if !strings.Contains(err.Error(), "550") {
|
|
t.Fatalf("expected 550, got: %v", err)
|
|
}
|
|
}
|
|
|
|
// TestAllowListBypassesSpamQuarantine confirms a zero reject threshold quarantines a
|
|
// non-allow-listed sender's mail into Spam (still accepted at SMTP level — spam is
|
|
// stored for review, not silently bounced), while an allow-listed sender's mail
|
|
// skips scoring entirely and lands in INBOX as normal.
|
|
func TestAllowListBypassesSpamQuarantine(t *testing.T) {
|
|
backend, mailboxID := newTestBackendWithMailbox(t)
|
|
// Force every non-allow-listed message to be quarantined as spam.
|
|
backend.Cfg.Section("Mailstore").Key("spam_reject_score").SetValue("0")
|
|
|
|
send := func(t *testing.T) error {
|
|
t.Helper()
|
|
c, err := smtp.Dial(startTestServer(t, backend))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer c.Close()
|
|
if err := c.Auth(smtp.PlainAuth("", "test@example.com", "testpass123", "127.0.0.1")); err != nil {
|
|
t.Fatalf("auth: %v", err)
|
|
}
|
|
if err := c.Mail("test@example.com"); err != nil {
|
|
t.Fatalf("MAIL FROM: %v", err)
|
|
}
|
|
if err := c.Rcpt("inbox@example.com"); err != nil {
|
|
t.Fatalf("RCPT: %v", err)
|
|
}
|
|
w, err := c.Data()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
w.Write([]byte("Subject: hi\r\n\r\nhi"))
|
|
return w.Close()
|
|
}
|
|
|
|
if err := send(t); err != nil {
|
|
t.Fatalf("expected delivery accepted (quarantined) with a zero reject threshold and no allow-list entry, got: %v", err)
|
|
}
|
|
spamMsgs, err := backend.DB.ListMessagesInFolder(mailboxID, "Spam")
|
|
if err != nil || len(spamMsgs) != 1 {
|
|
t.Fatalf("expected 1 quarantined message in Spam, got %d (err=%v)", len(spamMsgs), err)
|
|
}
|
|
|
|
if _, err := backend.DB.AddAllowBlockEntry(mailboxID, "allow", "test@example.com"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := send(t); err != nil {
|
|
t.Fatalf("expected delivery to succeed once the sender is allow-listed, got: %v", err)
|
|
}
|
|
inboxMsgs, err := backend.DB.ListMessagesInFolder(mailboxID, "INBOX")
|
|
if err != nil || len(inboxMsgs) != 1 {
|
|
t.Fatalf("expected 1 message in INBOX once allow-listed (spam scoring skipped), got %d (err=%v)", len(inboxMsgs), err)
|
|
}
|
|
}
|
|
|
|
func TestFilterRuleDeleteDropsMessage(t *testing.T) {
|
|
backend, mailboxID := newTestBackendWithMailbox(t)
|
|
if _, err := backend.DB.CreateRule(mailboxID, 0, "subject", "contains", "drop-me", "delete", ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
addr := startTestServer(t, backend)
|
|
|
|
c, err := smtp.Dial(addr)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer c.Close()
|
|
if err := c.Auth(smtp.PlainAuth("", "test@example.com", "testpass123", "127.0.0.1")); err != nil {
|
|
t.Fatalf("auth: %v", err)
|
|
}
|
|
if err := c.Mail("test@example.com"); err != nil {
|
|
t.Fatalf("MAIL FROM: %v", err)
|
|
}
|
|
if err := c.Rcpt("inbox@example.com"); err != nil {
|
|
t.Fatalf("RCPT: %v", err)
|
|
}
|
|
w, err := c.Data()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
w.Write([]byte("Subject: drop-me please\r\n\r\nhi"))
|
|
if err := w.Close(); err != nil {
|
|
t.Fatalf("expected DATA to still report success even though the rule drops the message, got: %v", err)
|
|
}
|
|
|
|
msgs, err := backend.DB.ListMessagesInFolder(mailboxID, "INBOX")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(msgs) != 0 {
|
|
t.Fatalf("expected the delete rule to prevent storage, found %d messages", len(msgs))
|
|
}
|
|
}
|
|
|
|
func TestFilterRuleMarkReadSetsSeenFlag(t *testing.T) {
|
|
backend, mailboxID := newTestBackendWithMailbox(t)
|
|
if _, err := backend.DB.CreateRule(mailboxID, 0, "subject", "contains", "newsletter", "mark_read", ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
addr := startTestServer(t, backend)
|
|
|
|
c, err := smtp.Dial(addr)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer c.Close()
|
|
if err := c.Auth(smtp.PlainAuth("", "test@example.com", "testpass123", "127.0.0.1")); err != nil {
|
|
t.Fatalf("auth: %v", err)
|
|
}
|
|
if err := c.Mail("test@example.com"); err != nil {
|
|
t.Fatalf("MAIL FROM: %v", err)
|
|
}
|
|
if err := c.Rcpt("inbox@example.com"); err != nil {
|
|
t.Fatalf("RCPT: %v", err)
|
|
}
|
|
w, err := c.Data()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
w.Write([]byte("Subject: weekly newsletter\r\n\r\nhi"))
|
|
if err := w.Close(); err != nil {
|
|
t.Fatalf("DATA: %v", err)
|
|
}
|
|
|
|
msgs, err := backend.DB.ListMessagesInFolder(mailboxID, "INBOX")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(msgs) != 1 {
|
|
t.Fatalf("expected 1 message, got %d", len(msgs))
|
|
}
|
|
if !strings.Contains(msgs[0].Flags, `\Seen`) {
|
|
t.Fatalf("expected the mark_read rule to set \\Seen, got flags %q", msgs[0].Flags)
|
|
}
|
|
}
|
|
|
|
func TestFilterRuleMoveToFolderStoresInNamedFolder(t *testing.T) {
|
|
backend, mailboxID := newTestBackendWithMailbox(t)
|
|
if _, err := backend.DB.CreateRule(mailboxID, 0, "subject", "contains", "spam", "move_to_folder", "Spam"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
addr := startTestServer(t, backend)
|
|
|
|
c, err := smtp.Dial(addr)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer c.Close()
|
|
if err := c.Auth(smtp.PlainAuth("", "test@example.com", "testpass123", "127.0.0.1")); err != nil {
|
|
t.Fatalf("auth: %v", err)
|
|
}
|
|
if err := c.Mail("test@example.com"); err != nil {
|
|
t.Fatalf("MAIL FROM: %v", err)
|
|
}
|
|
if err := c.Rcpt("inbox@example.com"); err != nil {
|
|
t.Fatalf("RCPT: %v", err)
|
|
}
|
|
w, err := c.Data()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
w.Write([]byte("Subject: this looks like spam\r\n\r\nhi"))
|
|
if err := w.Close(); err != nil {
|
|
t.Fatalf("DATA: %v", err)
|
|
}
|
|
|
|
inbox, err := backend.DB.ListMessagesInFolder(mailboxID, "INBOX")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(inbox) != 0 {
|
|
t.Fatalf("expected nothing in INBOX, found %d", len(inbox))
|
|
}
|
|
spam, err := backend.DB.ListMessagesInFolder(mailboxID, "Spam")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(spam) != 1 {
|
|
t.Fatalf("expected 1 message in Spam, got %d", len(spam))
|
|
}
|
|
}
|