forked from emersion/go-smtp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
data.go
148 lines (131 loc) · 3.09 KB
/
data.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package smtp
import (
"bufio"
"fmt"
"io"
)
type EnhancedCode [3]int
// SMTPError specifies the error code, enhanced error code (if any) and
// message returned by the server.
type SMTPError struct {
Code int
EnhancedCode EnhancedCode
Message string
}
// NoEnhancedCode is used to indicate that enhanced error code should not be
// included in response.
//
// Note that RFC 2034 requires an enhanced code to be included in all 2xx, 4xx
// and 5xx responses. This constant is exported for use by extensions, you
// should probably use EnhancedCodeNotSet instead.
var NoEnhancedCode = EnhancedCode{-1, -1, -1}
// EnhancedCodeNotSet is a nil value of EnhancedCode field in SMTPError, used
// to indicate that backend failed to provide enhanced status code. X.0.0 will
// be used (X is derived from error code).
var EnhancedCodeNotSet = EnhancedCode{0, 0, 0}
func (err *SMTPError) Error() string {
s := fmt.Sprintf("SMTP error %03d", err.Code)
if err.Message != "" {
s += ": " + err.Message
}
return s
}
func (err *SMTPError) Temporary() bool {
return err.Code/100 == 4
}
var ErrDataTooLarge = &SMTPError{
Code: 552,
EnhancedCode: EnhancedCode{5, 3, 4},
Message: "Maximum message size exceeded",
}
type dataReader struct {
r *bufio.Reader
state int
limited bool
n int64 // Maximum bytes remaining
}
func newDataReader(c *Conn) *dataReader {
dr := &dataReader{
r: c.text.R,
}
if c.server.MaxMessageBytes > 0 {
dr.limited = true
dr.n = int64(c.server.MaxMessageBytes)
}
return dr
}
func (r *dataReader) Read(b []byte) (n int, err error) {
if r.limited {
if r.n <= 0 {
return 0, ErrDataTooLarge
}
if int64(len(b)) > r.n {
b = b[0:r.n]
}
}
// Code below is taken from net/textproto with only one modification to
// not rewrite CRLF -> LF.
// Run data through a simple state machine to
// elide leading dots and detect End-of-Data (<CR><LF>.<CR><LF>) line.
const (
stateBeginLine = iota // beginning of line; initial state; must be zero
stateDot // read . at beginning of line
stateDotCR // read .\r at beginning of line
stateCR // read \r (possibly at end of line)
stateData // reading data in middle of line
stateEOF // reached .\r\n end marker line
)
for n < len(b) && r.state != stateEOF {
var c byte
c, err = r.r.ReadByte()
if err != nil {
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
break
}
switch r.state {
case stateBeginLine:
if c == '.' {
r.state = stateDot
continue
}
if c == '\r' {
r.state = stateCR
break
}
r.state = stateData
case stateDot:
if c == '\r' {
r.state = stateDotCR
continue
}
r.state = stateData
case stateDotCR:
if c == '\n' {
r.state = stateEOF
continue
}
r.state = stateData
case stateCR:
if c == '\n' {
r.state = stateBeginLine
break
}
r.state = stateData
case stateData:
if c == '\r' {
r.state = stateCR
}
}
b[n] = c
n++
}
if err == nil && r.state == stateEOF {
err = io.EOF
}
if r.limited {
r.n -= int64(n)
}
return
}