diff --git a/message/part.go b/message/part.go index 7c6c949..fd2e9e8 100644 --- a/message/part.go +++ b/message/part.go @@ -490,7 +490,6 @@ func parseAddressList(log *mlog.Log, h mail.Header, k string) []Address { var user, host string addr, err := smtp.ParseAddress(a.Address) if err != nil { - // todo: pass a ctx to this function so we can log with cid. log.Infox("parsing address (continuing)", err, mlog.Field("address", a.Address)) } else { user = addr.Localpart.String() diff --git a/message/writer.go b/message/writer.go index 79e1920..4b7bc92 100644 --- a/message/writer.go +++ b/message/writer.go @@ -5,7 +5,7 @@ import ( ) // Writer is a write-through helper, collecting properties about the written -// message. +// message and replacing bare \n line endings with \r\n. type Writer struct { writer io.Writer @@ -24,6 +24,8 @@ func NewWriter(w io.Writer) *Writer { // Write implements io.Writer. func (w *Writer) Write(buf []byte) (int, error) { + origtail := w.tail + if !w.HaveBody && len(buf) > 0 { get := func(i int) byte { if i < 0 { @@ -33,7 +35,7 @@ func (w *Writer) Write(buf []byte) (int, error) { } for i, b := range buf { - if b == '\n' && get(i-3) == '\r' && get(i-2) == '\n' && get(i-1) == '\r' { + if b == '\n' && (get(i-1) == '\n' || get(i-1) == '\r' && get(i-2) == '\n') { w.HaveBody = true break } @@ -54,9 +56,45 @@ func (w *Writer) Write(buf []byte) (int, error) { } } } - n, err := w.writer.Write(buf) - if n > 0 { - w.Size += int64(n) + + wrote := 0 + o := 0 +Top: + for o < len(buf) { + for i := o; i < len(buf); i++ { + if buf[i] == '\n' && (i > 0 && buf[i-1] != '\r' || i == 0 && origtail[2] != '\r') { + // Write buffer leading up to missing \r. + if i > o+1 { + n, err := w.writer.Write(buf[o:i]) + if n > 0 { + wrote += n + w.Size += int64(n) + } + if err != nil { + return wrote, err + } + } + n, err := w.writer.Write([]byte{'\r', '\n'}) + if n == 2 { + wrote += 1 // For only the newline. + w.Size += int64(2) + } + if err != nil { + return wrote, err + } + o = i + 1 + continue Top + } + } + n, err := w.writer.Write(buf[o:]) + if n > 0 { + wrote += n + w.Size += int64(n) + } + if err != nil { + return wrote, err + } + break } - return n, err + return wrote, nil } diff --git a/message/writer_test.go b/message/writer_test.go index 49b9991..65f1cae 100644 --- a/message/writer_test.go +++ b/message/writer_test.go @@ -34,9 +34,22 @@ func TestMsgWriter(t *testing.T) { check("no header\r\n", false) check("key: value\r\n\r\n", true) check("key: value\r\n\r\nbody", true) - check("key: value\n\nbody", false) + check("key: value\n\nbody", true) + check("key: value\n\r\nbody", true) check("key: value\r\rbody", false) check("\r\n\r\n", true) check("\r\n\r\nbody", true) check("\r\nbody", true) + + // Check \n is replaced with \r\n. + var b strings.Builder + mw := NewWriter(&b) + msg := "key: value\n\nline1\r\nline2\n" + _, err := mw.Write([]byte(msg)) + tcheck(t, err, "write") + got := b.String() + exp := "key: value\r\n\r\nline1\r\nline2\r\n" + if got != exp { + t.Fatalf("got %q, expected %q", got, exp) + } }