3// todo: set up a test for dane, mta-sts, etc.
18 "github.com/mjl-/mox/dns"
19 "github.com/mjl-/mox/imapclient"
20 "github.com/mjl-/mox/mlog"
21 "github.com/mjl-/mox/mox-"
22 "github.com/mjl-/mox/sasl"
23 "github.com/mjl-/mox/smtpclient"
26func tcheck(t *testing.T, err error, errmsg string) {
29 t.Fatalf("%s: %s", errmsg, err)
33func TestDeliver(t *testing.T) {
34 log := mlog.New("integration", nil)
37 hostname, err := os.Hostname()
38 tcheck(t, err, "hostname")
39 ourHostname, err := dns.ParseDomain(hostname)
40 tcheck(t, err, "parse hostname")
42 // Single update from IMAP IDLE.
43 type idleResponse struct {
44 untagged imapclient.Untagged
48 // Deliver submits a message over submissions, and checks with imap idle if the
49 // message is received by the destination mail server.
50 deliver := func(checkTime bool, dialtls bool, imaphost, imapuser, imappassword string, send func()) {
53 // Connect to IMAP, execute IDLE command, which will return on deliver message.
54 // TLS certificates work because the container has the CA certificates configured.
58 imapconn, err = tls.Dial("tcp", imaphost, nil)
60 imapconn, err = net.Dial("tcp", imaphost)
62 tcheck(t, err, "dial imap")
63 defer imapconn.Close()
65 imapc, err := imapclient.New(imapconn, false)
66 tcheck(t, err, "new imapclient")
68 _, _, err = imapc.Login(imapuser, imappassword)
69 tcheck(t, err, "imap login")
71 _, _, err = imapc.Select("Inbox")
72 tcheck(t, err, "imap select inbox")
74 err = imapc.Commandf("", "idle")
75 tcheck(t, err, "write imap idle command")
77 _, _, _, err = imapc.ReadContinuation()
78 tcheck(t, err, "read imap continuation")
80 idle := make(chan idleResponse)
83 untagged, err := imapc.ReadUntagged()
84 idle <- idleResponse{untagged, err}
91 err := imapc.Writelinef("done")
92 tcheck(t, err, "aborting idle")
98 // Wait for notification of delivery.
101 tcheck(t, resp.err, "idle notification")
102 _, ok := resp.untagged.(imapclient.UntaggedExists)
104 t.Fatalf("got idle %#v, expected untagged exists", resp.untagged)
106 if d := time.Since(t0); checkTime && d < 1*time.Second {
107 t.Fatalf("delivery took %v, but should have taken at least 1 second, the first-time sender delay", d)
109 case <-time.After(30 * time.Second):
110 t.Fatalf("timeout after 5s waiting for IMAP IDLE notification of new message, should take about 1 second")
114 submit := func(dialtls bool, mailfrom, password, desthost, rcptto string) {
118 conn, err = tls.Dial("tcp", desthost, nil)
120 conn, err = net.Dial("tcp", desthost)
122 tcheck(t, err, "dial submission")
125 msg := fmt.Sprintf(`From: <%s>
131 msg = strings.ReplaceAll(msg, "\n", "\r\n")
132 auth := func(mechanisms []string, cs *tls.ConnectionState) (sasl.Client, error) {
133 return sasl.NewClientPlain(mailfrom, password), nil
135 c, err := smtpclient.New(mox.Context, log.Logger, conn, smtpclient.TLSSkip, false, ourHostname, dns.Domain{ASCII: desthost}, smtpclient.Opts{Auth: auth})
136 tcheck(t, err, "smtp hello")
137 err = c.Deliver(mox.Context, mailfrom, rcptto, int64(len(msg)), strings.NewReader(msg), false, false, false)
138 tcheck(t, err, "deliver with smtp")
140 tcheck(t, err, "close smtpclient")
143 // Make sure moxacmepebble has a TLS certificate.
144 conn, err := tls.Dial("tcp", "moxacmepebble.mox1.example:465", nil)
145 tcheck(t, err, "dial submission")
148 log.Print("submitting email to moxacmepebble, waiting for imap notification at moxmail2")
150 deliver(true, true, "moxmail2.mox2.example:993", "moxtest2@mox2.example", "accountpass4321", func() {
151 submit(true, "moxtest1@mox1.example", "accountpass1234", "moxacmepebble.mox1.example:465", "moxtest2@mox2.example")
153 log.Print("success", slog.Duration("duration", time.Since(t0)))
155 log.Print("submitting email to moxmail2, waiting for imap notification at moxacmepebble")
157 deliver(true, true, "moxacmepebble.mox1.example:993", "moxtest1@mox1.example", "accountpass1234", func() {
158 submit(true, "moxtest2@mox2.example", "accountpass4321", "moxmail2.mox2.example:465", "moxtest1@mox1.example")
160 log.Print("success", slog.Duration("duration", time.Since(t0)))
162 log.Print("submitting email to postfix, waiting for imap notification at moxacmepebble")
164 deliver(false, true, "moxacmepebble.mox1.example:993", "moxtest1@mox1.example", "accountpass1234", func() {
165 submit(true, "moxtest1@mox1.example", "accountpass1234", "moxacmepebble.mox1.example:465", "root@postfix.example")
167 log.Print("success", slog.Duration("duration", time.Since(t0)))
169 log.Print("submitting email to localserve")
171 deliver(false, false, "localserve.mox1.example:1143", "mox@localhost", "moxmoxmox", func() {
172 submit(false, "mox@localhost", "moxmoxmox", "localserve.mox1.example:1587", "moxtest1@mox1.example")
174 log.Print("success", slog.Duration("duration", time.Since(t0)))
176 log.Print("submitting email to localserve")
178 deliver(false, false, "localserve.mox1.example:1143", "mox@localhost", "moxmoxmox", func() {
179 cmd := exec.Command("go", "run", ".", "sendmail", "mox@localhost")
180 const msg = `Subject: test
184 cmd.Stdin = strings.NewReader(msg)
185 var out strings.Builder
188 log.Print("sendmail", slog.String("output", out.String()))
189 tcheck(t, err, "sendmail")
191 log.Print("success", slog.Any("duration", time.Since(t0)))