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
21 "src.bluestatic.org/mailpopbox/pop3"
24 func runPOP3Server(config Config, log *zap.Logger) <-chan ServerControlMessage {
27 controlChan: make(chan ServerControlMessage),
28 log: log.With(zap.String("server", "pop3")),
31 return server.controlChan
34 type pop3Server struct {
36 controlChan chan ServerControlMessage
40 func (server *pop3Server) run() {
41 for _, s := range server.config.Servers {
42 if err := os.Mkdir(s.MaildropPath, 0700); err != nil && !os.IsExist(err) {
43 server.log.Error("failed to open maildrop", zap.Error(err))
44 server.controlChan <- ServerControlFatalError
48 l, err := server.newListener()
50 server.controlChan <- ServerControlFatalError
54 connChan := make(chan net.Conn)
55 go RunAcceptLoop(l, connChan, server.log)
57 reloadChan := CreateReloadSignal()
62 server.log.Info("restarting server")
64 server.controlChan <- ServerControlRestart
66 case conn, ok := <-connChan:
68 go pop3.AcceptConnection(conn, server, server.log)
70 server.controlChan <- ServerControlFatalError
77 func (server *pop3Server) newListener() (net.Listener, error) {
78 tlsConfig, err := server.config.GetTLSConfig()
80 server.log.Error("failed to configure TLS", zap.Error(err))
84 addr := fmt.Sprintf(":%d", server.config.POP3Port)
85 server.log.Info("starting server", zap.String("address", addr))
89 l, err = net.Listen("tcp", addr)
91 l, err = tls.Listen("tcp", addr, tlsConfig)
94 server.log.Error("listen", zap.Error(err))
101 func (server *pop3Server) Name() string {
102 return server.config.Hostname
105 func (server *pop3Server) OpenMailbox(user, pass string) (pop3.Mailbox, error) {
106 for _, s := range server.config.Servers {
107 if user == MailboxAccount+s.Domain && pass == s.MailboxPassword {
108 return server.openMailbox(s.MaildropPath)
111 return nil, errors.New("permission denied")
114 func (server *pop3Server) openMailbox(maildrop string) (*mailbox, error) {
115 files, err := ioutil.ReadDir(maildrop)
117 server.log.Error("failed read maildrop dir", zap.String("dir", maildrop), zap.Error(err))
118 return nil, errors.New("error opening maildrop")
122 messages: make([]message, 0, len(files)),
126 for _, file := range files {
132 filename: path.Join(maildrop, file.Name()),
136 mb.messages = append(mb.messages, msg)
143 type mailbox struct {
147 type message struct {
154 func (m message) UniqueID() string {
156 return path.Base(m.filename[:l-len(".msg")])
159 func (m message) ID() int {
163 func (m message) Size() int {
167 func (m message) Deleted() bool {
171 func (mb *mailbox) ListMessages() ([]pop3.Message, error) {
172 msgs := make([]pop3.Message, len(mb.messages))
173 for i := 0; i < len(mb.messages); i++ {
174 msgs[i] = &mb.messages[i]
179 func (mb *mailbox) GetMessage(id int) pop3.Message {
180 if id == 0 || id > len(mb.messages) {
183 return &mb.messages[id-1]
186 func (mb *mailbox) Retrieve(msg pop3.Message) (io.ReadCloser, error) {
187 filename := msg.(*message).filename
188 return os.Open(filename)
191 func (mb *mailbox) Delete(msg pop3.Message) error {
192 msg.(*message).deleted = true
196 func (mb *mailbox) Close() error {
197 for _, message := range mb.messages {
199 os.Remove(message.filename)
205 func (mb *mailbox) Reset() {
206 for i, _ := range mb.messages {
207 mb.messages[i].deleted = false