package mail import ( "bytes" "io" "mime" "mime/multipart" "mime/quotedprintable" "net/mail" "strings" "testing" ) // Every part must declare quoted-printable and put only 7-bit bytes on the // wire. Raw UTF-8 under an implied 7bit encoding scored R_BAD_CTE_7BIT in // rspamd, went out without a DKIM signature, and landed in Gmail's spam. func TestEnvelopePartsAreQuotedPrintable(t *testing.T) { s := Sender{From: "Vantage "} m := message{ To: "a@example.com", Subject: "Smith & Sons · restored", Text: "VANTAGE · Account\nBilling has resumed.", HTML: "

VANTAGE · Account

" + strings.Repeat("x", 2000) + "

", } raw, err := s.envelope(m) if err != nil { t.Fatal(err) } for i, c := range raw { if c > 0x7e && c != '\r' && c != '\n' { t.Fatalf("byte %d is 0x%x: the wire must be 7-bit", i, c) } } for _, line := range strings.Split(string(raw), "\r\n") { if len(line) > 998 { t.Fatalf("line of %d bytes exceeds SMTP's 998", len(line)) } } msg, err := mail.ReadMessage(bytes.NewReader(raw)) if err != nil { t.Fatal(err) } _, params, err := mime.ParseMediaType(msg.Header.Get("Content-Type")) if err != nil { t.Fatal(err) } r := multipart.NewReader(msg.Body, params["boundary"]) want := []string{m.Text, m.HTML} for i := range want { p, err := r.NextRawPart() if err != nil { t.Fatalf("part %d: %v", i, err) } if cte := p.Header.Get("Content-Transfer-Encoding"); cte != "quoted-printable" { t.Fatalf("part %d Content-Transfer-Encoding = %q", i, cte) } // NextPart would decode for us; NextRawPart keeps the check honest by // decoding here, so a missing header cannot pass by accident. body, err := io.ReadAll(quotedprintable.NewReader(p)) if err != nil { t.Fatal(err) } // Quoted-printable text mode writes line breaks as CRLF, which is the // canonical form on the wire. if strings.ReplaceAll(string(body), "\r\n", "\n") != want[i] { t.Fatalf("part %d decodes to %q", i, body) } } }