14 stateAuth state = iota
20 errStateAuth = "not in AUTHORIZATION"
21 errStateTxn = "not in TRANSACTION"
22 errSyntax = "syntax error"
25 type connection struct {
38 func AcceptConnection(netConn net.Conn, po PostOffice) {
41 tp: textproto.NewConn(netConn),
46 conn.ok(fmt.Sprintf("POP3 (mailpopbox) server %s", po.Name()))
49 conn.line, err = conn.tp.ReadLine()
51 conn.err("did't catch that")
56 if _, err := fmt.Sscanf(conn.line, "%s", &cmd); err != nil {
57 conn.err("invalid command")
82 conn.err("unknown command")
87 func (conn *connection) ok(msg string) {
91 conn.tp.PrintfLine("+OK%s", msg)
94 func (conn *connection) err(msg string) {
97 conn.tp.PrintfLine("-ERR%s", msg)
101 func (conn *connection) doQUIT() {
102 defer conn.tp.Close()
105 err := conn.mb.Close()
107 conn.err("failed to remove some messages")
114 func (conn *connection) doUSER() {
115 if conn.state != stateAuth {
116 conn.err(errStateAuth)
120 if _, err := fmt.Sscanf(conn.line, "USER %s", &conn.user); err != nil {
128 func (conn *connection) doPASS() {
129 if conn.state != stateAuth {
130 conn.err(errStateAuth)
134 if len(conn.user) == 0 {
139 pass := strings.TrimPrefix(conn.line, "PASS ")
140 if mbox, err := conn.po.OpenMailbox(conn.user, pass); err == nil {
141 conn.state = stateTxn
145 conn.err(err.Error())
149 func (conn *connection) doSTAT() {
150 if conn.state != stateTxn {
151 conn.err(errStateTxn)
155 msgs, err := conn.mb.ListMessages()
157 conn.err(err.Error())
163 for _, msg := range msgs {
171 conn.ok(fmt.Sprintf("%d %d", num, size))
174 func (conn *connection) doLIST() {
175 if conn.state != stateTxn {
176 conn.err(errStateTxn)
180 msgs, err := conn.mb.ListMessages()
182 conn.err(err.Error())
186 conn.ok("scan listing")
187 for _, msg := range msgs {
188 conn.tp.PrintfLine("%d %d", msg.ID(), msg.Size())
190 conn.tp.PrintfLine(".")
193 func (conn *connection) doRETR() {
194 if conn.state != stateTxn {
195 conn.err(errStateTxn)
199 msg := conn.getRequestedMessage()
204 rc, err := conn.mb.Retrieve(msg)
206 conn.err(err.Error())
210 w := conn.tp.DotWriter()
215 func (conn *connection) doDELE() {
216 if conn.state != stateTxn {
217 conn.err(errStateTxn)
221 msg := conn.getRequestedMessage()
226 if err := conn.mb.Delete(msg); err != nil {
227 conn.err(err.Error())
233 func (conn *connection) doRSET() {
234 if conn.state != stateTxn {
235 conn.err(errStateTxn)
242 func (conn *connection) getRequestedMessage() Message {
245 if _, err := fmt.Sscanf(conn.line, "%s %d", &cmd, &idx); err != nil {
251 conn.err("invalid message-number")
255 msg := conn.mb.GetMessage(idx)
257 conn.err("no such message")