| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124 |
- package guerrilla
- import (
- "bufio"
- "errors"
- "fmt"
- "io"
- "strconv"
- "strings"
- )
- var (
- LineLimitExceeded = errors.New("Maximum line length exceeded")
- MessageSizeExceeded = errors.New("Maximum message size exceeded")
- )
- // Backends process received mail. Depending on the implementation, that can
- // be storing in a database, retransmitting to another server, etc.
- // Must return an SMTP message (i.e. "250 OK") and a boolean indicating
- // whether the message was processed successfully.
- type Backend interface {
- Process(*Envelope) BackendResult
- Shutdown() error
- }
- // BackendResult represents a response to an SMTP client after receiving DATA.
- // The String method should return an SMTP message ready to send back to the
- // client, for example `250 OK: Message received`.
- type BackendResult interface {
- fmt.Stringer
- // Code should return the SMTP code associated with this response, ie. `250`
- Code() int
- }
- // Internal implementation of BackendResult for use by backend implementations.
- type backendResult string
- func (br backendResult) String() string {
- return string(br)
- }
- // Parses the SMTP code from the first 3 characters of the SMTP message.
- // Returns 554 if code cannot be parsed.
- func (br backendResult) Code() int {
- trimmed := strings.TrimSpace(string(br))
- if len(trimmed) < 3 {
- return 554
- }
- code, err := strconv.Atoi(trimmed[:3])
- if err != nil {
- return 554
- }
- return code
- }
- func NewBackendResult(message string) BackendResult {
- return backendResult(message)
- }
- // EmailAddress encodes an email address of the form `<user@host>`
- type EmailAddress struct {
- User string
- Host string
- }
- func (ep *EmailAddress) String() string {
- return fmt.Sprintf("%s@%s", ep.User, ep.Host)
- }
- func (ep *EmailAddress) isEmpty() bool {
- return ep.User == "" && ep.Host == ""
- }
- // we need to adjust the limit, so we embed io.LimitedReader
- type adjustableLimitedReader struct {
- R *io.LimitedReader
- }
- // bolt this on so we can adjust the limit
- func (alr *adjustableLimitedReader) setLimit(n int64) {
- alr.R.N = n
- }
- // Returns a specific error when a limit is reached, that can be differentiated
- // from an EOF error from the standard io.Reader.
- func (alr *adjustableLimitedReader) Read(p []byte) (n int, err error) {
- n, err = alr.R.Read(p)
- if err == io.EOF && alr.R.N <= 0 {
- // return our custom error since io.Reader returns EOF
- err = LineLimitExceeded
- }
- return
- }
- // allocate a new adjustableLimitedReader
- func newAdjustableLimitedReader(r io.Reader, n int64) *adjustableLimitedReader {
- lr := &io.LimitedReader{R: r, N: n}
- return &adjustableLimitedReader{lr}
- }
- // This is a bufio.Reader what will use our adjustable limit reader
- // We 'extend' buffio to have the limited reader feature
- type smtpBufferedReader struct {
- *bufio.Reader
- alr *adjustableLimitedReader
- }
- // Delegate to the adjustable limited reader
- func (sbr *smtpBufferedReader) setLimit(n int64) {
- sbr.alr.setLimit(n)
- }
- // Set a new reader & use it to reset the underlying reader
- func (sbr *smtpBufferedReader) Reset(r io.Reader) {
- sbr.alr = newAdjustableLimitedReader(r, CommandLineMaxLength)
- sbr.Reader.Reset(sbr.alr)
- }
- // Allocate a new SMTPBufferedReader
- func newSMTPBufferedReader(rd io.Reader) *smtpBufferedReader {
- alr := newAdjustableLimitedReader(rd, CommandLineMaxLength)
- s := &smtpBufferedReader{bufio.NewReader(alr), alr}
- return s
- }
|