10var ErrCRLF = errors.New("invalid bare carriage return or newline")
 
12var errMissingCRLF = errors.New("missing crlf at end of message")
 
14// DataWrite reads data (a mail message) from r, and writes it to smtp
 
15// connection w with dot stuffing, as required by the SMTP data command.
 
17// Messages with bare carriage returns or bare newlines result in an error.
 
18func DataWrite(w io.Writer, r io.Reader) error {
 
21	var prevlast, last byte = '\r', '\n' // Start on a new line, so we insert a dot if the first byte is a dot.
 
22	// todo: at least for smtp submission we should probably set a max line length, eg 1000 octects including crlf. 
../rfc/5321:3512 
23	buf := make([]byte, 8*1024)
 
25		nr, err := r.Read(buf)
 
27			// Process buf by writing a line at a time, and checking if the next character
 
28			// after the line starts with a dot. Insert an extra dot if so.
 
31				if p[0] == '.' && prevlast == '\r' && last == '\n' {
 
32					if _, err := w.Write([]byte{'.'}); err != nil {
 
36				// Look for the next newline, or end of buffer.
 
45						} else if firstcr != n-1 {
 
46							// Bare carriage return.
 
51					} else if c == '\r' && firstcr < 0 {
 
57				if _, err := w.Write(p[:n]); err != nil {
 
60				// Keep track of the last two bytes we've written.
 
62					prevlast, last = last, p[0]
 
64					prevlast, last = p[n-2], p[n-1]
 
71		} else if err != nil {
 
75	if prevlast != '\r' || last != '\n' {
 
78	if _, err := w.Write(dotcrlf); err != nil {
 
84var dotcrlf = []byte(".\r\n")
 
86// DataReader is an io.Reader that reads data from an SMTP DATA command, doing dot
 
87// unstuffing and returning io.EOF when a bare dot is received. Use NewDataReader.
 
89// Bare carriage returns, and the sequences "[^\r]\n." and "\n.\n" result in an
 
91type DataReader struct {
 
95	buf         []byte // From previous read.
 
96	err         error  // Read error, for after r.buf is exhausted.
 
98	// When we see invalid combinations of CR and LF, we keep reading, and report an
 
99	// error at the final "\r\n.\r\n". We cannot just stop reading and return an error,
 
100	// the SMTP protocol would become out of sync.
 
104// NewDataReader returns an initialized DataReader.
 
105func NewDataReader(r *bufio.Reader) *DataReader {
 
108		// Set up initial state to accept a message that is only "." and CRLF.
 
114// Read implements io.Reader.
 
115func (r *DataReader) Read(p []byte) (int, error) {
 
118		// Read until newline as long as it fits in the buffer.
 
123			// todo: set a max length, eg 1000 octets including crlf excluding potential leading dot. 
../rfc/5321:3512 
124			r.buf, r.err = r.r.ReadSlice('\n')
 
125			if r.err == bufio.ErrBufferFull {
 
127			} else if r.err == io.EOF {
 
128				// Mark EOF as bad for now. If we see the ending dotcrlf below, err becomes regular
 
130				r.err = io.ErrUnexpectedEOF
 
135			for i, c := range r.buf {
 
136				if c == '\r' && (i == len(r.buf) || r.buf[i+1] != '\n') {
 
141			// We require crlf. A bare LF is not a line ending for the end of the SMTP
 
143			// Bare newlines are accepted as message data, unless around a bare dot. The SMTP
 
144			// server adds missing carriage returns. We don't reject bare newlines outright,
 
145			// real-world messages like that occur.
 
146			if r.plast == '\r' && r.last == '\n' {
 
147				if bytes.Equal(r.buf, dotcrlf) {
 
154				} else if r.buf[0] == '.' {
 
156					if len(r.buf) >= 2 && r.buf[1] == '\n' {
 
161			} else if r.last == '\n' && (bytes.HasPrefix(r.buf, []byte(".\n")) || bytes.HasPrefix(r.buf, []byte(".\r\n"))) {
 
162				// Reject "[^\r]\n.\n" and "[^\r]\n.\r\n"
 
171				r.plast, r.last = r.last, r.buf[0]
 
173				r.plast, r.last = r.buf[n-2], r.buf[n-1]