--- /dev/null
+[submodule "vendor/uber-go/zap"]
+ path = vendor/github.com/uber-go/zap
+ url = https://github.com/uber-go/zap
+[submodule "vendor/github.com/uber-go/atomic"]
+ path = vendor/github.com/uber-go/atomic
+ url = https://github.com/uber-go/atomic
"encoding/json"
"fmt"
"os"
+
+ "github.com/uber-go/zap"
)
func main() {
}
configFile.Close()
- pop3 := runPOP3Server(config)
- smtp := runSMTPServer(config)
+ log := zap.New(zap.NewTextEncoder())
+
+ pop3 := runPOP3Server(config, log)
+ smtp := runSMTPServer(config, log)
select {
case err := <-pop3:
"os"
"path"
+ "github.com/uber-go/zap"
+
"src.bluestatic.org/mailpopbox/pop3"
)
-func runPOP3Server(config Config) <-chan error {
+func runPOP3Server(config Config, log zap.Logger) <-chan error {
server := pop3Server{
config: config,
rc: make(chan error),
+ log: log.With(zap.String("server", "pop3")),
}
go server.run()
return server.rc
type pop3Server struct {
config Config
rc chan error
+ log zap.Logger
}
func (server *pop3Server) run() {
for _, s := range server.config.Servers {
if err := os.Mkdir(s.MaildropPath, 0700); err != nil && !os.IsExist(err) {
+ server.log.Error("failed to open maildrop", zap.Error(err))
server.rc <- err
}
}
tlsConfig, err := server.config.GetTLSConfig()
if err != nil {
+ server.log.Error("failed to configure TLS", zap.Error(err))
server.rc <- err
return
}
addr := fmt.Sprintf(":%d", server.config.POP3Port)
+ server.log.Info("starting server", zap.String("address", addr))
var l net.Listener
if tlsConfig == nil {
l, err = tls.Listen("tcp", addr, tlsConfig)
}
if err != nil {
+ server.log.Error("listen", zap.Error(err))
server.rc <- err
return
}
for {
conn, err := l.Accept()
if err != nil {
+ server.log.Error("accept", zap.Error(err))
server.rc <- err
break
}
- go pop3.AcceptConnection(conn, server)
+ go pop3.AcceptConnection(conn, server, server.log)
}
}
"net"
"net/textproto"
"strings"
+
+ "github.com/uber-go/zap"
)
type state int
tp *textproto.Conn
remoteAddr net.Addr
+ log zap.Logger
+
state
line string
user string
}
-func AcceptConnection(netConn net.Conn, po PostOffice) {
+func AcceptConnection(netConn net.Conn, po PostOffice, log zap.Logger) {
conn := connection{
po: po,
tp: textproto.NewConn(netConn),
state: stateAuth,
+ log: log.With(zap.Stringer("client", netConn.RemoteAddr())),
}
var err error
conn.line, err = conn.tp.ReadLine()
if err != nil {
conn.err("did't catch that")
+ conn.log.Error("ReadLine()", zap.Error(err))
continue
}
pass := conn.line[len("PASS "):]
if mbox, err := conn.po.OpenMailbox(conn.user, pass); err == nil {
+ conn.log.Info("authenticated", zap.String("user", conn.user))
conn.state = stateTxn
conn.mb = mbox
conn.ok("")
} else {
+ conn.log.Error("PASS", zap.Error(err))
conn.err(err.Error())
}
}
msgs, err := conn.mb.ListMessages()
if err != nil {
+ conn.log.Error("STAT", zap.Error(err))
conn.err(err.Error())
return
}
msgs, err := conn.mb.ListMessages()
if err != nil {
+ conn.log.Error("LIST", zap.Error(err))
conn.err(err.Error())
return
}
rc, err := conn.mb.Retrieve(msg)
if err != nil {
+ conn.log.Error("RETR", zap.Error(err))
conn.err(err.Error())
return
}
}
if err := conn.mb.Delete(msg); err != nil {
+ conn.log.Error("DELE", zap.Error(err))
conn.err(err.Error())
} else {
conn.ok("")
"path"
"strings"
+ "github.com/uber-go/zap"
+
"src.bluestatic.org/mailpopbox/smtp"
)
-func runSMTPServer(config Config) <-chan error {
+func runSMTPServer(config Config, log zap.Logger) <-chan error {
server := smtpServer{
config: config,
rc: make(chan error),
+ log: log.With(zap.String("server", "smtp")),
}
go server.run()
return server.rc
config Config
tlsConfig *tls.Config
+ log zap.Logger
+
rc chan error
}
var err error
server.tlsConfig, err = server.config.GetTLSConfig()
if err != nil {
+ server.log.Error("failed to configure TLS", zap.Error(err))
server.rc <- err
}
- l, err := net.Listen("tcp", fmt.Sprintf(":%d", server.config.SMTPPort))
+ addr := fmt.Sprintf(":%d", server.config.SMTPPort)
+ server.log.Info("starting server", zap.String("address", addr))
+
+ l, err := net.Listen("tcp", addr)
if err != nil {
+ server.log.Error("listen", zap.Error(err))
server.rc <- err
return
}
for {
conn, err := l.Accept()
if err != nil {
+ server.log.Error("accept", zap.Error(err))
server.rc <- err
return
}
- go smtp.AcceptConnection(conn, server)
+ go smtp.AcceptConnection(conn, server, server.log)
}
}
"net/textproto"
"strings"
"time"
+
+ "github.com/uber-go/zap"
)
type state int
esmtp bool
+ log zap.Logger
+
state
line string
rcptTo []mail.Address
}
-func AcceptConnection(netConn net.Conn, server Server) error {
+func AcceptConnection(netConn net.Conn, server Server, log zap.Logger) error {
conn := connection{
server: server,
tp: textproto.NewConn(netConn),
nc: netConn,
remoteAddr: netConn.RemoteAddr(),
+ log: log.With(zap.Stringer("client", netConn.RemoteAddr())),
state: stateNew,
}
conn.tp.PrintfLine("250 SIZE %d", 40960000)
}
+ conn.log.Info("doEHLO()", zap.String("ehlo", conn.ehlo))
+
conn.state = stateInitial
}
return
}
+ conn.log.Info("doSTARTTLS()")
conn.writeReply(220, "initiate TLS connection")
newConn := tls.Server(conn.nc, tlsConfig)
conn.tp = textproto.NewConn(conn.tlsNc)
conn.state = stateInitial
+ conn.log.Info("HELO again")
+
conn.writeReply(220, fmt.Sprintf("%s ESMTPS [%s] (mailpopbox)",
conn.server.Name(), newConn.LocalAddr()))
}
return
}
+ conn.log.Info("doMAIL()", zap.String("address", conn.mailFrom.Address))
+
conn.state = stateMail
conn.reply(ReplyOK)
}
}
if reply := conn.server.VerifyAddress(*address); reply != ReplyOK {
+ conn.log.Warn("invalid address",
+ zap.String("address", address.Address),
+ zap.Stringer("reply", reply))
conn.reply(reply)
return
}
+ conn.log.Info("doRCPT()", zap.String("address", address.Address))
+
conn.rcptTo = append(conn.rcptTo, *address)
conn.state = stateRecipient
}
conn.writeReply(354, "Start mail input; end with <CRLF>.<CRLF>")
+ conn.log.Info("doDATA()")
data, err := conn.tp.ReadDotBytes()
if err != nil {
- // TODO: log error
+ conn.log.Error("failed to ReadDotBytes()", zap.Error(err))
conn.writeReply(552, "transaction failed")
return
}
ID: conn.envelopeID(received),
}
+ conn.log.Info("received message",
+ zap.Int("bytes", len(data)),
+ zap.Time("date", received),
+ zap.String("id", env.ID))
+
trace := conn.getReceivedInfo(env)
env.Data = append(trace, data...)
if reply := conn.server.OnMessageDelivered(env); reply != nil {
+ conn.log.Warn("message was rejected", zap.String("id", env.ID))
conn.reply(*reply)
return
}
}
func (conn *connection) doRSET() {
+ conn.log.Info("doRSET()")
conn.state = stateInitial
conn.resetBuffers()
conn.reply(ReplyOK)
Message string
}
+func (l ReplyLine) String() string {
+ return fmt.Sprintf("%d %s", l.Code, l.Message)
+}
+
var (
ReplyOK = ReplyLine{250, "OK"}
ReplyBadSyntax = ReplyLine{501, "syntax error"}
--- /dev/null
+Subproject commit 3b8db5e93c4c02efbc313e17b2e796b0914a01fb
--- /dev/null
+Subproject commit 05dadc4e239529c50d6f730c17f0a3aaf35b64fd