]>
src.bluestatic.org Git - mailpopbox.git/blob - pop3/conn.go
2 // Copyright 2020 Blue Static <https://www.bluestatic.org>
3 // This program is free software licensed under the GNU General Public License,
4 // version 3.0. The full text of the license can be found in LICENSE.txt.
5 // SPDX-License-Identifier: GPL-3.0-only
22 stateAuth state
= iota
28 errStateAuth
= "not in AUTHORIZATION"
29 errStateTxn
= "not in TRANSACTION"
30 errSyntax
= "syntax error"
31 errDeletedMsg
= "no such message - deleted"
34 type connection
struct {
49 func AcceptConnection(netConn net
.Conn
, po PostOffice
, log
*zap
.Logger
) {
50 log
= log
.With(zap
.Stringer("client", netConn
.RemoteAddr()))
53 tp
: textproto
.NewConn(netConn
),
58 conn
.log
.Info("accepted connection")
59 conn
.ok(fmt
.Sprintf("POP3 (mailpopbox) server %s", po
.Name()))
64 conn
.line
, err
= conn
.tp
.ReadLine()
66 conn
.log
.Error("ReadLine()", zap
.Error(err
))
72 if _
, err
:= fmt
.Sscanf(conn
.line
, "%s", &cmd
); err
!= nil {
73 conn
.err("invalid command")
77 conn
.log
= log
.With(zap
.String("command", cmd
))
79 switch strings
.ToUpper(cmd
) {
104 conn
.err("unknown command")
109 func (conn
*connection
) ok(msg
string) {
113 conn
.tp
.PrintfLine("+OK%s", msg
)
116 func (conn
*connection
) err(msg
string) {
117 conn
.log
.Error("error", zap
.String("message", msg
))
120 conn
.tp
.PrintfLine("-ERR%s", msg
)
124 func (conn
*connection
) doQUIT() {
125 defer conn
.tp
.Close()
128 err
:= conn
.mb
.Close()
130 conn
.err("failed to remove some messages")
137 func (conn
*connection
) doUSER() {
138 if conn
.state
!= stateAuth
{
139 conn
.err(errStateAuth
)
144 if len(conn
.line
) < cmd
{
145 conn
.err("invalid user")
149 conn
.user
= conn
.line
[cmd
:]
153 func (conn
*connection
) doPASS() {
154 if conn
.state
!= stateAuth
{
155 conn
.err(errStateAuth
)
159 if len(conn
.user
) == 0 {
165 if len(conn
.line
) < cmd
{
166 conn
.err("invalid pass")
170 pass
:= conn
.line
[cmd
:]
171 if mbox
, err
:= conn
.po
.OpenMailbox(conn
.user
, pass
); err
== nil {
172 conn
.log
.Info("authenticated", zap
.String("user", conn
.user
))
173 conn
.state
= stateTxn
177 conn
.log
.Error("failed to open mailbox", zap
.Error(err
))
178 conn
.err(err
.Error())
182 func (conn
*connection
) doSTAT() {
183 if conn
.state
!= stateTxn
{
184 conn
.err(errStateTxn
)
188 msgs
, err
:= conn
.mb
.ListMessages()
190 conn
.log
.Error("failed to list messages", zap
.Error(err
))
191 conn
.err(err
.Error())
197 for _
, msg
:= range msgs
{
205 conn
.ok(fmt
.Sprintf("%d %d", num
, size
))
208 func (conn
*connection
) doLIST() {
209 if conn
.state
!= stateTxn
{
210 conn
.err(errStateTxn
)
214 msgs
, err
:= conn
.mb
.ListMessages()
216 conn
.log
.Error("failed to list messages", zap
.Error(err
))
217 conn
.err(err
.Error())
221 conn
.ok("scan listing")
222 for _
, msg
:= range msgs
{
223 conn
.tp
.PrintfLine("%d %d", msg
.ID(), msg
.Size())
225 conn
.tp
.PrintfLine(".")
228 func (conn
*connection
) doRETR() {
229 if conn
.state
!= stateTxn
{
230 conn
.err(errStateTxn
)
234 msg
:= conn
.getRequestedMessage()
240 conn
.err(errDeletedMsg
)
244 rc
, err
:= conn
.mb
.Retrieve(msg
)
246 conn
.log
.Error("failed to retrieve messages", zap
.Error(err
))
247 conn
.err(err
.Error())
251 conn
.log
.Info("retreive message", zap
.String("unique-id", msg
.UniqueID()))
252 conn
.ok(fmt
.Sprintf("%d", msg
.Size()))
254 w
:= conn
.tp
.DotWriter()
259 func (conn
*connection
) doDELE() {
260 if conn
.state
!= stateTxn
{
261 conn
.err(errStateTxn
)
265 msg
:= conn
.getRequestedMessage()
271 conn
.err(errDeletedMsg
)
275 if err
:= conn
.mb
.Delete(msg
); err
!= nil {
276 conn
.log
.Error("failed to delete message", zap
.Error(err
))
277 conn
.err(err
.Error())
279 conn
.log
.Info("delete message", zap
.String("unique-id", msg
.UniqueID()))
284 func (conn
*connection
) doRSET() {
285 if conn
.state
!= stateTxn
{
286 conn
.err(errStateTxn
)
290 conn
.log
.Info("reset")
294 func (conn
*connection
) doUIDL() {
295 if conn
.state
!= stateTxn
{
296 conn
.err(errStateTxn
)
300 msgs
, err
:= conn
.mb
.ListMessages()
302 conn
.log
.Error("failed to list messages", zap
.Error(err
))
303 conn
.err(err
.Error())
307 conn
.ok("unique-id listing")
308 for _
, msg
:= range msgs
{
312 conn
.tp
.PrintfLine("%d %s", msg
.ID(), msg
.UniqueID())
314 conn
.tp
.PrintfLine(".")
317 func (conn
*connection
) doCAPA() {
318 conn
.ok("capabilitiy list")
325 for _
, c
:= range caps
{
326 conn
.tp
.PrintfLine(c
)
330 func (conn
*connection
) getRequestedMessage() Message
{
333 if _
, err
:= fmt
.Sscanf(conn
.line
, "%s %d", &cmd
, &idx
); err
!= nil {
339 conn
.err("invalid message-number")
343 msg
:= conn
.mb
.GetMessage(idx
)
345 conn
.err("no such message")