mirror of https://github.com/jlelse/GoBlog
119 lines
2.1 KiB
Go
119 lines
2.1 KiB
Go
|
// This package contains code to mock an SMTP server and test mail sending.
|
||
|
|
||
|
package mocksmtp
|
||
|
|
||
|
import (
|
||
|
"bufio"
|
||
|
"bytes"
|
||
|
"fmt"
|
||
|
"net"
|
||
|
"net/textproto"
|
||
|
"strconv"
|
||
|
)
|
||
|
|
||
|
// Inspired by https://play.golang.org/p/8mfrqNVWTPK
|
||
|
|
||
|
type ReceivedGetter func() (string, error)
|
||
|
|
||
|
func StartMockSMTPServer() (port int, rg ReceivedGetter, err error) {
|
||
|
|
||
|
// Default server responses
|
||
|
serverResponses := []string{
|
||
|
"220 smtp.example.com Service ready",
|
||
|
"250-ELHO -> ok",
|
||
|
"250-Show Options for ESMTP",
|
||
|
"250-8BITMIME",
|
||
|
"250-SIZE",
|
||
|
"250-AUTH LOGIN PLAIN",
|
||
|
"250 HELP",
|
||
|
"235 AUTH -> ok",
|
||
|
"250 MAIL FROM -> ok",
|
||
|
"250 RCPT TO -> ok",
|
||
|
"354 DATA",
|
||
|
"250 ... -> ok",
|
||
|
"221 QUIT",
|
||
|
}
|
||
|
|
||
|
// Channel to check if error occured or done
|
||
|
var errOrDone = make(chan error)
|
||
|
|
||
|
// Received data buffer
|
||
|
var buffer bytes.Buffer
|
||
|
bufferWriter := bufio.NewWriter(&buffer)
|
||
|
|
||
|
// Start server on random port
|
||
|
mockSmtpServer, err := net.Listen("tcp", "127.0.0.1:0")
|
||
|
if err != nil {
|
||
|
return 0, nil, err
|
||
|
}
|
||
|
|
||
|
// Get port from listener
|
||
|
_, portStr, err := net.SplitHostPort(mockSmtpServer.Addr().String())
|
||
|
if err != nil {
|
||
|
return 0, nil, err
|
||
|
}
|
||
|
port, err = strconv.Atoi(portStr)
|
||
|
if err != nil {
|
||
|
return 0, nil, err
|
||
|
}
|
||
|
|
||
|
// Run mock SMTP server
|
||
|
go func() {
|
||
|
defer close(errOrDone)
|
||
|
defer bufferWriter.Flush()
|
||
|
defer mockSmtpServer.Close()
|
||
|
|
||
|
conn, err := mockSmtpServer.Accept()
|
||
|
if err != nil {
|
||
|
errOrDone <- err
|
||
|
return
|
||
|
}
|
||
|
defer conn.Close()
|
||
|
|
||
|
tc := textproto.NewConn(conn)
|
||
|
defer tc.Close()
|
||
|
|
||
|
for _, res := range serverResponses {
|
||
|
if res == "" {
|
||
|
break
|
||
|
}
|
||
|
|
||
|
_ = tc.PrintfLine("%s", res)
|
||
|
|
||
|
if len(res) >= 4 && res[3] == '-' {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
if res == "221 QUIT" {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
for {
|
||
|
msg, err := tc.ReadLine()
|
||
|
if err != nil {
|
||
|
errOrDone <- err
|
||
|
return
|
||
|
}
|
||
|
|
||
|
_, _ = fmt.Fprintf(bufferWriter, "%s\n", msg)
|
||
|
|
||
|
if res != "354 DATA" || msg == "." {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
// Define function to get received data
|
||
|
getReceivedData := func() (string, error) {
|
||
|
err, hasErr := <-errOrDone
|
||
|
if hasErr {
|
||
|
return "", err
|
||
|
}
|
||
|
return buffer.String(), nil
|
||
|
}
|
||
|
|
||
|
// Return port and function
|
||
|
return port, getReceivedData, nil
|
||
|
}
|