forked from phalaaxx/milter
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Breaking Change: functional options, New interfacenames
- Loading branch information
1 parent
1247c53
commit 63a5cca
Showing
12 changed files
with
380 additions
and
127 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
package milter | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"net" | ||
"net/textproto" | ||
) | ||
|
||
// A DefaultSession can be used as a basic implementation for SessionHandler Interface | ||
// It has already a From and Rcpts[] field, mostly used to embett in your own | ||
// Session struct. | ||
type DefaultSession struct { | ||
SID string // Session ID | ||
MID string // Mail ID | ||
From string | ||
ClientName string | ||
HeloName string | ||
ClientIP net.IP | ||
Rcpts []string | ||
MessageHeaders textproto.MIMEHeader | ||
Message *bytes.Buffer | ||
} | ||
|
||
// https://github.com/mschneider82/milter/blob/master/interface.go | ||
func (e *DefaultSession) Init(sid, mid string) { | ||
e.SID, e.MID = sid, mid | ||
e.Message = new(bytes.Buffer) | ||
return | ||
} | ||
|
||
func (e *DefaultSession) Disconnect() { | ||
return | ||
} | ||
|
||
func (e *DefaultSession) Connect(name, family string, port uint16, ip net.IP, m *Modifier) (Response, error) { | ||
e.ClientName = name | ||
e.ClientIP = ip | ||
return RespContinue, nil | ||
} | ||
|
||
func (e *DefaultSession) Helo(name string, m *Modifier) (Response, error) { | ||
e.HeloName = name | ||
return RespContinue, nil | ||
} | ||
|
||
func (e *DefaultSession) MailFrom(from string, m *Modifier) (Response, error) { | ||
e.From = from | ||
return RespContinue, nil | ||
} | ||
|
||
func (e *DefaultSession) RcptTo(rcptTo string, m *Modifier) (Response, error) { | ||
e.Rcpts = append(e.Rcpts, rcptTo) | ||
return RespContinue, nil | ||
} | ||
|
||
/* handle headers one by one */ | ||
func (e *DefaultSession) Header(name, value string, m *Modifier) (Response, error) { | ||
headerLine := fmt.Sprintf("%s: %s\r\n", name, value) | ||
if _, err := e.Message.WriteString(headerLine); err != nil { | ||
return nil, err | ||
} | ||
return RespContinue, nil | ||
} | ||
|
||
// emptyLine is needed between Headers and Body | ||
const emptyLine = "\r\n" | ||
|
||
/* at end of headers initialize message buffer and add headers to it */ | ||
func (e *DefaultSession) Headers(headers textproto.MIMEHeader, m *Modifier) (Response, error) { | ||
// return accept if not a multipart message | ||
e.MessageHeaders = headers | ||
|
||
if _, err := e.Message.WriteString(emptyLine); err != nil { | ||
return nil, err | ||
} | ||
// continue with milter processing | ||
return RespContinue, nil | ||
} | ||
|
||
// accept body chunk | ||
func (e *DefaultSession) BodyChunk(chunk []byte, m *Modifier) (Response, error) { | ||
// save chunk to buffer | ||
if _, err := e.Message.Write(chunk); err != nil { | ||
return nil, err | ||
} | ||
return RespContinue, nil | ||
} | ||
|
||
/* Body is called when email message body has been sent */ | ||
func (e *DefaultSession) Body(m *Modifier) (Response, error) { | ||
// accept message by default | ||
return RespAccept, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package milter_test | ||
|
||
import ( | ||
"fmt" | ||
"io/ioutil" | ||
"log" | ||
|
||
"github.com/mschneider82/milter" | ||
) | ||
|
||
// A Session embetted the SessionHandler Interface | ||
type Session struct { | ||
milter.DefaultSession | ||
} | ||
|
||
func (s *Session) Body(m *milter.Modifier) (milter.Response, error) { | ||
b, _ := ioutil.ReadAll(s.Message) | ||
log.Printf("Mail's first 100 chars: %s", string(b[0:100])) | ||
return milter.RespAccept, nil | ||
} | ||
|
||
func ExampleRun() { | ||
panichandler := func(e error) { | ||
fmt.Printf("Panic happend: %s\n", e.Error()) | ||
} | ||
|
||
setsymlist := make(milter.RequestMacros) | ||
setsymlist[milter.SMFIM_CONNECT] = []milter.Macro{milter.MACRO_DAEMON_NAME, milter.Macro("{client_addr}")} | ||
|
||
milterfactory := func() (milter.SessionHandler, milter.OptAction, milter.OptProtocol, milter.RequestMacros) { | ||
return &Session{}, | ||
milter.OptAllActions, // BitMask for wanted Actions | ||
0, // BitMask for unwanted SMTP Parts; 0 = nothing to opt out | ||
setsymlist // optional: can be nil | ||
} | ||
|
||
m := milter.New(milterfactory, | ||
milter.WithTCPListener("127.0.0.1:12349"), | ||
milter.WithLogger(milter.StdOutLogger), | ||
milter.WithPanicHandler(panichandler), | ||
) | ||
err := m.Run() | ||
if err != nil { | ||
log.Fatalf("Error: %s", err.Error()) | ||
} | ||
|
||
defer m.Close() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,24 @@ | ||
package milter | ||
|
||
// Logger is a interface to inject a custom logger | ||
type Logger interface { | ||
import "log" | ||
|
||
// CustomLogger is a interface to inject a custom logger | ||
type CustomLogger interface { | ||
Printf(format string, v ...interface{}) | ||
} | ||
|
||
type nopLogger struct{} | ||
|
||
func (n nopLogger) Printf(format string, v ...interface{}) {} | ||
|
||
// NopLogger can be used to discard all logs caused by milter library | ||
var NopLogger = CustomLogger(nopLogger{}) | ||
|
||
// StdOutLogger is the default logger used if no Logger was supplied | ||
var StdOutLogger = CustomLogger(stdoutLogger{}) | ||
|
||
type stdoutLogger struct{} | ||
|
||
func (s stdoutLogger) Printf(format string, v ...interface{}) { | ||
log.Printf(format, v...) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package milter | ||
|
||
import ( | ||
"log" | ||
"net" | ||
) | ||
|
||
// An Option configures a Server using the functional options paradigm | ||
// popularized by Rob Pike. | ||
type Option interface { | ||
apply(*Server) | ||
} | ||
|
||
// An ListenerOption configures a Server using the functional options paradigm | ||
// popularized by Rob Pike. | ||
type ListenerOption interface { | ||
lapply(*Server) | ||
} | ||
|
||
type optionFunc func(*Server) | ||
|
||
func (f optionFunc) apply(Server *Server) { f(Server) } | ||
|
||
type loptionFunc func(*Server) | ||
|
||
func (f loptionFunc) lapply(Server *Server) { f(Server) } | ||
|
||
// WithLogger adds an Logger | ||
func WithLogger(l CustomLogger) Option { | ||
return optionFunc(func(server *Server) { | ||
server.logger = l | ||
}) | ||
} | ||
|
||
// WithListener adds an Listener | ||
func WithListener(listener net.Listener) ListenerOption { | ||
return loptionFunc(func(server *Server) { | ||
server.listener = listener | ||
}) | ||
} | ||
|
||
// WithTCPListener e.g. "127.0.0.1:12349" | ||
func WithTCPListener(address string) ListenerOption { | ||
protocol := "tcp" | ||
// bind to listening address | ||
socket, err := net.Listen(protocol, address) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
return WithListener(socket) | ||
} | ||
|
||
// WithUnixSocket e.g. "/var/spool/postfix/var/run/milter/milter.sock" | ||
// make sure that the file does not exist! | ||
func WithUnixSocket(file string) ListenerOption { | ||
protocol := "unix" | ||
// bind to listening address | ||
socket, err := net.Listen(protocol, file) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
return WithListener(socket) | ||
} | ||
|
||
// WithPanicHandler Adds the error panic handler | ||
// Multiple panic handlers are supported | ||
func WithPanicHandler(handler func(error)) Option { | ||
return optionFunc(func(server *Server) { | ||
server.errHandlers = append(server.errHandlers, handler) | ||
}) | ||
} |
Oops, something went wrong.