1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
|
package milter
import (
"errors"
"net"
"net/textproto"
)
// Milter protocol version implemented by the server.
//
// Note: Not exported as we might want to support multiple versions
// transparently in the future.
var serverProtocolVersion uint32 = 2
// ErrServerClosed is returned by the Server's Serve method after a call to
// Close.
var ErrServerClosed = errors.New("milter: server closed")
// Milter is an interface for milter callback handlers.
type Milter interface {
// Connect is called to provide SMTP connection data for incoming message.
// Suppress with OptNoConnect.
Connect(host string, family string, port uint16, addr net.IP, m *Modifier) (Response, error)
// Helo is called to process any HELO/EHLO related filters. Suppress with
// OptNoHelo.
Helo(name string, m *Modifier) (Response, error)
// MailFrom is called to process filters on envelope FROM address. Suppress
// with OptNoMailFrom.
MailFrom(from string, m *Modifier) (Response, error)
// RcptTo is called to process filters on envelope TO address. Suppress with
// OptNoRcptTo.
RcptTo(rcptTo string, m *Modifier) (Response, error)
// Header is called once for each header in incoming message. Suppress with
// OptNoHeaders.
Header(name string, value string, m *Modifier) (Response, error)
// Headers is called when all message headers have been processed. Suppress
// with OptNoEOH.
Headers(h textproto.MIMEHeader, m *Modifier) (Response, error)
// BodyChunk is called to process next message body chunk data (up to 64KB
// in size). Suppress with OptNoBody.
BodyChunk(chunk []byte, m *Modifier) (Response, error)
// Body is called at the end of each message. All changes to message's
// content & attributes must be done here.
Body(m *Modifier) (Response, error)
}
// NoOpMilter is a dummy Milter implementation that does nothing.
type NoOpMilter struct{}
var _ Milter = NoOpMilter{}
func (NoOpMilter) Connect(host string, family string, port uint16, addr net.IP, m *Modifier) (Response, error) {
return RespContinue, nil
}
func (NoOpMilter) Helo(name string, m *Modifier) (Response, error) {
return RespContinue, nil
}
func (NoOpMilter) MailFrom(from string, m *Modifier) (Response, error) {
return RespContinue, nil
}
func (NoOpMilter) RcptTo(rcptTo string, m *Modifier) (Response, error) {
return RespContinue, nil
}
func (NoOpMilter) Header(name string, value string, m *Modifier) (Response, error) {
return RespContinue, nil
}
func (NoOpMilter) Headers(h textproto.MIMEHeader, m *Modifier) (Response, error) {
return RespContinue, nil
}
func (NoOpMilter) BodyChunk(chunk []byte, m *Modifier) (Response, error) {
return RespContinue, nil
}
func (NoOpMilter) Body(m *Modifier) (Response, error) {
return RespAccept, nil
}
// Server is a milter server.
type Server struct {
NewMilter func() Milter
Actions OptAction
Protocol OptProtocol
listeners []net.Listener
closed bool
}
// Serve starts the server.
func (s *Server) Serve(ln net.Listener) error {
defer ln.Close()
s.listeners = append(s.listeners, ln)
for {
conn, err := ln.Accept()
if err != nil {
if s.closed {
return ErrServerClosed
}
return err
}
session := milterSession{
server: s,
actions: s.Actions,
protocol: s.Protocol,
conn: conn,
backend: s.NewMilter(),
}
go session.HandleMilterCommands()
}
}
func (s *Server) Close() error {
s.closed = true
for _, ln := range s.listeners {
if err := ln.Close(); err != nil {
return err
}
}
return nil
}
|