From 37de8de1c592619e155f93640eab70f2efa0dddc Mon Sep 17 00:00:00 2001 From: Mechiel Lukkien Date: Fri, 16 Feb 2024 20:14:31 +0100 Subject: [PATCH] fix incorrect error about bare cr/lf when sending a message over smtp we weren't properly tracking the cr's and lf's when being strict about message lines when sending data. we are reading buffered data from a Reader. if that chunk happens to start with a newline, we weren't looking at the previously written data, which could be a cr. instead, in that case, we would always claim the cr/lf wasn't correct. the new test case triggered the behaviour before having the fix. should solve issue #129 by x8x, thanks for the report! --- smtp/data.go | 6 ++++-- smtp/data_test.go | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/smtp/data.go b/smtp/data.go index c3f293b..7a59d75 100644 --- a/smtp/data.go +++ b/smtp/data.go @@ -40,8 +40,10 @@ func DataWrite(w io.Writer, r io.Reader) error { c := p[n] if c == '\n' { if firstcr < 0 { - // Bare newline. - return ErrCRLF + if n > 0 || last != '\r' { + // Bare newline. + return ErrCRLF + } } else if firstcr != n-1 { // Bare carriage return. return ErrCRLF diff --git a/smtp/data_test.go b/smtp/data_test.go index ac0496c..f438b08 100644 --- a/smtp/data_test.go +++ b/smtp/data_test.go @@ -129,3 +129,27 @@ func TestDataReader(t *testing.T) { t.Fatalf("got err %v, expected io.ErrUnexpectedEOF", err) } } + +func TestDataWriteLineBoundaries(t *testing.T) { + const valid = "Subject: test\r\n\r\nbody\r\n" + if err := DataWrite(io.Discard, &oneReader{[]byte(valid)}); err != nil { + t.Fatalf("data write: %v", err) + } +} + +// oneReader returns data one byte at a time. +type oneReader struct { + buf []byte +} + +func (r *oneReader) Read(buf []byte) (int, error) { + if len(r.buf) == 0 { + return 0, io.EOF + } + if len(buf) == 0 { + return 0, nil + } + buf[0] = r.buf[0] + r.buf = r.buf[1:] + return 1, nil +}