mirror of
https://github.com/jhillyerd/inbucket.git
synced 2025-12-17 01:27:01 +00:00
feat: Add SMTPSession and BeforeRcptToAccepted event (#541)
Signed-off-by: James Hillyerd <james@hillyerd.com>
This commit is contained in:
@@ -47,3 +47,9 @@ type SMTPResponse struct {
|
||||
ErrorCode int // SMTP error code to respond with on deny.
|
||||
ErrorMsg string // SMTP error message to respond with on deny.
|
||||
}
|
||||
|
||||
// SMTPSession captures SMTP `MAIL FROM` & `RCPT TO` values prior to mail DATA being received.
|
||||
type SMTPSession struct {
|
||||
From *mail.Address
|
||||
To []*mail.Address
|
||||
}
|
||||
|
||||
@@ -20,10 +20,11 @@ type Host struct {
|
||||
// processed asynchronously with respect to the rest of Inbuckets operation. However, an event
|
||||
// listener will not be called until the one before it completes.
|
||||
type Events struct {
|
||||
AfterMessageDeleted AsyncEventBroker[event.MessageMetadata]
|
||||
AfterMessageStored AsyncEventBroker[event.MessageMetadata]
|
||||
BeforeMailAccepted EventBroker[event.AddressParts, event.SMTPResponse]
|
||||
BeforeMessageStored EventBroker[event.InboundMessage, event.InboundMessage]
|
||||
AfterMessageDeleted AsyncEventBroker[event.MessageMetadata]
|
||||
AfterMessageStored AsyncEventBroker[event.MessageMetadata]
|
||||
BeforeMailAccepted EventBroker[event.AddressParts, event.SMTPResponse]
|
||||
BeforeMessageStored EventBroker[event.InboundMessage, event.InboundMessage]
|
||||
BeforeRcptToAccepted EventBroker[event.SMTPSession, event.SMTPResponse]
|
||||
}
|
||||
|
||||
// Void indicates the event emitter will ignore any value returned by listeners.
|
||||
|
||||
@@ -29,8 +29,9 @@ type InbucketAfterFuncs struct {
|
||||
// InbucketBeforeFuncs holds references to Lua extension functions to be called
|
||||
// before Inbucket handles an event.
|
||||
type InbucketBeforeFuncs struct {
|
||||
MailAccepted *lua.LFunction
|
||||
MessageStored *lua.LFunction
|
||||
MailAccepted *lua.LFunction
|
||||
MessageStored *lua.LFunction
|
||||
RcptToAccepted *lua.LFunction
|
||||
}
|
||||
|
||||
func registerInbucketTypes(ls *lua.LState) {
|
||||
@@ -189,6 +190,8 @@ func inbucketBeforeIndex(ls *lua.LState) int {
|
||||
ls.Push(funcOrNil(before.MailAccepted))
|
||||
case "message_stored":
|
||||
ls.Push(funcOrNil(before.MessageStored))
|
||||
case "rcpt_to_accepted":
|
||||
ls.Push(funcOrNil(before.RcptToAccepted))
|
||||
default:
|
||||
// Unknown field.
|
||||
ls.Push(lua.LNil)
|
||||
@@ -207,6 +210,8 @@ func inbucketBeforeNewIndex(ls *lua.LState) int {
|
||||
m.MailAccepted = ls.CheckFunction(3)
|
||||
case "message_stored":
|
||||
m.MessageStored = ls.CheckFunction(3)
|
||||
case "rcpt_to_accepted":
|
||||
m.RcptToAccepted = ls.CheckFunction(3)
|
||||
default:
|
||||
ls.RaiseError("invalid inbucket.before index %q", index)
|
||||
}
|
||||
|
||||
70
pkg/extension/luahost/bind_smtpsession.go
Normal file
70
pkg/extension/luahost/bind_smtpsession.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package luahost
|
||||
|
||||
import (
|
||||
"github.com/inbucket/inbucket/v3/pkg/extension/event"
|
||||
lua "github.com/yuin/gopher-lua"
|
||||
)
|
||||
|
||||
const smtpSessionName = "smtp_session"
|
||||
|
||||
func registerSMTPSessionType(ls *lua.LState) {
|
||||
mt := ls.NewTypeMetatable(smtpSessionName)
|
||||
ls.SetGlobal(smtpSessionName, mt)
|
||||
|
||||
// Static attributes.
|
||||
ls.SetField(mt, "new", ls.NewFunction(newSMTPSession))
|
||||
|
||||
// Methods.
|
||||
ls.SetField(mt, "__index", ls.NewFunction(smtpSessionIndex))
|
||||
}
|
||||
|
||||
func newSMTPSession(ls *lua.LState) int {
|
||||
val := &event.SMTPSession{}
|
||||
ud := wrapSMTPSession(ls, val)
|
||||
ls.Push(ud)
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
func wrapSMTPSession(ls *lua.LState, val *event.SMTPSession) *lua.LUserData {
|
||||
ud := ls.NewUserData()
|
||||
ud.Value = val
|
||||
ls.SetMetatable(ud, ls.GetTypeMetatable(smtpSessionName))
|
||||
|
||||
return ud
|
||||
}
|
||||
|
||||
// Checks there is an SMTPSession at stack position `pos`, else throws Lua error.
|
||||
func checkSMTPSession(ls *lua.LState, pos int) *event.SMTPSession {
|
||||
ud := ls.CheckUserData(pos)
|
||||
if v, ok := ud.Value.(*event.SMTPSession); ok {
|
||||
return v
|
||||
}
|
||||
ls.ArgError(pos, smtpSessionName+" expected")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Gets a field value from SMTPSession user object. This emulates a Lua table,
|
||||
// allowing `msg.subject` instead of a Lua object syntax of `msg:subject()`.
|
||||
func smtpSessionIndex(ls *lua.LState) int {
|
||||
m := checkSMTPSession(ls, 1)
|
||||
field := ls.CheckString(2)
|
||||
|
||||
// Push the requested field's value onto the stack.
|
||||
switch field {
|
||||
case "from":
|
||||
ls.Push(wrapMailAddress(ls, m.From))
|
||||
case "to":
|
||||
lt := &lua.LTable{}
|
||||
for _, v := range m.To {
|
||||
addr := v
|
||||
lt.Append(wrapMailAddress(ls, addr))
|
||||
}
|
||||
ls.Push(lt)
|
||||
default:
|
||||
// Unknown field.
|
||||
ls.Push(lua.LNil)
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
38
pkg/extension/luahost/bind_smtpsession_test.go
Normal file
38
pkg/extension/luahost/bind_smtpsession_test.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package luahost
|
||||
|
||||
import (
|
||||
"net/mail"
|
||||
"testing"
|
||||
|
||||
"github.com/inbucket/inbucket/v3/pkg/extension/event"
|
||||
"github.com/inbucket/inbucket/v3/pkg/test"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSMTPSessionGetters(t *testing.T) {
|
||||
want := &event.SMTPSession{
|
||||
From: &mail.Address{Name: "name1", Address: "addr1"},
|
||||
To: []*mail.Address{
|
||||
{Name: "name2", Address: "addr2"},
|
||||
{Name: "name3", Address: "addr3"},
|
||||
},
|
||||
}
|
||||
script := `
|
||||
assert(msg, "msg should not be nil")
|
||||
|
||||
assert_eq(msg.from.name, "name1", "from.name")
|
||||
assert_eq(msg.from.address, "addr1", "from.address")
|
||||
|
||||
assert_eq(#msg.to, 2, "#msg.to")
|
||||
assert_eq(msg.to[1].name, "name2", "to[1].name")
|
||||
assert_eq(msg.to[1].address, "addr2", "to[1].address")
|
||||
assert_eq(msg.to[2].name, "name3", "to[2].name")
|
||||
assert_eq(msg.to[2].address, "addr3", "to[2].address")
|
||||
`
|
||||
|
||||
ls, _ := test.NewLuaState()
|
||||
registerSMTPSessionType(ls)
|
||||
registerMailAddressType(ls)
|
||||
ls.SetGlobal("msg", wrapSMTPSession(ls, want))
|
||||
require.NoError(t, ls.DoString(script))
|
||||
}
|
||||
@@ -112,6 +112,9 @@ func (h *Host) wireFunctions(logger zerolog.Logger, ls *lua.LState) {
|
||||
if ib.Before.MessageStored != nil {
|
||||
events.BeforeMessageStored.AddListener(listenerName, h.handleBeforeMessageStored)
|
||||
}
|
||||
if ib.Before.RcptToAccepted != nil {
|
||||
events.BeforeRcptToAccepted.AddListener(listenerName, h.handleBeforeRcptToAccepted)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Host) handleAfterMessageDeleted(msg event.MessageMetadata) {
|
||||
@@ -169,9 +172,33 @@ func (h *Host) handleBeforeMailAccepted(addr event.AddressParts) *event.SMTPResp
|
||||
ls.Pop(1)
|
||||
logger.Debug().Msgf("Lua function returned %q (%v)", lval, lval.Type().String())
|
||||
|
||||
if lval.Type() == lua.LTNil || lua.LVIsFalse(lval) {
|
||||
result, err := unwrapSMTPResponse(lval)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("Bad response from Lua Function")
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (h *Host) handleBeforeRcptToAccepted(session event.SMTPSession) *event.SMTPResponse {
|
||||
logger, ls, ib, ok := h.prepareInbucketFuncCall("before.rcpt_to_accepted")
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
defer h.pool.putState(ls)
|
||||
|
||||
logger.Debug().Msgf("Calling Lua function with %+v", session)
|
||||
if err := ls.CallByParam(
|
||||
lua.P{Fn: ib.Before.RcptToAccepted, NRet: 1, Protect: true},
|
||||
wrapSMTPSession(ls, &session),
|
||||
); err != nil {
|
||||
logger.Error().Err(err).Msg("Failed to call Lua function")
|
||||
return nil
|
||||
}
|
||||
|
||||
lval := ls.Get(-1)
|
||||
ls.Pop(1)
|
||||
logger.Debug().Msgf("Lua function returned %q (%v)", lval, lval.Type().String())
|
||||
|
||||
result, err := unwrapSMTPResponse(lval)
|
||||
if err != nil {
|
||||
@@ -201,7 +228,7 @@ func (h *Host) handleBeforeMessageStored(msg event.InboundMessage) *event.Inboun
|
||||
ls.Pop(1)
|
||||
logger.Debug().Msgf("Lua function returned %q (%v)", lval, lval.Type().String())
|
||||
|
||||
if lval.Type() == lua.LTNil || lua.LVIsFalse(lval) {
|
||||
if lua.LVIsFalse(lval) {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -211,3 +211,85 @@ func TestBeforeMessageStored(t *testing.T) {
|
||||
}
|
||||
assert.Equal(t, want, got, "Response InboundMessage did not match")
|
||||
}
|
||||
|
||||
func TestBeforeMessageStoredNilReturn(t *testing.T) {
|
||||
// Event to send.
|
||||
msg := event.InboundMessage{
|
||||
Mailboxes: []string{"one", "two"},
|
||||
From: &mail.Address{Name: "From Name", Address: "from@example.com"},
|
||||
To: []*mail.Address{
|
||||
{Name: "To1 Name", Address: "to1@example.com"},
|
||||
{Name: "To2 Name", Address: "to2@example.com"},
|
||||
},
|
||||
Subject: "inbound subj",
|
||||
Size: 42,
|
||||
}
|
||||
|
||||
// Register lua event listener.
|
||||
script := `
|
||||
async = true
|
||||
|
||||
function inbucket.before.message_stored(msg)
|
||||
assert(msg)
|
||||
notify:send(asserts_ok)
|
||||
|
||||
-- Generate response.
|
||||
return nil
|
||||
end
|
||||
`
|
||||
extHost := extension.NewHost()
|
||||
luaHost, err := luahost.NewFromReader(consoleLogger, extHost,
|
||||
strings.NewReader(test.LuaInit+script), "test.lua")
|
||||
require.NoError(t, err)
|
||||
notify := luaHost.CreateChannel("notify")
|
||||
|
||||
// Send event to be accepted.
|
||||
got := extHost.Events.BeforeMessageStored.Emit(&msg)
|
||||
require.Nil(t, got, "Expected nil result from Emit()")
|
||||
|
||||
// Verify Lua assertions passed.
|
||||
test.AssertNotified(t, notify)
|
||||
}
|
||||
|
||||
func TestBeforeRcptToAccepted(t *testing.T) {
|
||||
// Event to send.
|
||||
session := event.SMTPSession{
|
||||
From: &mail.Address{Name: "", Address: "from@example.com"},
|
||||
To: []*mail.Address{
|
||||
{Name: "", Address: "to1@example.com"},
|
||||
{Name: "", Address: "to2@example.com"},
|
||||
},
|
||||
}
|
||||
|
||||
// Register lua event listener.
|
||||
script := `
|
||||
async = true
|
||||
|
||||
function inbucket.before.rcpt_to_accepted(msg)
|
||||
-- Verify incoming values.
|
||||
assert_eq(msg.from.address, "from@example.com")
|
||||
assert_eq(2, #msg.to, "#msg.to")
|
||||
assert_eq(msg.to[1].address, "to1@example.com")
|
||||
assert_eq(msg.to[2].address, "to2@example.com")
|
||||
notify:send(asserts_ok)
|
||||
|
||||
return smtp.allow()
|
||||
end
|
||||
`
|
||||
extHost := extension.NewHost()
|
||||
luaHost, err := luahost.NewFromReader(consoleLogger, extHost,
|
||||
strings.NewReader(test.LuaInit+script), "test.lua")
|
||||
require.NoError(t, err)
|
||||
notify := luaHost.CreateChannel("notify")
|
||||
|
||||
// Send event to be accepted.
|
||||
got := extHost.Events.BeforeRcptToAccepted.Emit(&session)
|
||||
require.NotNil(t, got, "Expected result from Emit()")
|
||||
|
||||
// Verify Lua assertions passed.
|
||||
test.AssertNotified(t, notify)
|
||||
|
||||
// Verify response values.
|
||||
want := event.SMTPResponse{Action: event.ActionAllow}
|
||||
assert.Equal(t, want, *got)
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ func (lp *statePool) newState() (*lua.LState, error) {
|
||||
registerMailAddressType(ls)
|
||||
registerMessageMetadataType(ls)
|
||||
registerSMTPResponseType(ls)
|
||||
registerSMTPSessionType(ls)
|
||||
|
||||
// Run compiled script.
|
||||
ls.Push(ls.NewFunctionFromProto(lp.funcProto))
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/mail"
|
||||
"net/textproto"
|
||||
"regexp"
|
||||
"strconv"
|
||||
@@ -495,7 +496,26 @@ func (s *Session) mailHandler(cmd string, arg string) {
|
||||
s.logger.Warn().Str("to", addr).Err(err).Msg("Bad address as RCPT arg")
|
||||
return
|
||||
}
|
||||
if !recip.ShouldAccept() {
|
||||
|
||||
// Append new address to extSession for inspection.
|
||||
addrCopy := recip.Address
|
||||
extSession := s.extSession()
|
||||
extSession.To = append(extSession.To, &addrCopy)
|
||||
|
||||
// Process through extensions.
|
||||
extAction := event.ActionDefer
|
||||
extResult := s.extHost.Events.BeforeRcptToAccepted.Emit(extSession)
|
||||
if extResult != nil {
|
||||
extAction = extResult.Action
|
||||
}
|
||||
if extAction == event.ActionDeny {
|
||||
s.send(fmt.Sprintf("%03d %s", extResult.ErrorCode, extResult.ErrorMsg))
|
||||
s.logger.Warn().Msgf("Extension denied mail to <%v>", recip.Address)
|
||||
return
|
||||
}
|
||||
|
||||
// Ignore ShouldAccept if extensions explicitly allowed this Recipient.
|
||||
if extAction == event.ActionDefer && !recip.ShouldAccept() {
|
||||
s.logger.Warn().Str("to", addr).Msg("Rejecting recipient domain")
|
||||
s.send("550 Relay not permitted")
|
||||
return
|
||||
@@ -687,3 +707,18 @@ func (s *Session) ooSeq(cmd string) {
|
||||
s.send(fmt.Sprintf("503 Command %v is out of sequence", cmd))
|
||||
s.logger.Warn().Msgf("Wasn't expecting %v here", cmd)
|
||||
}
|
||||
|
||||
// extSession builds an SMTPSession for extensions.
|
||||
func (s *Session) extSession() *event.SMTPSession {
|
||||
from := s.from.Address
|
||||
to := make([]*mail.Address, 0, len(s.recipients))
|
||||
for _, recip := range s.recipients {
|
||||
addr := recip.Address
|
||||
to = append(to, &addr)
|
||||
}
|
||||
|
||||
return &event.SMTPSession{
|
||||
From: &from,
|
||||
To: to,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/inbucket/inbucket/v3/pkg/test"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type scriptStep struct {
|
||||
@@ -457,7 +458,7 @@ func TestBeforeMailAcceptedEventResponse(t *testing.T) {
|
||||
// Play and verify SMTP session.
|
||||
script := []scriptStep{
|
||||
{"HELO localhost", 250},
|
||||
tc.script,
|
||||
tc.script, // error code is the significant part.
|
||||
{"QUIT", 221}}
|
||||
playSession(t, server, script)
|
||||
|
||||
@@ -466,6 +467,175 @@ func TestBeforeMailAcceptedEventResponse(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Tests "RCPT TO" emits BeforeRcptToAccepted event.
|
||||
func TestBeforeRcptToAcceptedSingleEventEmitted(t *testing.T) {
|
||||
ds := test.NewStore()
|
||||
extHost := extension.NewHost()
|
||||
server := setupSMTPServer(ds, extHost)
|
||||
|
||||
var got *event.SMTPSession
|
||||
extHost.Events.BeforeRcptToAccepted.AddListener(
|
||||
"test",
|
||||
func(session event.SMTPSession) *event.SMTPResponse {
|
||||
got = &session
|
||||
return &event.SMTPResponse{Action: event.ActionDefer}
|
||||
})
|
||||
|
||||
// Play and verify SMTP session.
|
||||
script := []scriptStep{
|
||||
{"HELO localhost", 250},
|
||||
{"MAIL FROM:<john@gmail.com>", 250},
|
||||
{"RCPT TO:<user@gmail.com>", 250},
|
||||
{"QUIT", 221}}
|
||||
playSession(t, server, script)
|
||||
|
||||
require.NotNil(t, got, "BeforeRcptToListener did not receive SMTPSession")
|
||||
require.NotNil(t, got.From)
|
||||
require.NotNil(t, got.To)
|
||||
assert.Equal(t, "john@gmail.com", got.From.Address)
|
||||
assert.Len(t, got.To, 1)
|
||||
assert.Equal(t, "user@gmail.com", got.To[0].Address)
|
||||
}
|
||||
|
||||
// Tests "RCPT TO" emits many BeforeRcptToAccepted events.
|
||||
func TestBeforeRcptToAcceptedManyEventsEmitted(t *testing.T) {
|
||||
ds := test.NewStore()
|
||||
extHost := extension.NewHost()
|
||||
server := setupSMTPServer(ds, extHost)
|
||||
|
||||
var called int
|
||||
var got *event.SMTPSession
|
||||
extHost.Events.BeforeRcptToAccepted.AddListener(
|
||||
"test",
|
||||
func(session event.SMTPSession) *event.SMTPResponse {
|
||||
called++
|
||||
got = &session
|
||||
return &event.SMTPResponse{Action: event.ActionDefer}
|
||||
})
|
||||
|
||||
// Play and verify SMTP session.
|
||||
script := []scriptStep{
|
||||
{"HELO localhost", 250},
|
||||
{"MAIL FROM:<john@gmail.com>", 250},
|
||||
{"RCPT TO:<user@gmail.com>", 250},
|
||||
{"RCPT TO:<user2@gmail.com>", 250},
|
||||
{"QUIT", 221}}
|
||||
playSession(t, server, script)
|
||||
|
||||
require.Equal(t, 2, called, "2 events should have been emitted")
|
||||
require.NotNil(t, got, "BeforeRcptToListener did not receive SMTPSession")
|
||||
require.NotNil(t, got.From)
|
||||
require.NotNil(t, got.To)
|
||||
assert.Equal(t, "john@gmail.com", got.From.Address)
|
||||
assert.Len(t, got.To, 2)
|
||||
assert.Equal(t, "user@gmail.com", got.To[0].Address)
|
||||
assert.Equal(t, "user2@gmail.com", got.To[1].Address)
|
||||
}
|
||||
|
||||
// Tests we can continue after denying a "RCPT TO".
|
||||
func TestBeforeRcptToAcceptedEventDeny(t *testing.T) {
|
||||
ds := test.NewStore()
|
||||
extHost := extension.NewHost()
|
||||
server := setupSMTPServer(ds, extHost)
|
||||
|
||||
var called int
|
||||
var got *event.SMTPSession
|
||||
extHost.Events.BeforeRcptToAccepted.AddListener(
|
||||
"test",
|
||||
func(session event.SMTPSession) *event.SMTPResponse {
|
||||
called++
|
||||
|
||||
// Reject bad address.
|
||||
action := event.ActionDefer
|
||||
for _, to := range session.To {
|
||||
if to.Address == "bad@apple.com" {
|
||||
action = event.ActionDeny
|
||||
}
|
||||
}
|
||||
|
||||
got = &session
|
||||
return &event.SMTPResponse{Action: action, ErrorCode: 550, ErrorMsg: "rotten"}
|
||||
})
|
||||
|
||||
// Play and verify SMTP session.
|
||||
script := []scriptStep{
|
||||
{"HELO localhost", 250},
|
||||
{"MAIL FROM:<john@gmail.com>", 250},
|
||||
{"RCPT TO:<user@gmail.com>", 250},
|
||||
{"RCPT TO:<bad@apple.com>", 550},
|
||||
{"RCPT TO:<user2@gmail.com>", 250},
|
||||
{"QUIT", 221}}
|
||||
playSession(t, server, script)
|
||||
|
||||
require.Equal(t, 3, called, "3 events should have been emitted")
|
||||
require.NotNil(t, got, "BeforeRcptToListener did not receive SMTPSession")
|
||||
require.NotNil(t, got.From)
|
||||
require.NotNil(t, got.To)
|
||||
assert.Equal(t, "john@gmail.com", got.From.Address)
|
||||
|
||||
// Verify bad apple dropped from final event.
|
||||
assert.Len(t, got.To, 2)
|
||||
assert.Equal(t, "user@gmail.com", got.To[0].Address)
|
||||
assert.Equal(t, "user2@gmail.com", got.To[1].Address)
|
||||
}
|
||||
|
||||
// Test "RCPT TO" acts on BeforeRcptToAccepted event result.
|
||||
func TestBeforeRcptToAcceptedEventResponse(t *testing.T) {
|
||||
ds := test.NewStore()
|
||||
extHost := extension.NewHost()
|
||||
server := setupSMTPServer(ds, extHost)
|
||||
|
||||
var shouldReturn *event.SMTPResponse
|
||||
var gotEvent *event.SMTPSession
|
||||
extHost.Events.BeforeRcptToAccepted.AddListener(
|
||||
"test",
|
||||
func(session event.SMTPSession) *event.SMTPResponse {
|
||||
gotEvent = &session
|
||||
return shouldReturn
|
||||
})
|
||||
|
||||
tcs := map[string]struct {
|
||||
script scriptStep // Command to send and SMTP code expected.
|
||||
eventRes event.SMTPResponse // Response to send from event listener.
|
||||
}{
|
||||
"allow": {
|
||||
script: scriptStep{"RCPT TO:<john@gmail.com>", 250},
|
||||
eventRes: event.SMTPResponse{Action: event.ActionAllow},
|
||||
},
|
||||
"deny": {
|
||||
script: scriptStep{"RCPT TO:<john@gmail.com>", 550},
|
||||
eventRes: event.SMTPResponse{
|
||||
Action: event.ActionDeny,
|
||||
ErrorCode: 550,
|
||||
ErrorMsg: "meh",
|
||||
},
|
||||
},
|
||||
"defer": {
|
||||
script: scriptStep{"RCPT TO:<john@gmail.com>", 250},
|
||||
eventRes: event.SMTPResponse{Action: event.ActionDefer},
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range tcs {
|
||||
tc := tc
|
||||
t.Run(name, func(t *testing.T) {
|
||||
// Reset event listener.
|
||||
shouldReturn = &tc.eventRes
|
||||
gotEvent = nil
|
||||
|
||||
// Play and verify SMTP session.
|
||||
script := []scriptStep{
|
||||
{"HELO localhost", 250},
|
||||
{"MAIL FROM:<user@gmail.com>", 250},
|
||||
tc.script, // error code is the significant part.
|
||||
{"QUIT", 221}}
|
||||
playSession(t, server, script)
|
||||
|
||||
assert.NotNil(t, gotEvent, "BeforeRcptToListener did not receive SMTPSession")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// playSession creates a new session, reads the greeting and then plays the script
|
||||
func playSession(t *testing.T, server *Server, script []scriptStep) {
|
||||
t.Helper()
|
||||
|
||||
Reference in New Issue
Block a user