13 "github.com/uber-go/zap"
15 "src.bluestatic.org/mailpopbox/pop3"
18 func runPOP3Server(config Config, log zap.Logger) <-chan error {
22 log: log.With(zap.String("server", "pop3")),
28 type pop3Server struct {
34 func (server *pop3Server) run() {
35 for _, s := range server.config.Servers {
36 if err := os.Mkdir(s.MaildropPath, 0700); err != nil && !os.IsExist(err) {
37 server.log.Error("failed to open maildrop", zap.Error(err))
42 tlsConfig, err := server.config.GetTLSConfig()
44 server.log.Error("failed to configure TLS", zap.Error(err))
49 addr := fmt.Sprintf(":%d", server.config.POP3Port)
50 server.log.Info("starting server", zap.String("address", addr))
54 l, err = net.Listen("tcp", addr)
56 l, err = tls.Listen("tcp", addr, tlsConfig)
59 server.log.Error("listen", zap.Error(err))
65 conn, err := l.Accept()
67 server.log.Error("accept", zap.Error(err))
72 go pop3.AcceptConnection(conn, server, server.log)
76 func (server *pop3Server) Name() string {
77 return server.config.Hostname
80 func (server *pop3Server) OpenMailbox(user, pass string) (pop3.Mailbox, error) {
81 for _, s := range server.config.Servers {
82 if user == "mailbox@"+s.Domain && pass == s.MailboxPassword {
83 return server.openMailbox(s.MaildropPath)
86 return nil, errors.New("permission denied")
89 func (server *pop3Server) openMailbox(maildrop string) (*mailbox, error) {
90 files, err := ioutil.ReadDir(maildrop)
92 // TODO: hide error, log instead
97 messages: make([]message, 0, len(files)),
101 for _, file := range files {
107 filename: path.Join(maildrop, file.Name()),
111 mb.messages = append(mb.messages, msg)
118 type mailbox struct {
122 type message struct {
129 func (m message) ID() int {
133 func (m message) Size() int {
137 func (m message) Deleted() bool {
141 func (mb *mailbox) ListMessages() ([]pop3.Message, error) {
142 msgs := make([]pop3.Message, len(mb.messages))
143 for i := 0; i < len(mb.messages); i++ {
144 msgs[i] = &mb.messages[i]
149 func (mb *mailbox) GetMessage(id int) pop3.Message {
150 if id > len(mb.messages) {
153 return &mb.messages[id-1]
156 func (mb *mailbox) Retrieve(msg pop3.Message) (io.ReadCloser, error) {
157 filename := msg.(*message).filename
158 return os.Open(filename)
161 func (mb *mailbox) Delete(msg pop3.Message) error {
162 msg.(*message).deleted = true
166 func (mb *mailbox) Close() error {
167 for _, message := range mb.messages {
169 os.Remove(message.filename)
175 func (mb *mailbox) Reset() {
176 for _, message := range mb.messages {
177 message.deleted = false