1package smtp_test
2
3import (
4	"bufio"
5	"errors"
6	"io"
7	"strings"
8	"testing"
9
10	"github.com/emersion/go-smtp"
11)
12
13func sendDeliveryCmdsLMTP(t *testing.T, scanner *bufio.Scanner, c io.Writer) {
14	sendLHLO(t, scanner, c)
15
16	io.WriteString(c, "MAIL FROM:<root@nsa.gov>\r\n")
17	scanner.Scan()
18	io.WriteString(c, "RCPT TO:<root@gchq.gov.uk>\r\n")
19	scanner.Scan()
20	io.WriteString(c, "RCPT TO:<root@bnd.bund.de>\r\n")
21	scanner.Scan()
22	io.WriteString(c, "DATA\r\n")
23	scanner.Scan()
24	io.WriteString(c, "Hey <3\r\n")
25	io.WriteString(c, ".\r\n")
26}
27
28func sendLHLO(t *testing.T, scanner *bufio.Scanner, c io.Writer) {
29	io.WriteString(c, "LHLO localhost\r\n")
30	scanner.Scan()
31	if scanner.Text() != "250-Hello localhost" {
32		t.Fatal("Invalid LHLO response:", scanner.Text())
33	}
34	for scanner.Scan() {
35		s := scanner.Text()
36
37		if strings.HasPrefix(s, "250 ") {
38			break
39		} else if !strings.HasPrefix(s, "250-") {
40			t.Fatal("Invalid capability response:", s)
41		}
42	}
43}
44
45func TestServer_LMTP(t *testing.T) {
46	be, s, c, scanner := testServerGreeted(t, func(s *smtp.Server) {
47		s.LMTP = true
48		be := s.Backend.(*backend)
49		be.implementLMTPData = true
50		be.lmtpStatus = []struct {
51			addr string
52			err  error
53		}{
54			{"root@gchq.gov.uk", errors.New("nah")},
55			{"root@bnd.bund.de", nil},
56		}
57	})
58	defer s.Close()
59	defer c.Close()
60
61	sendDeliveryCmdsLMTP(t, scanner, c)
62
63	scanner.Scan()
64	if !strings.HasPrefix(scanner.Text(), "554 5.0.0 <root@gchq.gov.uk>") {
65		t.Fatal("Invalid DATA first response:", scanner.Text())
66	}
67	scanner.Scan()
68	if !strings.HasPrefix(scanner.Text(), "250 ") {
69		t.Fatal("Invalid DATA second response:", scanner.Text())
70	}
71
72	if len(be.messages) != 0 || len(be.anonmsgs) != 1 {
73		t.Fatal("Invalid number of sent messages:", be.messages, be.anonmsgs)
74	}
75}
76
77func TestServer_LMTP_Early(t *testing.T) {
78	// This test confirms responses are sent as early as possible
79	// e.g. right after SetStatus is called.
80
81	lmtpStatusSync := make(chan struct{})
82
83	be, s, c, scanner := testServerGreeted(t, func(s *smtp.Server) {
84		s.LMTP = true
85		be := s.Backend.(*backend)
86		be.implementLMTPData = true
87		be.lmtpStatusSync = lmtpStatusSync
88		be.lmtpStatus = []struct {
89			addr string
90			err  error
91		}{
92			{"root@gchq.gov.uk", errors.New("nah")},
93			{"root@bnd.bund.de", nil},
94		}
95	})
96	defer s.Close()
97	defer c.Close()
98
99	sendDeliveryCmdsLMTP(t, scanner, c)
100
101	// Test backend sends to sync channel after calling SetStatus.
102
103	scanner.Scan()
104	if !strings.HasPrefix(scanner.Text(), "554 5.0.0 <root@gchq.gov.uk>") {
105		t.Fatal("Invalid DATA first response:", scanner.Text())
106	}
107
108	<-be.lmtpStatusSync
109
110	scanner.Scan()
111	if !strings.HasPrefix(scanner.Text(), "250 ") {
112		t.Fatal("Invalid DATA second response:", scanner.Text())
113	}
114
115	<-be.lmtpStatusSync
116
117	if len(be.messages) != 0 || len(be.anonmsgs) != 1 {
118		t.Fatal("Invalid number of sent messages:", be.messages, be.anonmsgs)
119	}
120}
121
122func TestServer_LMTP_Expand(t *testing.T) {
123	// This checks whether handleDataLMTP
124	// correctly expands results if backend doesn't
125	// implement LMTPSession.
126
127	be, s, c, scanner := testServerGreeted(t, func(s *smtp.Server) {
128		s.LMTP = true
129	})
130	defer s.Close()
131	defer c.Close()
132
133	sendDeliveryCmdsLMTP(t, scanner, c)
134
135	scanner.Scan()
136	if !strings.HasPrefix(scanner.Text(), "250 ") {
137		t.Fatal("Invalid DATA first response:", scanner.Text())
138	}
139	scanner.Scan()
140	if !strings.HasPrefix(scanner.Text(), "250 ") {
141		t.Fatal("Invalid DATA second response:", scanner.Text())
142	}
143
144	if len(be.messages) != 0 || len(be.anonmsgs) != 1 {
145		t.Fatal("Invalid number of sent messages:", be.messages, be.anonmsgs)
146	}
147}
148
149func TestServer_LMTP_DuplicatedRcpt(t *testing.T) {
150	be, s, c, scanner := testServerGreeted(t, func(s *smtp.Server) {
151		s.LMTP = true
152		be := s.Backend.(*backend)
153		be.implementLMTPData = true
154		be.lmtpStatus = []struct {
155			addr string
156			err  error
157		}{
158			{"root@gchq.gov.uk", &smtp.SMTPError{Code: 555}},
159			{"root@bnd.bund.de", nil},
160			{"root@gchq.gov.uk", &smtp.SMTPError{Code: 556}},
161		}
162	})
163	defer s.Close()
164	defer c.Close()
165
166	sendLHLO(t, scanner, c)
167
168	io.WriteString(c, "MAIL FROM:<root@nsa.gov>\r\n")
169	scanner.Scan()
170	io.WriteString(c, "RCPT TO:<root@gchq.gov.uk>\r\n")
171	scanner.Scan()
172	io.WriteString(c, "RCPT TO:<root@bnd.bund.de>\r\n")
173	scanner.Scan()
174	io.WriteString(c, "RCPT TO:<root@gchq.gov.uk>\r\n")
175	scanner.Scan()
176	io.WriteString(c, "DATA\r\n")
177	scanner.Scan()
178	io.WriteString(c, "Hey <3\r\n")
179	io.WriteString(c, ".\r\n")
180
181	scanner.Scan()
182	if !strings.HasPrefix(scanner.Text(), "555 5.0.0 <root@gchq.gov.uk>") {
183		t.Fatal("Invalid DATA first response:", scanner.Text())
184	}
185	scanner.Scan()
186	if !strings.HasPrefix(scanner.Text(), "250 ") {
187		t.Fatal("Invalid DATA second response:", scanner.Text())
188	}
189	scanner.Scan()
190	if !strings.HasPrefix(scanner.Text(), "556 5.0.0 <root@gchq.gov.uk>") {
191		t.Fatal("Invalid DATA first response:", scanner.Text())
192	}
193
194	if len(be.messages) != 0 || len(be.anonmsgs) != 1 {
195		t.Fatal("Invalid number of sent messages:", be.messages, be.anonmsgs)
196	}
197}
198