]>
src.bluestatic.org Git - mailpopbox.git/blob - smtp/conn_test.go
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
23 func _fl(depth
int) string {
24 _
, file
, line
, _
:= runtime
.Caller(depth
+ 1)
25 return fmt
.Sprintf("[%s:%d]", filepath
.Base(file
), line
)
28 func ok(t testing
.TB
, err error
) {
30 t
.Errorf("%s unexpected error: %v", _fl(1), err
)
34 func readCodeLine(t testing
.TB
, conn
*textproto
.Conn
, code
int) string {
35 _
, message
, err
:= conn
.ReadCodeLine(code
)
37 t
.Errorf("%s ReadCodeLine error: %v", _fl(1), err
)
42 // runServer creates a TCP socket, runs a listening server, and returns the connection.
43 // The server exits when the Conn is closed.
44 func runServer(t
*testing
.T
, server Server
) net
.Listener
{
45 l
, err
:= net
.Listen("tcp", "localhost:0")
53 conn
, err
:= l
.Accept()
57 go AcceptConnection(conn
, server
, zap
.NewNop())
64 type testServer
struct {
69 func (s
*testServer
) Name() string {
73 func (s
*testServer
) VerifyAddress(addr mail
.Address
) ReplyLine
{
74 for _
, block
:= range s
.blockList
{
75 if strings
.ToLower(block
) == addr
.Address
{
76 return ReplyBadMailbox
82 func createClient(t
*testing
.T
, addr net
.Addr
) *textproto
.Conn
{
83 conn
, err
:= textproto
.Dial(addr
.Network(), addr
.String())
91 type requestResponse
struct {
94 handler
func(testing
.TB
, *textproto
.Conn
)
97 func runTableTest(t testing
.TB
, conn
*textproto
.Conn
, seq
[]requestResponse
) {
98 for i
, rr
:= range seq
{
99 t
.Logf("%s case %d", _fl(1), i
)
100 ok(t
, conn
.PrintfLine(rr
.request
))
101 if rr
.handler
!= nil {
104 readCodeLine(t
, conn
, rr
.responseCode
)
110 func TestScenarioTypical(t
*testing
.T
) {
112 blockList
: []string{"Green@foo.com"},
114 l
:= runServer(t
, &s
)
117 conn
:= createClient(t
, l
.Addr())
119 message
:= readCodeLine(t
, conn
, 220)
120 if !strings
.HasPrefix(message
, s
.Name()) {
121 t
.Errorf("Greeting does not have server name, got %q", message
)
124 greet
:= "greeting.TestScenarioTypical"
125 ok(t
, conn
.PrintfLine("EHLO "+greet
))
127 _
, message
, err
:= conn
.ReadResponse(250)
129 if !strings
.Contains(message
, greet
) {
130 t
.Errorf("EHLO response does not contain greeting, got %q", message
)
133 ok(t
, conn
.PrintfLine("MAIL FROM:<Smith@bar.com>"))
134 readCodeLine(t
, conn
, 250)
136 ok(t
, conn
.PrintfLine("RCPT TO:<Jones@foo.com>"))
137 readCodeLine(t
, conn
, 250)
139 ok(t
, conn
.PrintfLine("RCPT TO:<Green@foo.com>"))
140 readCodeLine(t
, conn
, 550)
142 ok(t
, conn
.PrintfLine("RCPT TO:<Brown@foo.com>"))
143 readCodeLine(t
, conn
, 250)
145 ok(t
, conn
.PrintfLine("DATA"))
146 readCodeLine(t
, conn
, 354)
148 ok(t
, conn
.PrintfLine("Blah blah blah..."))
149 ok(t
, conn
.PrintfLine("...etc. etc. etc."))
150 ok(t
, conn
.PrintfLine("."))
151 readCodeLine(t
, conn
, 250)
153 ok(t
, conn
.PrintfLine("QUIT"))
154 readCodeLine(t
, conn
, 221)
157 func TestVerifyAddress(t
*testing
.T
) {
159 blockList
: []string{"banned@test.mail"},
161 l
:= runServer(t
, &s
)
164 conn
:= createClient(t
, l
.Addr())
165 readCodeLine(t
, conn
, 220)
167 runTableTest(t
, conn
, []requestResponse
{
168 {"EHLO test", 0, func(t testing
.TB
, conn
*textproto
.Conn
) { conn
.ReadResponse(250) }},
169 {"VRFY banned@test.mail", 252, nil},
170 {"VRFY allowed@test.mail", 252, nil},
171 {"MAIL FROM:<sender@example.com>", 250, nil},
172 {"RCPT TO:<banned@test.mail>", 550, nil},
177 func TestBadAddress(t
*testing
.T
) {
178 l
:= runServer(t
, &testServer
{})
181 conn
:= createClient(t
, l
.Addr())
182 readCodeLine(t
, conn
, 220)
184 runTableTest(t
, conn
, []requestResponse
{
185 {"EHLO test", 0, func(t testing
.TB
, conn
*textproto
.Conn
) { conn
.ReadResponse(250) }},
186 {"MAIL FROM:<sender>", 501, nil},
187 {"MAIL FROM:<sender@foo.com> SIZE=2163", 250, nil},
188 {"RCPT TO:<banned.net>", 501, nil},
193 func TestCaseSensitivty(t
*testing
.T
) {
195 s
.blockList
= []string{"reject@mail.com"}
199 conn
:= createClient(t
, l
.Addr())
200 readCodeLine(t
, conn
, 220)
202 runTableTest(t
, conn
, []requestResponse
{
204 {"ehLO test.TEST", 0, func(t testing
.TB
, conn
*textproto
.Conn
) { conn
.ReadResponse(250) }},
205 {"mail FROM:<sender@example.com>", 250, nil},
206 {"RcPT tO:<receive@mail.com>", 250, nil},
207 {"RCPT TO:<reject@MAIL.com>", 550, nil},
208 {"RCPT TO:<reject@mail.com>", 550, nil},
209 {"DATa", 0, func(t testing
.TB
, conn
*textproto
.Conn
) {
210 readCodeLine(t
, conn
, 354)
212 ok(t
, conn
.PrintfLine("."))
213 readCodeLine(t
, conn
, 250)
215 {"MAIL FR:", 501, nil},
220 func TestGetReceivedInfo(t
*testing
.T
) {
222 server
: &testServer
{},
223 remoteAddr
: &net
.IPAddr
{net
.IPv4(127, 0, 0, 1), ""},
229 const line1
= "Received: from remote.test. (localhost [127.0.0.1])" + crlf
230 const line2
= "by Test-Server (mailpopbox) with "
231 const msgId
= "abcdef.hijk"
232 lineLast
:= now
.Format(time
.RFC1123Z
) + crlf
246 {params
{"remote.test.", true, false, "foo@bar.com"},
248 line2
+ "ESMTP id " + msgId
+ crlf
,
249 "for <foo@bar.com>" + crlf
,
250 "(using PLAINTEXT);" + crlf
,
254 for _
, test
:= range tests
{
255 t
.Logf("%#v", test
.params
)
257 conn
.ehlo
= test
.params
.ehlo
258 conn
.esmtp
= test
.params
.esmtp
259 //conn.tls = test.params.tls
261 envelope
:= Envelope
{
262 RcptTo
: []mail
.Address
{{"", test
.params
.address
}},
267 actual
:= conn
.getReceivedInfo(envelope
)
268 actualLines
:= strings
.SplitAfter(string(actual
), crlf
)
270 if len(actualLines
) != len(test
.expect
) {
271 t
.Errorf("wrong numbber of lines, expected %d, got %d", len(test
.expect
), len(actualLines
))
275 for i
, line
:= range actualLines
{
276 expect
:= test
.expect
[i
]
277 if expect
!= strings
.TrimLeft(line
, " ") {
278 t
.Errorf("Expected equal string %q, got %q", expect
, line
)