Implement STARTTLS in the SMTP server.
[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 "src.bluestatic.org/mailpopbox/smtp"
13 )
14
15 func runSMTPServer(config Config) <-chan error {
16 server := smtpServer{
17 config: config,
18 rc: make(chan error),
19 }
20 go server.run()
21 return server.rc
22 }
23
24 type smtpServer struct {
25 config Config
26 tlsConfig *tls.Config
27
28 rc chan error
29 }
30
31 func (server *smtpServer) run() {
32 var err error
33 server.tlsConfig, err = server.config.GetTLSConfig()
34 if err != nil {
35 server.rc <- err
36 }
37
38 l, err := net.Listen("tcp", fmt.Sprintf(":%d", server.config.SMTPPort))
39 if err != nil {
40 server.rc <- err
41 return
42 }
43
44 for {
45 conn, err := l.Accept()
46 if err != nil {
47 server.rc <- err
48 return
49 }
50
51 go smtp.AcceptConnection(conn, server)
52 }
53 }
54
55 func (server *smtpServer) Name() string {
56 return server.config.Hostname
57 }
58
59 func (server *smtpServer) TLSConfig() *tls.Config {
60 return server.tlsConfig
61 }
62
63 func (server *smtpServer) VerifyAddress(addr mail.Address) smtp.ReplyLine {
64 if server.maildropForAddress(addr) == "" {
65 return smtp.ReplyBadMailbox
66 }
67 return smtp.ReplyOK
68 }
69
70 func (server *smtpServer) OnEHLO() *smtp.ReplyLine {
71 return nil
72 }
73
74 func (server *smtpServer) OnMessageDelivered(en smtp.Envelope) *smtp.ReplyLine {
75 maildrop := server.maildropForAddress(en.RcptTo[0])
76 if maildrop == "" {
77 // TODO: log error
78 return &smtp.ReplyBadMailbox
79 }
80
81 f, err := os.Create(path.Join(maildrop, en.ID+".msg"))
82 if err != nil {
83 // TODO: log error
84 return &smtp.ReplyBadMailbox
85 }
86
87 smtp.WriteEnvelopeForDelivery(f, en)
88 f.Close()
89 return nil
90 }
91
92 func (server *smtpServer) maildropForAddress(addr mail.Address) string {
93 domainIdx := strings.LastIndex(addr.Address, "@")
94 if domainIdx == -1 {
95 return ""
96 }
97
98 domain := addr.Address[domainIdx+1:]
99
100 for _, s := range server.config.Servers {
101 if domain == s.Domain {
102 return s.MaildropPath
103 }
104 }
105
106 return ""
107 }