1package message
2
3import (
4 "io"
5)
6
7// Writer is a write-through helper, collecting properties about the written
8// message.
9type Writer struct {
10 writer io.Writer
11
12 HaveBody bool // Body is optional. ../rfc/5322:343
13 Has8bit bool // Whether a byte with the high/8bit has been read. So whether this is 8BITMIME instead of 7BIT.
14 Size int64
15
16 tail [3]byte // For detecting header/body-separating crlf.
17 // todo: should be parsing headers here, as we go
18}
19
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'}}
23}
24
25// Write implements io.Writer.
26func (w *Writer) Write(buf []byte) (int, error) {
27 if !w.HaveBody && len(buf) > 0 {
28 get := func(i int) byte {
29 if i < 0 {
30 return w.tail[3+i]
31 }
32 return buf[i]
33 }
34
35 for i, b := range buf {
36 if b == '\n' && get(i-3) == '\r' && get(i-2) == '\n' && get(i-1) == '\r' {
37 w.HaveBody = true
38 break
39 }
40 }
41
42 n := len(buf)
43 if n > 3 {
44 n = 3
45 }
46 copy(w.tail[:], w.tail[n:])
47 copy(w.tail[3-n:], buf[len(buf)-n:])
48 }
49 if !w.Has8bit {
50 for _, b := range buf {
51 if b&0x80 != 0 {
52 w.Has8bit = true
53 break
54 }
55 }
56 }
57 n, err := w.writer.Write(buf)
58 if n > 0 {
59 w.Size += int64(n)
60 }
61 return n, err
62}
63