diff options
Diffstat (limited to 'vendor/github.com/emersion/go-smtp/data.go')
-rw-r--r-- | vendor/github.com/emersion/go-smtp/data.go | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/vendor/github.com/emersion/go-smtp/data.go b/vendor/github.com/emersion/go-smtp/data.go new file mode 100644 index 0000000..c338455 --- /dev/null +++ b/vendor/github.com/emersion/go-smtp/data.go @@ -0,0 +1,147 @@ +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 +} |