func (p *pop3Session) handleTransactionLine(line string) (err error) {
	var info os.FileInfo
	var idx int
	parts := strings.Split(line, " ")
	cmd := strings.ToUpper(parts[0])
	switch cmd {
	case "DELE":
		if len(parts) == 2 {
			idx, err = strconv.Atoi(parts[1])
			if err == nil && (idx > 0 && idx <= len(p.msgs)) {
				p.dels = append(p.dels, p.msgs[idx-1])
				p.OK("")
			} else {
				err = p.Error(err.Error())
			}
		}
	case "RETR":
		if len(parts) == 2 {
			idx, err = strconv.Atoi(parts[1])
			if err == nil && (idx > 0 && idx <= len(p.msgs)) {
				msg := p.msgs[idx-1].Filepath()
				var f *os.File
				f, err = os.Open(msg)
				if err == nil {
					r := bufio.NewReader(f)
					info, err = f.Stat()
					if err == nil {
						err = p.c.PrintfLine("+OK %d octets", info.Size())
						for err == nil {
							line, err = r.ReadString(10)
							if err == io.EOF {
								err = nil
								break
							} else if err == nil {
								line = strings.Trim(line, "\r")
								line = strings.Trim(line, "\n")
								if line == "." {
									line = " ."
								}
								err = p.c.PrintfLine(line)
							} else {
								break
							}
						}
					}
					f.Close()
					err = p.c.PrintfLine(".")
				} else {
					err = p.Error(err.Error())
				}
			} else {
				err = p.Error("bad message")
			}
		} else {
			err = p.Error("invalid syntax")
		}
		break
	case "UIDL":
		if len(parts) == 2 {
			idx, err = strconv.Atoi(parts[1])
			if err == nil && (idx > 0 && idx <= len(p.msgs)) {
				err = p.OK(parts[1] + " " + p.msgs[idx-1].Filename())
			} else {
				err = p.Error("bad message")
			}
		} else {
			p.OK("")
			dw := p.c.DotWriter()
			for idx, msg := range p.msgs {
				fmt.Fprintf(dw, "%d %s\r\n", 1+idx, msg.Filename())
			}
			err = dw.Close()
		}
		break
	case "STAT":
		_, err = fmt.Fprintf(p.c.W, "+OK %d %d", len(p.msgs), p.octs)
		if err == nil {
			err = p.c.PrintfLine("")
		} else {
			err = p.Error(err.Error())
		}
		break
	case "LIST":
		if len(parts) == 2 {
			idx, err = strconv.Atoi(parts[1])
			if err == nil {
				if idx > 0 && idx <= len(p.msgs) {
					info, err = os.Stat(p.msgs[idx-1].Filepath())
					if err == nil {
						err = p.c.PrintfLine("+OK %d %d", idx, info.Size())
					}
				} else {
					err = p.Error("no such message")
				}
			}
		} else {
			err = p.c.PrintfLine("+OK %d messages (%d octets)", len(p.msgs), p.octs)
			if err == nil {
				dw := p.c.DotWriter()
				for i, msg := range p.msgs {
					info, err = os.Stat(msg.Filepath())
					if err == nil {
						fmt.Fprintf(dw, "%d %d\r\n", i+1, info.Size())
					} else {
						log.Errorf("error in pop3, stat(): %s", err.Error())
					}
				}
				err = dw.Close()
			}
		}
		break
	default:
		err = p.Error("bad command")
	}
	return
}
handle line when in transaction mode
package pop3
import (
	"github.com/majestrate/bdsmail/lib/mailstore"
	"github.com/majestrate/bdsmail/lib/starttls"
	"bufio"
	"crypto/tls"
	"errors"
	"fmt"
	log "github.com/sirupsen/logrus"
	"io"
	"net"
	"net/textproto"
	"os"
	"strconv"
	"strings"
)
type UserAuthenticator func(string, string) (bool, error)
type Server struct {
	Local mailstore.MailRouter
	Auth UserAuthenticator
	name string
	TLS *tls.Config
}
func New() *Server {
	host, _ := os.Hostname()
	return &Server{
		name: host,
	}
}
func (s *Server) getMessages(user string) (msgs []mailstore.Message, err error) {
	var st mailstore.Store
	if s.Local == nil {
		err = errors.New("could't find mail store")
		return
	}
	var has bool
	st, has = s.Local.FindStoreFor(user)
	if !has {
		err = errors.New("no such local user")
		return
	}
	var ms []mailstore.Message
	ms, err = st.ListNew()
	if err == nil {
		for _, m := range ms {
			_, err = st.Process(m)
			if err != nil {
				log.Errorf("error processing maildir: %s", err.Error())
			}
		}
	}
	ms, err = st.List()
	if err == nil {
		for _, msg := range ms {
			msgs = append(msgs, msg)
		}
	}
	return
}
func (s *Server) checkUser(user, passwd string) (allowed bool) {
	if s.Auth != nil {
		allowed, _ = s.Auth(user, passwd)
	}
	return
}
func (s *Server) obtainMessages(user string) (msgs []mailstore.Message, o int64, err error) {
	msgs, err = s.getMessages(user)
	if err == nil {
		for _, msg := range msgs {
			var info os.FileInfo
			info, err = os.Stat(msg.Filepath())
			if err == nil && !info.IsDir() {
				o += info.Size()
			}
		}
	} else {
		log.Errorf("pop3: %s", err.Error())
	}
	return
}
type pop3Session struct {
	nc net.Conn
	c  *textproto.Conn
	s *Server
	transaction bool
	user string
	msgs []mailstore.Message
	octs int64
	dels []mailstore.Message
}
func (p *pop3Session) Run() {
	err := p.OK("POP3 Server Ready")
	for err == nil {
		var line string
		line, err = p.c.ReadLine()
		if err == nil {
			if strings.ToUpper(line) == "QUIT" {
				err = p.OK("k bai")
				break
			} else if p.transaction {
				err = p.handleTransactionLine(line)
			} else {
				err = p.handleLine(line)
			}
		}
	}
	if err != nil && err != io.EOF {
		log.Errorf("error in pop3 session: %s", err.Error())
	}
	p.c.Close()
	for _, msg := range p.dels {
		os.Remove(msg.Filepath())
	}
}