]>
src.bluestatic.org Git - mailpopbox.git/blob - pop3/conn.go
10 "github.com/uber-go/zap"
16 stateAuth state
= iota
22 errStateAuth
= "not in AUTHORIZATION"
23 errStateTxn
= "not in TRANSACTION"
24 errSyntax
= "syntax error"
25 errDeletedMsg
= "no such message - deleted"
28 type connection
struct {
43 func AcceptConnection(netConn net
.Conn
, po PostOffice
, log zap
.Logger
) {
44 log
= log
.With(zap
.Stringer("client", netConn
.RemoteAddr()))
47 tp
: textproto
.NewConn(netConn
),
52 conn
.log
.Info("accepted connection")
53 conn
.ok(fmt
.Sprintf("POP3 (mailpopbox) server %s", po
.Name()))
58 conn
.line
, err
= conn
.tp
.ReadLine()
60 conn
.log
.Error("ReadLine()", zap
.Error(err
))
66 if _
, err
:= fmt
.Sscanf(conn
.line
, "%s", &cmd
); err
!= nil {
67 conn
.err("invalid command")
71 conn
.log
= log
.With(zap
.String("command", cmd
))
73 switch strings
.ToUpper(cmd
) {
98 conn
.err("unknown command")
103 func (conn
*connection
) ok(msg
string) {
107 conn
.tp
.PrintfLine("+OK%s", msg
)
110 func (conn
*connection
) err(msg
string) {
111 conn
.log
.Error("error", zap
.String("message", msg
))
114 conn
.tp
.PrintfLine("-ERR%s", msg
)
118 func (conn
*connection
) doQUIT() {
119 defer conn
.tp
.Close()
122 err
:= conn
.mb
.Close()
124 conn
.err("failed to remove some messages")
131 func (conn
*connection
) doUSER() {
132 if conn
.state
!= stateAuth
{
133 conn
.err(errStateAuth
)
138 if len(conn
.line
) < cmd
{
139 conn
.err("invalid user")
143 conn
.user
= conn
.line
[cmd
:]
147 func (conn
*connection
) doPASS() {
148 if conn
.state
!= stateAuth
{
149 conn
.err(errStateAuth
)
153 if len(conn
.user
) == 0 {
159 if len(conn
.line
) < cmd
{
160 conn
.err("invalid pass")
164 pass
:= conn
.line
[cmd
:]
165 if mbox
, err
:= conn
.po
.OpenMailbox(conn
.user
, pass
); err
== nil {
166 conn
.log
.Info("authenticated", zap
.String("user", conn
.user
))
167 conn
.state
= stateTxn
171 conn
.log
.Error("failed to open mailbox", zap
.Error(err
))
172 conn
.err(err
.Error())
176 func (conn
*connection
) doSTAT() {
177 if conn
.state
!= stateTxn
{
178 conn
.err(errStateTxn
)
182 msgs
, err
:= conn
.mb
.ListMessages()
184 conn
.log
.Error("failed to list messages", zap
.Error(err
))
185 conn
.err(err
.Error())
191 for _
, msg
:= range msgs
{
199 conn
.ok(fmt
.Sprintf("%d %d", num
, size
))
202 func (conn
*connection
) doLIST() {
203 if conn
.state
!= stateTxn
{
204 conn
.err(errStateTxn
)
208 msgs
, err
:= conn
.mb
.ListMessages()
210 conn
.log
.Error("failed to list messages", zap
.Error(err
))
211 conn
.err(err
.Error())
215 conn
.ok("scan listing")
216 for _
, msg
:= range msgs
{
217 conn
.tp
.PrintfLine("%d %d", msg
.ID(), msg
.Size())
219 conn
.tp
.PrintfLine(".")
222 func (conn
*connection
) doRETR() {
223 if conn
.state
!= stateTxn
{
224 conn
.err(errStateTxn
)
228 msg
:= conn
.getRequestedMessage()
234 conn
.err(errDeletedMsg
)
238 rc
, err
:= conn
.mb
.Retrieve(msg
)
240 conn
.log
.Error("failed to retrieve messages", zap
.Error(err
))
241 conn
.err(err
.Error())
245 conn
.log
.Info("retreive message", zap
.String("unique-id", msg
.UniqueID()))
246 conn
.ok(fmt
.Sprintf("%d", msg
.Size()))
248 w
:= conn
.tp
.DotWriter()
253 func (conn
*connection
) doDELE() {
254 if conn
.state
!= stateTxn
{
255 conn
.err(errStateTxn
)
259 msg
:= conn
.getRequestedMessage()
265 conn
.err(errDeletedMsg
)
269 if err
:= conn
.mb
.Delete(msg
); err
!= nil {
270 conn
.log
.Error("failed to delete message", zap
.Error(err
))
271 conn
.err(err
.Error())
273 conn
.log
.Info("delete message", zap
.String("unique-id", msg
.UniqueID()))
278 func (conn
*connection
) doRSET() {
279 if conn
.state
!= stateTxn
{
280 conn
.err(errStateTxn
)
284 conn
.log
.Info("reset")
288 func (conn
*connection
) doUIDL() {
289 if conn
.state
!= stateTxn
{
290 conn
.err(errStateTxn
)
294 msgs
, err
:= conn
.mb
.ListMessages()
296 conn
.log
.Error("failed to list messages", zap
.Error(err
))
297 conn
.err(err
.Error())
301 conn
.ok("unique-id listing")
302 for _
, msg
:= range msgs
{
306 conn
.tp
.PrintfLine("%d %s", msg
.ID(), msg
.UniqueID())
308 conn
.tp
.PrintfLine(".")
311 func (conn
*connection
) doCAPA() {
312 conn
.ok("capabilitiy list")
319 for _
, c
:= range caps
{
320 conn
.tp
.PrintfLine(c
)
324 func (conn
*connection
) getRequestedMessage() Message
{
327 if _
, err
:= fmt
.Sscanf(conn
.line
, "%s %d", &cmd
, &idx
); err
!= nil {
333 conn
.err("invalid message-number")
337 msg
:= conn
.mb
.GetMessage(idx
)
339 conn
.err("no such message")