Merge branch 'master' into outbound
[mailpopbox.git] / smtp / server.go
1 // mailpopbox
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
6
7 package smtp
8
9 import (
10 "crypto/tls"
11 "regexp"
12 "fmt"
13 "io"
14 "net"
15 "net/mail"
16 "strings"
17 "time"
18 )
19
20 type ReplyLine struct {
21 Code int
22 Message string
23 }
24
25 func (l ReplyLine) String() string {
26 return fmt.Sprintf("%d %s", l.Code, l.Message)
27 }
28
29 var SendAsSubject = regexp.MustCompile(`(?i)\[sendas:\s*([a-zA-Z0-9\.\-_]+)\]`)
30
31 var (
32 ReplyOK = ReplyLine{250, "OK"}
33 ReplyBadSyntax = ReplyLine{501, "syntax error"}
34 ReplyBadSequence = ReplyLine{503, "bad sequence of commands"}
35 ReplyBadMailbox = ReplyLine{550, "mailbox unavailable"}
36 ReplyMailboxUnallowed = ReplyLine{553, "mailbox name not allowed"}
37 )
38
39 func DomainForAddress(addr mail.Address) string {
40 return DomainForAddressString(addr.Address)
41 }
42
43 func DomainForAddressString(address string) string {
44 domainIdx := strings.LastIndex(address, "@")
45 if domainIdx == -1 {
46 return ""
47 }
48 return address[domainIdx+1:]
49 }
50
51 type Envelope struct {
52 RemoteAddr net.Addr
53 EHLO string
54 MailFrom mail.Address
55 RcptTo []mail.Address
56 Data []byte
57 Received time.Time
58 ID string
59 }
60
61 func WriteEnvelopeForDelivery(w io.Writer, e Envelope) {
62 fmt.Fprintf(w, "Delivered-To: <%s>\r\n", e.RcptTo[0].Address)
63 fmt.Fprintf(w, "Return-Path: <%s>\r\n", e.MailFrom.Address)
64 w.Write(e.Data)
65 }
66
67 type Server interface {
68 Name() string
69 TLSConfig() *tls.Config
70 VerifyAddress(mail.Address) ReplyLine
71 // Verify that the authc+passwd identity can send mail as authz.
72 Authenticate(authz, authc, passwd string) bool
73 OnMessageDelivered(Envelope) *ReplyLine
74
75 // RelayMessage instructs the server to send the Envelope to another
76 // MTA for outbound delivery.
77 RelayMessage(Envelope)
78 }
79
80 type EmptyServerCallbacks struct{}
81
82 func (*EmptyServerCallbacks) TLSConfig() *tls.Config {
83 return nil
84 }
85
86 func (*EmptyServerCallbacks) VerifyAddress(mail.Address) ReplyLine {
87 return ReplyOK
88 }
89
90 func (*EmptyServerCallbacks) Authenticate(authz, authc, passwd string) bool {
91 return false
92 }
93
94 func (*EmptyServerCallbacks) OnMessageDelivered(Envelope) *ReplyLine {
95 return nil
96 }
97
98 func (*EmptyServerCallbacks) RelayMessage(Envelope) {
99 }