7// Writer is a write-through helper, collecting properties about the written
8// message and replacing bare \n line endings with \r\n.
13 Has8bit bool // Whether a byte with the high/8bit has been read. So whether this needs SMTP 8BITMIME instead of 7BIT.
14 Size int64 // Number of bytes written, may be different from bytes read due to LF to CRLF conversion.
16 tail [3]byte // For detecting header/body-separating crlf.
17 // todo: should be parsing headers here, as we go
20func NewWriter(w io.Writer) *Writer {
21 // Pretend we already saw \r\n, for handling empty header.
22 return &Writer{writer: w, tail: [3]byte{0, '\r', '\n'}}
25// Write implements io.Writer, and writes buf as message to the Writer's underlying
26// io.Writer. It converts bare new lines (LF) to carriage returns with new lines
28func (w *Writer) Write(buf []byte) (int, error) {
31 if !w.HaveBody && len(buf) > 0 {
32 get := func(i int) byte {
39 for i, b := range buf {
40 if b == '\n' && (get(i-1) == '\n' || get(i-1) == '\r' && get(i-2) == '\n') {
50 copy(w.tail[:], w.tail[n:])
51 copy(w.tail[3-n:], buf[len(buf)-n:])
54 for _, b := range buf {
66 for i := o; i < len(buf); i++ {
67 if buf[i] == '\n' && (i > 0 && buf[i-1] != '\r' || i == 0 && origtail[2] != '\r') {
68 // Write buffer leading up to missing \r.
70 n, err := w.writer.Write(buf[o:i])
79 n, err := w.writer.Write([]byte{'\r', '\n'})
81 wrote += 1 // For only the newline.
91 n, err := w.writer.Write(buf[o:])