]> src.bluestatic.org Git - mailpopbox.git/blob - smtp.go
Add small program to help debug TLS issues using a non-Go stack.
[mailpopbox.git] / smtp.go
1 package main
2
3 import (
4 "crypto/tls"
5 "fmt"
6 "net"
7 "net/mail"
8 "os"
9 "path"
10 "strings"
11
12 "github.com/uber-go/zap"
13
14 "src.bluestatic.org/mailpopbox/smtp"
15 )
16
17 func runSMTPServer(config Config, log zap.Logger) <-chan error {
18 server := smtpServer{
19 config: config,
20 rc: make(chan error),
21 log: log.With(zap.String("server", "smtp")),
22 }
23 go server.run()
24 return server.rc
25 }
26
27 type smtpServer struct {
28 config Config
29 tlsConfig *tls.Config
30
31 log zap.Logger
32
33 rc chan error
34 }
35
36 func (server *smtpServer) run() {
37 var err error
38 server.tlsConfig, err = server.config.GetTLSConfig()
39 if err != nil {
40 server.log.Error("failed to configure TLS", zap.Error(err))
41 server.rc <- err
42 }
43
44 addr := fmt.Sprintf(":%d", server.config.SMTPPort)
45 server.log.Info("starting server", zap.String("address", addr))
46
47 l, err := net.Listen("tcp", addr)
48 if err != nil {
49 server.log.Error("listen", zap.Error(err))
50 server.rc <- err
51 return
52 }
53
54 for {
55 conn, err := l.Accept()
56 if err != nil {
57 server.log.Error("accept", zap.Error(err))
58 server.rc <- err
59 return
60 }
61
62 go smtp.AcceptConnection(conn, server, server.log)
63 }
64 }
65
66 func (server *smtpServer) Name() string {
67 return server.config.Hostname
68 }
69
70 func (server *smtpServer) TLSConfig() *tls.Config {
71 return server.tlsConfig
72 }
73
74 func (server *smtpServer) VerifyAddress(addr mail.Address) smtp.ReplyLine {
75 if server.maildropForAddress(addr) == "" {
76 return smtp.ReplyBadMailbox
77 }
78 return smtp.ReplyOK
79 }
80
81 func (server *smtpServer) OnEHLO() *smtp.ReplyLine {
82 return nil
83 }
84
85 func (server *smtpServer) OnMessageDelivered(en smtp.Envelope) *smtp.ReplyLine {
86 maildrop := server.maildropForAddress(en.RcptTo[0])
87 if maildrop == "" {
88 // TODO: log error
89 return &smtp.ReplyBadMailbox
90 }
91
92 f, err := os.Create(path.Join(maildrop, en.ID+".msg"))
93 if err != nil {
94 // TODO: log error
95 return &smtp.ReplyBadMailbox
96 }
97
98 smtp.WriteEnvelopeForDelivery(f, en)
99 f.Close()
100 return nil
101 }
102
103 func (server *smtpServer) maildropForAddress(addr mail.Address) string {
104 domainIdx := strings.LastIndex(addr.Address, "@")
105 if domainIdx == -1 {
106 return ""
107 }
108
109 domain := addr.Address[domainIdx+1:]
110
111 for _, s := range server.config.Servers {
112 if domain == s.Domain {
113 return s.MaildropPath
114 }
115 }
116
117 return ""
118 }