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
19 "src.bluestatic.org/mailpopbox/smtp"
22 func runSMTPServer(config Config, log *zap.Logger) <-chan ServerControlMessage {
25 controlChan: make(chan ServerControlMessage),
26 log: log.With(zap.String("server", "smtp")),
28 server.mta = smtp.NewDefaultMTA(&server, server.log)
30 return server.controlChan
33 type smtpServer struct {
41 controlChan chan ServerControlMessage
44 func (server *smtpServer) run() {
45 if !server.loadTLSConfig() {
49 addr := fmt.Sprintf(":%d", server.config.SMTPPort)
50 server.log.Info("starting server", zap.String("address", addr))
52 l, err := net.Listen("tcp", addr)
54 server.log.Error("listen", zap.Error(err))
55 server.controlChan <- ServerControlFatalError
59 connChan := make(chan net.Conn)
60 go RunAcceptLoop(l, connChan, server.log)
62 reloadChan := CreateReloadSignal()
67 if !server.loadTLSConfig() {
70 case conn, ok := <-connChan:
72 go smtp.AcceptConnection(conn, server, server.log)
80 func (server *smtpServer) loadTLSConfig() bool {
82 server.tlsConfig, err = server.config.GetTLSConfig()
84 server.log.Error("failed to configure TLS", zap.Error(err))
85 server.controlChan <- ServerControlFatalError
88 server.log.Info("loaded TLS config")
92 func (server *smtpServer) Name() string {
93 return server.config.Hostname
96 func (server *smtpServer) TLSConfig() *tls.Config {
97 return server.tlsConfig
100 func (server *smtpServer) VerifyAddress(addr mail.Address) smtp.ReplyLine {
101 if server.maildropForAddress(addr) == "" {
102 return smtp.ReplyBadMailbox
107 func (server *smtpServer) Authenticate(authz, authc, passwd string) bool {
108 authcAddr, err := mail.ParseAddress(authc)
113 authzAddr, err := mail.ParseAddress(authz)
114 if authz != "" && err != nil {
118 domain := smtp.DomainForAddress(*authcAddr)
119 for _, s := range server.config.Servers {
120 if domain == s.Domain {
121 authOk := authc == MailboxAccount+s.Domain && passwd == s.MailboxPassword
122 if authzAddr != nil {
123 authOk = authOk && smtp.DomainForAddress(*authzAddr) == domain
131 func (server *smtpServer) DeliverMessage(en smtp.Envelope) *smtp.ReplyLine {
132 maildrop := server.maildropForAddress(en.RcptTo[0])
134 server.log.Error("faild to open maildrop to deliver message", zap.String("id", en.ID))
135 return &smtp.ReplyBadMailbox
138 f, err := os.Create(path.Join(maildrop, en.ID+".msg"))
140 server.log.Error("failed to create message file", zap.String("id", en.ID), zap.Error(err))
141 return &smtp.ReplyBadMailbox
144 smtp.WriteEnvelopeForDelivery(f, en)
149 func (server *smtpServer) RelayMessage(en smtp.Envelope) {
150 go server.mta.RelayMessage(en)
153 func (server *smtpServer) maildropForAddress(addr mail.Address) string {
154 domain := smtp.DomainForAddress(addr)
155 for _, s := range server.config.Servers {
156 if domain == s.Domain {
157 return s.MaildropPath