func (c *Conn) List(msgID int) ([]MessageID, error) {
	var (
		buf *bytes.Buffer
		err error
	)
	if msgID <= 0 {
		buf, err = c.Cmd("LIST", true)
	} else {
		buf, err = c.Cmd("LIST", false, msgID)
	}
	if err != nil {
		return nil, err
	}
	var (
		out   []MessageID
		lines = bytes.Split(buf.Bytes(), lineBreak)
	)
	for _, l := range lines {
		f := bytes.Fields(l)
		if len(f) == 0 {
			break
		}
		id, err := strconv.Atoi(string(f[0]))
		if err != nil {
			return nil, err
		}
		size, err := strconv.Atoi(string(f[1]))
		if err != nil {
			return nil, err
		}
		out = append(out, MessageID{ID: id, Size: size})
	}
	return out, nil
}
List returns a list of (message ID, message Size) pairs.
If the optional msgID > 0, then only that particular message is listed.
The message IDs are sequential, 1 to N.
package pop3
import (
	"bufio"
	"bytes"
	"crypto/tls"
	"errors"
	"fmt"
	"net"
	"strconv"
	"strings"
	"time"
	"github.com/emersion/go-message"
)
type Client struct {
	opt Opt
}
type Conn struct {
	conn net.Conn
	r    *bufio.Reader
	w    *bufio.Writer
}
type Opt struct {
	Host string `json:"host"`
	Port int    `json:"port"`
	DialTimeout time.Duration `json:"dial_timeout"`
	TLSEnabled    bool `json:"tls_enabled"`
	TLSSkipVerify bool `json:"tls_skip_verify"`
}
type MessageID struct {
	ID   int
	Size int
	UID string
}
var (
	lineBreak = []byte("\r\n")
	respOK      = []byte("+OK")    
	respOKInfo  = []byte("+OK ")   
	respErr     = []byte("-ERR")   
	respErrInfo = []byte("-ERR ")  
)
func New(opt Opt) *Client {
	if opt.DialTimeout < time.Millisecond {
		opt.DialTimeout = time.Second * 3
	}
	return &Client{
		opt: opt,
	}
}
func (c *Client) NewConn() (*Conn, error) {
	var (
		addr = fmt.Sprintf("%s:%d", c.opt.Host, c.opt.Port)
	)
	conn, err := net.DialTimeout("tcp", addr, c.opt.DialTimeout)
	if err != nil {
		return nil, err
	}
	if c.opt.TLSEnabled {
		tlsCfg := tls.Config{}
		if c.opt.TLSSkipVerify {
			tlsCfg.InsecureSkipVerify = c.opt.TLSSkipVerify
		} else {
			tlsCfg.ServerName = c.opt.Host
		}
		conn = tls.Client(conn, &tlsCfg)
	}
	pCon := &Conn{
		conn: conn,
		r:    bufio.NewReader(conn),
		w:    bufio.NewWriter(conn),
	}
	if _, err := pCon.ReadOne(); err != nil {
		return nil, err
	}
	return pCon, nil
}
func (c *Conn) Send(b string) error {
	if _, err := c.w.WriteString(b + "\r\n"); err != nil {
		return err
	}
	return c.w.Flush()
}
func (c *Conn) Cmd(cmd string, isMulti bool, args ...interface{}) (*bytes.Buffer, error) {
	var cmdLine string
	if len(args) > 0 {
		format := " " + strings.TrimRight(strings.Repeat("%v ", len(args)), " ")
		cmdLine = fmt.Sprintf(cmd+format, args...)
	} else {
		cmdLine = cmd
	}
	if err := c.Send(cmdLine); err != nil {
		return nil, err
	}
	b, err := c.ReadOne()
	if err != nil {
		return nil, err
	}
	if !isMulti {
		return bytes.NewBuffer(b), err
	}
	buf, err := c.ReadAll()
	return buf, err
}
func (c *Conn) ReadOne() ([]byte, error) {
	b, _, err := c.r.ReadLine()
	if err != nil {
		return nil, err
	}
	r, err := parseResp(b)
	return r, err
}
func (c *Conn) ReadAll() (*bytes.Buffer, error) {
	buf := &bytes.Buffer{}
	for {
		b, _, err := c.r.ReadLine()
		if err != nil {
			return nil, err
		}
		if bytes.Equal(b, []byte(".")) {
			break
		}
		if _, err := buf.Write(b); err != nil {
			return nil, err
		}
		if _, err := buf.Write(lineBreak); err != nil {
			return nil, err
		}
	}
	return buf, nil
}
func (c *Conn) Auth(user, password string) error {
	if err := c.User(user); err != nil {
		return err
	}
	if err := c.Pass(password); err != nil {
		return err
	}
	return c.Noop()
}
func (c *Conn) User(s string) error {
	_, err := c.Cmd("USER", false, s)
	return err
}
func (c *Conn) Pass(s string) error {
	_, err := c.Cmd("PASS", false, s)
	return err
}
func (c *Conn) Stat() (int, int, error) {
	b, err := c.Cmd("STAT", false)
	if err != nil {
		return 0, 0, err
	}
	f := bytes.Fields(b.Bytes())
	count, err := strconv.Atoi(string(f[0]))
	if err != nil {
		return 0, 0, err
	}
	if count == 0 {
		return 0, 0, nil
	}
	size, err := strconv.Atoi(string(f[1]))
	if err != nil {
		return 0, 0, err
	}
	return count, size, nil
}