14 stateAuth state = iota
20 errStateAuth = "not in AUTHORIZATION"
21 errStateTxn = "not in TRANSACTION"
22 errSyntax = "syntax error"
23 errDeletedMsg = "no such message - deleted"
26 type connection struct {
39 func AcceptConnection(netConn net.Conn, po PostOffice) {
42 tp: textproto.NewConn(netConn),
47 conn.ok(fmt.Sprintf("POP3 (mailpopbox) server %s", po.Name()))
50 conn.line, err = conn.tp.ReadLine()
52 conn.err("did't catch that")
57 if _, err := fmt.Sscanf(conn.line, "%s", &cmd); err != nil {
58 conn.err("invalid command")
83 conn.err("unknown command")
88 func (conn *connection) ok(msg string) {
92 conn.tp.PrintfLine("+OK%s", msg)
95 func (conn *connection) err(msg string) {
98 conn.tp.PrintfLine("-ERR%s", msg)
102 func (conn *connection) doQUIT() {
103 defer conn.tp.Close()
106 err := conn.mb.Close()
108 conn.err("failed to remove some messages")
115 func (conn *connection) doUSER() {
116 if conn.state != stateAuth {
117 conn.err(errStateAuth)
121 if _, err := fmt.Sscanf(conn.line, "USER %s", &conn.user); err != nil {
129 func (conn *connection) doPASS() {
130 if conn.state != stateAuth {
131 conn.err(errStateAuth)
135 if len(conn.user) == 0 {
140 pass := strings.TrimPrefix(conn.line, "PASS ")
141 if mbox, err := conn.po.OpenMailbox(conn.user, pass); err == nil {
142 conn.state = stateTxn
146 conn.err(err.Error())
150 func (conn *connection) doSTAT() {
151 if conn.state != stateTxn {
152 conn.err(errStateTxn)
156 msgs, err := conn.mb.ListMessages()
158 conn.err(err.Error())
164 for _, msg := range msgs {
172 conn.ok(fmt.Sprintf("%d %d", num, size))
175 func (conn *connection) doLIST() {
176 if conn.state != stateTxn {
177 conn.err(errStateTxn)
181 msgs, err := conn.mb.ListMessages()
183 conn.err(err.Error())
187 conn.ok("scan listing")
188 for _, msg := range msgs {
189 conn.tp.PrintfLine("%d %d", msg.ID(), msg.Size())
191 conn.tp.PrintfLine(".")
194 func (conn *connection) doRETR() {
195 if conn.state != stateTxn {
196 conn.err(errStateTxn)
200 msg := conn.getRequestedMessage()
206 conn.err(errDeletedMsg)
210 rc, err := conn.mb.Retrieve(msg)
212 conn.err(err.Error())
216 w := conn.tp.DotWriter()
221 func (conn *connection) doDELE() {
222 if conn.state != stateTxn {
223 conn.err(errStateTxn)
227 msg := conn.getRequestedMessage()
233 conn.err(errDeletedMsg)
237 if err := conn.mb.Delete(msg); err != nil {
238 conn.err(err.Error())
244 func (conn *connection) doRSET() {
245 if conn.state != stateTxn {
246 conn.err(errStateTxn)
253 func (conn *connection) getRequestedMessage() Message {
256 if _, err := fmt.Sscanf(conn.line, "%s %d", &cmd, &idx); err != nil {
262 conn.err("invalid message-number")
266 msg := conn.mb.GetMessage(idx)
268 conn.err("no such message")