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 type ReplyLine struct {
26 func (l ReplyLine) String() string {
27 return fmt.Sprintf("%d %s", l.Code, l.Message)
30 var SendAsSubject = regexp.MustCompile(`(?i)\[sendas:\s*([a-zA-Z0-9\.\-_]+)\]`)
33 ReplyOK = ReplyLine{250, "OK"}
34 ReplyBadSyntax = ReplyLine{501, "syntax error"}
35 ReplyBadSequence = ReplyLine{503, "bad sequence of commands"}
36 ReplyBadMailbox = ReplyLine{550, "mailbox unavailable"}
37 ReplyMailboxUnallowed = ReplyLine{553, "mailbox name not allowed"}
40 func DomainForAddress(addr mail.Address) string {
41 return DomainForAddressString(addr.Address)
44 func DomainForAddressString(address string) string {
45 domainIdx := strings.LastIndex(address, "@")
49 return address[domainIdx+1:]
52 type Envelope struct {
62 func WriteEnvelopeForDelivery(w io.Writer, e Envelope) {
63 fmt.Fprintf(w, "Delivered-To: <%s>\r\n", e.RcptTo[0].Address)
64 fmt.Fprintf(w, "Return-Path: <%s>\r\n", e.MailFrom.Address)
68 func generateEnvelopeId(prefix string, t time.Time) string {
71 return fmt.Sprintf("%s.%d.%x", prefix, t.UnixNano(), idBytes)
74 // lookupRemoteHost attempts to reverse look-up the provided IP address. On
75 // success, it returns the hostname and the IP as formatted for a receive
76 // trace. If the lookup fails, it just returns the original IP.
77 func lookupRemoteHost(addr net.Addr) string {
78 rhost, _, err := net.SplitHostPort(addr.String())
83 rhosts, err := net.LookupAddr(rhost)
85 rhost = fmt.Sprintf("%s [%s]", rhosts[0], rhost)
91 type Server interface {
93 TLSConfig() *tls.Config
94 VerifyAddress(mail.Address) ReplyLine
95 // Verify that the authc+passwd identity can send mail as authz.
96 Authenticate(authz, authc, passwd string) bool
97 DeliverMessage(Envelope) *ReplyLine
99 // RelayMessage instructs the server to send the Envelope to another
100 // MTA for outbound delivery.
101 RelayMessage(Envelope)
104 type EmptyServerCallbacks struct{}
106 func (*EmptyServerCallbacks) TLSConfig() *tls.Config {
110 func (*EmptyServerCallbacks) VerifyAddress(mail.Address) ReplyLine {
114 func (*EmptyServerCallbacks) Authenticate(authz, authc, passwd string) bool {
118 func (*EmptyServerCallbacks) DeliverMessage(Envelope) *ReplyLine {
122 func (*EmptyServerCallbacks) RelayMessage(Envelope) {