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
}
|