Files
postmoogle/vendor/github.com/emersion/go-smtp/data.go
2022-11-16 12:08:51 +02:00

148 lines
3.0 KiB
Go

package smtp
import (
"bufio"
"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 {
return err.Message
}
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 ending .\r\n 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
}
r.state = stateData
case stateDot:
if c == '\r' {
r.state = stateDotCR
continue
}
if c == '\n' {
r.state = stateEOF
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
}
if c == '\n' {
r.state = stateBeginLine
}
}
b[n] = c
n++
}
if err == nil && r.state == stateEOF {
err = io.EOF
}
if r.limited {
r.n -= int64(n)
}
return
}