func (a *LoginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
	if !more {
		return nil, nil
	}
	switch string(fromServer) {
	case "Username:":
		return []byte(a.Username), nil
	case "Password:":
		return []byte(a.Password), nil
	default:
		return nil, errors.New("unkown SMTP fromServer")
	}
}
Next passes the credentials for SMTP LOGIN auth type.
package smtppool
import (
	"crypto/tls"
	"errors"
	"fmt"
	"net"
	"net/mail"
	"net/smtp"
	"net/textproto"
	"sync"
	"time"
)
type Opt struct {
	Host string `json:"host"`
	Port int `json:"port"`
	HelloHostname string `json:"hello_hostname"`
	MaxConns int `json:"max_conns"`
	MaxMessageRetries int `json:"max_msg_retries"`
	IdleTimeout time.Duration `json:"idle_timeout"`
	PoolWaitTimeout time.Duration `json:"wait_timeout"`
	Auth smtp.Auth
	TLSConfig *tls.Config
}
type Pool struct {
	opt          Opt
	conns        chan *conn
	createdConns int
	lastActivity time.Time
	mut          sync.Mutex
	stopBorrow chan bool
	closed bool
}
type conn struct {
	conn   *smtp.Client
	numErr int
	lastActivity time.Time
}
type LoginAuth struct {
	Username string
	Password string
}
var ErrPoolClosed = errors.New("pool closed")
func New(o Opt) (*Pool, error) {
	if o.MaxConns < 1 {
		return nil, errors.New("MaxConns should be >= 1")
	}
	if o.MaxMessageRetries == 0 {
		o.MaxMessageRetries = 2
	}
	if o.PoolWaitTimeout.Seconds() < 1 {
		o.PoolWaitTimeout = time.Second * 2
	}
	p := &Pool{
		opt:        o,
		conns:      make(chan *conn, o.MaxConns),
		stopBorrow: make(chan bool),
	}
	if o.IdleTimeout.Seconds() >= 1 {
		go p.sweepConns(time.Second * 2)
	}
	return p, nil
}
func (p *Pool) Send(e Email) error {
	var lastErr error
	for i := 0; i < p.opt.MaxMessageRetries; i++ {
		c, err := p.borrowConn()
		if err != nil {
			return err
		}
		canRetry, err := c.send(e)
		if err == nil {
			_ = p.returnConn(c, nil)
			return nil
		}
		lastErr = err
		_ = p.returnConn(c, err)
		if !canRetry {
			return err
		}
	}
	return lastErr
}
func (p *Pool) Close() {
	p.mut.Lock()
	p.closed = true
	p.mut.Unlock()
	close(p.stopBorrow)
	if p.opt.IdleTimeout.Seconds() <= 1 {
		p.sweepConns(time.Second * 1)
	}
}
func (p *Pool) newConn() (cn *conn, err error) {
	netCon, err := net.DialTimeout("tcp",
		fmt.Sprintf("%s:%d", p.opt.Host, p.opt.Port), p.opt.PoolWaitTimeout)
	if err != nil {
		return nil, err
	}
	sm, err := smtp.NewClient(netCon, p.opt.Host)
	if err != nil {
		return nil, err
	}
	defer func() {
		if err != nil {
			sm.Close()
		}
	}()
	if p.opt.HelloHostname != "" {
		sm.Hello(p.opt.HelloHostname)
	}
	if p.opt.TLSConfig != nil {
		if ok, _ := sm.Extension("STARTTLS"); !ok {
			return nil, errors.New("SMTP STARTTLS extension not found")
		}
		if err := sm.StartTLS(p.opt.TLSConfig); err != nil {
			return nil, err
		}
	}
	if p.opt.Auth != nil {
		if ok, _ := sm.Extension("AUTH"); !ok {
			return nil, errors.New("SMTP AUTH extension not found")
		}
		if err := sm.Auth(p.opt.Auth); err != nil {
			return nil, err
		}
	}
	return &conn{
		conn: sm,
	}, nil
}
func (p *Pool) borrowConn() (*conn, error) {
	p.mut.Lock()
	switch {
	case p.closed:
		p.mut.Unlock()
		return nil, ErrPoolClosed
	case p.createdConns <= p.opt.MaxConns && len(p.conns) == 0:
		p.createdConns++
		p.mut.Unlock()
		return p.newConn()
	default:
		p.mut.Unlock()
	}
	select {
	case c := <-p.conns:
		return c, nil
	case <-p.stopBorrow:
		return nil, ErrPoolClosed
	case <-time.After(p.opt.PoolWaitTimeout):
		return nil, errors.New("timed out waiting for free conn in pool")
	}
}
func (p *Pool) returnConn(c *conn, lastErr error) (err error) {
	defer func() {
		if err != nil {
			p.mut.Lock()
			p.createdConns--
			p.mut.Unlock()
			c.conn.Close()
		}
	}()
	if lastErr != nil {
		if _, ok := lastErr.(*textproto.Error); !ok {
			return lastErr
		}
	}
	select {
	case p.conns <- c:
		return nil
	case <-time.After(p.opt.PoolWaitTimeout):
		return errors.New("timed out returning connection to pool")
	case <-p.stopBorrow:
		return ErrPoolClosed
	}
}
func (p *Pool) sweepConns(interval time.Duration) {
	activeConns := make([]*conn, cap(p.conns))
	for {
		<-time.After(interval)
		activeConns = activeConns[:0]
		p.mut.Lock()
		var (
			num          = len(p.conns)
			createdConns = p.createdConns
			closed       = p.closed
		)
		p.mut.Unlock()
		if closed && createdConns == 0 {
			return
		}
		for i := 0; i < num; i++ {
			var c *conn
			select {
			case c = <-p.conns:
			default:
				continue
			}
			if closed || time.Since(c.lastActivity) > p.opt.IdleTimeout {
				p.mut.Lock()
				p.createdConns--
				p.mut.Unlock()
				if closed {
					_ = c.conn.Quit()
				} else {
					_ = c.conn.Close()
				}
				continue
			}
			activeConns = append(activeConns, c)
		}
		for _, c := range activeConns {
			select {
			case p.conns <- c:
			default:
				_ = c.conn.Close()
				p.mut.Lock()
				p.createdConns--
				p.mut.Unlock()
			}
		}
	}
}
func (c *conn) send(e Email) (bool, error) {
	c.lastActivity = time.Now()
	emails, err := combineEmails(e.To, e.Cc, e.Bcc)
	if err != nil {
		return false, err
	}
	from, err := mail.ParseAddress(e.From)
	if err != nil {
		return false, err
	}
	if err = c.conn.Mail(from.Address); err != nil {
		return true, err
	}
	for _, recip := range emails {
		if err = c.conn.Rcpt(recip); err != nil {
			return true, err
		}
	}
	w, err := c.conn.Data()
	if err != nil {
		return true, err
	}
	isClosed := false
	defer func() {
		if !isClosed {
			w.Close()
		}
	}()
	msg, err := e.Bytes()
	if err != nil {
		return false, err
	}
	if _, err = w.Write(msg); err != nil {
		return true, err
	}
	if err := w.Close(); err != nil {
		return true, err
	}
	isClosed = true
	return false, nil
}
func (a *LoginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
	return "LOGIN", []byte{}, nil
}