aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/emersion/go-smtp/data.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/emersion/go-smtp/data.go')
-rw-r--r--vendor/github.com/emersion/go-smtp/data.go147
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
+}