Merge branch 'main' into user-account
This commit is contained in:
commit
0df3e3e4f5
9 changed files with 1059 additions and 12764 deletions
|
@ -37,13 +37,12 @@ func logvm(v *visitor, m *message) *log.Event {
|
|||
}
|
||||
|
||||
// logem creates a new log event with email fields
|
||||
func logem(state *smtp.ConnectionState) *log.Event {
|
||||
return log.
|
||||
Tag(tagSMTP).
|
||||
Fields(log.Context{
|
||||
"smtp_hostname": state.Hostname,
|
||||
"smtp_remote_addr": state.RemoteAddr.String(),
|
||||
})
|
||||
func logem(smtpConn *smtp.Conn) *log.Event {
|
||||
ev := log.Tag(tagSMTP).Field("smtp_hostname", smtpConn.Hostname())
|
||||
if smtpConn.Conn() != nil {
|
||||
ev.Field("smtp_remote_addr", smtpConn.Conn().RemoteAddr().String())
|
||||
}
|
||||
return ev
|
||||
}
|
||||
|
||||
func httpContext(r *http.Request) log.Context {
|
||||
|
|
|
@ -33,6 +33,9 @@ type smtpBackend struct {
|
|||
mu sync.Mutex
|
||||
}
|
||||
|
||||
var _ smtp.Backend = (*smtpBackend)(nil)
|
||||
var _ smtp.Session = (*smtpSession)(nil)
|
||||
|
||||
func newMailBackend(conf *Config, handler func(http.ResponseWriter, *http.Request)) *smtpBackend {
|
||||
return &smtpBackend{
|
||||
config: conf,
|
||||
|
@ -40,14 +43,9 @@ func newMailBackend(conf *Config, handler func(http.ResponseWriter, *http.Reques
|
|||
}
|
||||
}
|
||||
|
||||
func (b *smtpBackend) Login(state *smtp.ConnectionState, username, _ string) (smtp.Session, error) {
|
||||
logem(state).Debug("Incoming mail, login with user %s", username)
|
||||
return &smtpSession{backend: b, state: state}, nil
|
||||
}
|
||||
|
||||
func (b *smtpBackend) AnonymousLogin(state *smtp.ConnectionState) (smtp.Session, error) {
|
||||
logem(state).Debug("Incoming mail, anonymous login")
|
||||
return &smtpSession{backend: b, state: state}, nil
|
||||
func (b *smtpBackend) NewSession(conn *smtp.Conn) (smtp.Session, error) {
|
||||
logem(conn).Debug("Incoming mail")
|
||||
return &smtpSession{backend: b, conn: conn}, nil
|
||||
}
|
||||
|
||||
func (b *smtpBackend) Counts() (total int64, success int64, failure int64) {
|
||||
|
@ -59,23 +57,23 @@ func (b *smtpBackend) Counts() (total int64, success int64, failure int64) {
|
|||
// smtpSession is returned after EHLO.
|
||||
type smtpSession struct {
|
||||
backend *smtpBackend
|
||||
state *smtp.ConnectionState
|
||||
conn *smtp.Conn
|
||||
topic string
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func (s *smtpSession) AuthPlain(username, password string) error {
|
||||
logem(s.state).Debug("AUTH PLAIN (with username %s)", username)
|
||||
func (s *smtpSession) AuthPlain(username, _ string) error {
|
||||
logem(s.conn).Field("smtp_username", username).Debug("AUTH PLAIN (with username %s)", username)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *smtpSession) Mail(from string, opts smtp.MailOptions) error {
|
||||
logem(s.state).Debug("MAIL FROM: %s (with options: %#v)", from, opts)
|
||||
func (s *smtpSession) Mail(from string, opts *smtp.MailOptions) error {
|
||||
logem(s.conn).Field("smtp_mail_from", from).Debug("MAIL FROM: %s", from)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *smtpSession) Rcpt(to string) error {
|
||||
logem(s.state).Debug("RCPT TO: %s", to)
|
||||
logem(s.conn).Field("smtp_rcpt_to", to).Debug("RCPT TO: %s", to)
|
||||
return s.withFailCount(func() error {
|
||||
conf := s.backend.config
|
||||
addressList, err := mail.ParseAddressList(to)
|
||||
|
@ -112,11 +110,11 @@ func (s *smtpSession) Data(r io.Reader) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ev := logem(s.state).Tag(tagSMTP)
|
||||
ev := logem(s.conn)
|
||||
if ev.IsTrace() {
|
||||
ev.Field("smtp_data", string(b)).Trace("DATA")
|
||||
} else if ev.IsDebug() {
|
||||
ev.Debug("DATA: %d byte(s)", len(b))
|
||||
ev.Field("smtp_data_len", len(b)).Debug("DATA")
|
||||
}
|
||||
msg, err := mail.ReadMessage(bytes.NewReader(b))
|
||||
if err != nil {
|
||||
|
@ -156,9 +154,9 @@ func (s *smtpSession) Data(r io.Reader) error {
|
|||
|
||||
func (s *smtpSession) publishMessage(m *message) error {
|
||||
// Extract remote address (for rate limiting)
|
||||
remoteAddr, _, err := net.SplitHostPort(s.state.RemoteAddr.String())
|
||||
remoteAddr, _, err := net.SplitHostPort(s.conn.Conn().RemoteAddr().String())
|
||||
if err != nil {
|
||||
remoteAddr = s.state.RemoteAddr.String()
|
||||
remoteAddr = s.conn.Conn().RemoteAddr().String()
|
||||
}
|
||||
|
||||
// Call HTTP handler with fake HTTP request
|
||||
|
@ -198,7 +196,7 @@ func (s *smtpSession) withFailCount(fn func() error) error {
|
|||
if err != nil {
|
||||
// Almost all of these errors are parse errors, and user input errors.
|
||||
// We do not want to spam the log with WARN messages.
|
||||
logem(s.state).Err(err).Debug("Incoming mail error")
|
||||
logem(s.conn).Err(err).Debug("Incoming mail error")
|
||||
s.backend.failure++
|
||||
}
|
||||
return err
|
||||
|
|
|
@ -1,16 +1,23 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"github.com/emersion/go-smtp"
|
||||
"github.com/stretchr/testify/require"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestSmtpBackend_Multipart(t *testing.T) {
|
||||
email := `MIME-Version: 1.0
|
||||
email := `EHLO example.com
|
||||
MAIL FROM: phil@example.com
|
||||
RCPT TO: ntfy-mytopic@ntfy.sh
|
||||
DATA
|
||||
MIME-Version: 1.0
|
||||
Date: Tue, 28 Dec 2021 00:30:10 +0100
|
||||
Message-ID: <CAAvm79YP0C=Rt1N=KWmSUBB87KK2rRChmdzKqF1vCwMEUiVzLQ@mail.gmail.com>
|
||||
Subject: and one more
|
||||
|
@ -28,20 +35,25 @@ Content-Type: text/html; charset="UTF-8"
|
|||
|
||||
<div dir="ltr">what's up<br clear="all"><div><br></div></div>
|
||||
|
||||
--000000000000f3320b05d42915c9--`
|
||||
_, backend := newTestBackend(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
--000000000000f3320b05d42915c9--
|
||||
.
|
||||
`
|
||||
s, c, _, scanner := newTestSMTPServer(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
require.Equal(t, "/mytopic", r.URL.Path)
|
||||
require.Equal(t, "and one more", r.Header.Get("Title"))
|
||||
require.Equal(t, "what's up", readAll(t, r.Body))
|
||||
})
|
||||
session, _ := backend.AnonymousLogin(fakeConnState(t, "1.2.3.4"))
|
||||
require.Nil(t, session.Mail("phil@example.com", smtp.MailOptions{}))
|
||||
require.Nil(t, session.Rcpt("ntfy-mytopic@ntfy.sh"))
|
||||
require.Nil(t, session.Data(strings.NewReader(email)))
|
||||
defer s.Close()
|
||||
defer c.Close()
|
||||
writeAndReadUntilLine(t, email, c, scanner, "250 2.0.0 OK: queued")
|
||||
}
|
||||
|
||||
func TestSmtpBackend_MultipartNoBody(t *testing.T) {
|
||||
email := `MIME-Version: 1.0
|
||||
email := `EHLO example.com
|
||||
MAIL FROM: phil@example.com
|
||||
RCPT TO: ntfy-emailtest@ntfy.sh
|
||||
DATA
|
||||
MIME-Version: 1.0
|
||||
Date: Tue, 28 Dec 2021 01:33:34 +0100
|
||||
Message-ID: <CAAvm7ABCDsi9vsuu0WTRXzZQBC8dXrDOLT8iCWdqrsmg@mail.gmail.com>
|
||||
Subject: This email has a subject but no body
|
||||
|
@ -59,20 +71,25 @@ Content-Type: text/html; charset="UTF-8"
|
|||
|
||||
<div dir="ltr"><br></div>
|
||||
|
||||
--000000000000bcf4a405d429f8d4--`
|
||||
_, backend := newTestBackend(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
--000000000000bcf4a405d429f8d4--
|
||||
.
|
||||
`
|
||||
s, c, _, scanner := newTestSMTPServer(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
require.Equal(t, "/emailtest", r.URL.Path)
|
||||
require.Equal(t, "", r.Header.Get("Title")) // We flipped message and body
|
||||
require.Equal(t, "This email has a subject but no body", readAll(t, r.Body))
|
||||
})
|
||||
session, _ := backend.AnonymousLogin(fakeConnState(t, "1.2.3.4"))
|
||||
require.Nil(t, session.Mail("phil@example.com", smtp.MailOptions{}))
|
||||
require.Nil(t, session.Rcpt("ntfy-emailtest@ntfy.sh"))
|
||||
require.Nil(t, session.Data(strings.NewReader(email)))
|
||||
defer s.Close()
|
||||
defer c.Close()
|
||||
writeAndReadUntilLine(t, email, c, scanner, "250 2.0.0 OK: queued")
|
||||
}
|
||||
|
||||
func TestSmtpBackend_Plaintext(t *testing.T) {
|
||||
email := `Date: Tue, 28 Dec 2021 00:30:10 +0100
|
||||
email := `EHLO example.com
|
||||
MAIL FROM: phil@example.com
|
||||
RCPT TO: mytopic@ntfy.sh
|
||||
DATA
|
||||
Date: Tue, 28 Dec 2021 00:30:10 +0100
|
||||
Message-ID: <CAAvm79YP0C=Rt1N=KWmSUBB87KK2rRChmdzKqF1vCwMEUiVzLQ@mail.gmail.com>
|
||||
Subject: and one more
|
||||
From: Phil <phil@example.com>
|
||||
|
@ -80,56 +97,68 @@ To: mytopic@ntfy.sh
|
|||
Content-Type: text/plain; charset="UTF-8"
|
||||
|
||||
what's up
|
||||
.
|
||||
`
|
||||
conf, backend := newTestBackend(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
s, c, conf, scanner := newTestSMTPServer(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
require.Equal(t, "/mytopic", r.URL.Path)
|
||||
require.Equal(t, "and one more", r.Header.Get("Title"))
|
||||
require.Equal(t, "what's up", readAll(t, r.Body))
|
||||
})
|
||||
conf.SMTPServerAddrPrefix = ""
|
||||
session, _ := backend.AnonymousLogin(fakeConnState(t, "1.2.3.4"))
|
||||
require.Nil(t, session.Mail("phil@example.com", smtp.MailOptions{}))
|
||||
require.Nil(t, session.Rcpt("mytopic@ntfy.sh"))
|
||||
require.Nil(t, session.Data(strings.NewReader(email)))
|
||||
defer s.Close()
|
||||
defer c.Close()
|
||||
writeAndReadUntilLine(t, email, c, scanner, "250 2.0.0 OK: queued")
|
||||
}
|
||||
|
||||
func TestSmtpBackend_Plaintext_No_ContentType(t *testing.T) {
|
||||
email := `Subject: Very short mail
|
||||
email := `EHLO example.com
|
||||
MAIL FROM: phil@example.com
|
||||
RCPT TO: mytopic@ntfy.sh
|
||||
DATA
|
||||
Subject: Very short mail
|
||||
|
||||
what's up
|
||||
.
|
||||
`
|
||||
conf, backend := newTestBackend(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
s, c, conf, scanner := newTestSMTPServer(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
require.Equal(t, "/mytopic", r.URL.Path)
|
||||
require.Equal(t, "Very short mail", r.Header.Get("Title"))
|
||||
require.Equal(t, "what's up", readAll(t, r.Body))
|
||||
})
|
||||
conf.SMTPServerAddrPrefix = ""
|
||||
session, _ := backend.AnonymousLogin(fakeConnState(t, "1.2.3.4"))
|
||||
require.Nil(t, session.Mail("phil@example.com", smtp.MailOptions{}))
|
||||
require.Nil(t, session.Rcpt("mytopic@ntfy.sh"))
|
||||
require.Nil(t, session.Data(strings.NewReader(email)))
|
||||
defer s.Close()
|
||||
defer c.Close()
|
||||
writeAndReadUntilLine(t, email, c, scanner, "250 2.0.0 OK: queued")
|
||||
}
|
||||
|
||||
func TestSmtpBackend_Plaintext_EncodedSubject(t *testing.T) {
|
||||
email := `Date: Tue, 28 Dec 2021 00:30:10 +0100
|
||||
email := `EHLO example.com
|
||||
MAIL FROM: phil@example.com
|
||||
RCPT TO: ntfy-mytopic@ntfy.sh
|
||||
DATA
|
||||
Date: Tue, 28 Dec 2021 00:30:10 +0100
|
||||
Subject: =?UTF-8?B?VGhyZWUgc2FudGFzIPCfjoXwn46F8J+OhQ==?=
|
||||
From: Phil <phil@example.com>
|
||||
To: ntfy-mytopic@ntfy.sh
|
||||
Content-Type: text/plain; charset="UTF-8"
|
||||
|
||||
what's up
|
||||
.
|
||||
`
|
||||
_, backend := newTestBackend(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
s, c, _, scanner := newTestSMTPServer(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
require.Equal(t, "Three santas 🎅🎅🎅", r.Header.Get("Title"))
|
||||
})
|
||||
session, _ := backend.AnonymousLogin(fakeConnState(t, "1.2.3.4"))
|
||||
require.Nil(t, session.Mail("phil@example.com", smtp.MailOptions{}))
|
||||
require.Nil(t, session.Rcpt("ntfy-mytopic@ntfy.sh"))
|
||||
require.Nil(t, session.Data(strings.NewReader(email)))
|
||||
defer s.Close()
|
||||
defer c.Close()
|
||||
writeAndReadUntilLine(t, email, c, scanner, "250 2.0.0 OK: queued")
|
||||
}
|
||||
|
||||
func TestSmtpBackend_Plaintext_TooLongTruncate(t *testing.T) {
|
||||
email := `Date: Tue, 28 Dec 2021 00:30:10 +0100
|
||||
email := `EHLO example.com
|
||||
MAIL FROM: phil@example.com
|
||||
RCPT TO: mytopic@ntfy.sh
|
||||
DATA
|
||||
Date: Tue, 28 Dec 2021 00:30:10 +0100
|
||||
Message-ID: <CAAvm79YP0C=Rt1N=KWmSUBB87KK2rRChmdzKqF1vCwMEUiVzLQ@mail.gmail.com>
|
||||
Subject: and one more
|
||||
From: Phil <phil@example.com>
|
||||
|
@ -148,60 +177,61 @@ so i'm gonna fill the rest of this with AAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
and with BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
|
||||
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
|
||||
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
|
||||
that should do it
|
||||
.
|
||||
`
|
||||
conf, backend := newTestBackend(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
s, c, conf, scanner := newTestSMTPServer(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
expected := `you know this is a string.
|
||||
it's a long string.
|
||||
it's supposed to be longer than the max message length
|
||||
|
@ -214,68 +244,71 @@ so i'm gonna fill the rest of this with AAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
......................................................................
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||
and with BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
|
||||
BBBBBBBBBBBBBBBBBBBBBBBBB`
|
||||
require.Equal(t, 4096, len(expected)) // Sanity check
|
||||
require.Equal(t, expected, readAll(t, r.Body))
|
||||
})
|
||||
defer s.Close()
|
||||
defer c.Close()
|
||||
conf.SMTPServerAddrPrefix = ""
|
||||
session, _ := backend.AnonymousLogin(fakeConnState(t, "1.2.3.4"))
|
||||
require.Nil(t, session.Mail("phil@example.com", smtp.MailOptions{}))
|
||||
require.Nil(t, session.Rcpt("mytopic@ntfy.sh"))
|
||||
require.Nil(t, session.Data(strings.NewReader(email)))
|
||||
writeAndReadUntilLine(t, email, c, scanner, "250 2.0.0 OK: queued")
|
||||
}
|
||||
|
||||
func TestSmtpBackend_Unsupported(t *testing.T) {
|
||||
email := `Date: Tue, 28 Dec 2021 00:30:10 +0100
|
||||
email := `EHLO example.com
|
||||
MAIL FROM: phil@example.com
|
||||
RCPT TO: ntfy-mytopic@ntfy.sh
|
||||
DATA
|
||||
Date: Tue, 28 Dec 2021 00:30:10 +0100
|
||||
Message-ID: <CAAvm79YP0C=Rt1N=KWmSUBB87KK2rRChmdzKqF1vCwMEUiVzLQ@mail.gmail.com>
|
||||
Subject: and one more
|
||||
From: Phil <phil@example.com>
|
||||
|
@ -283,34 +316,89 @@ To: mytopic@ntfy.sh
|
|||
Content-Type: text/SOMETHINGELSE
|
||||
|
||||
what's up
|
||||
.
|
||||
`
|
||||
conf, backend := newTestBackend(t, func(http.ResponseWriter, *http.Request) {
|
||||
// Nothing.
|
||||
s, c, _, scanner := newTestSMTPServer(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatal("This should not be called")
|
||||
})
|
||||
conf.SMTPServerAddrPrefix = ""
|
||||
session, _ := backend.Login(fakeConnState(t, "1.2.3.4"), "user", "pass")
|
||||
require.Nil(t, session.Mail("phil@example.com", smtp.MailOptions{}))
|
||||
require.Nil(t, session.Rcpt("mytopic@ntfy.sh"))
|
||||
require.Equal(t, errUnsupportedContentType, session.Data(strings.NewReader(email)))
|
||||
defer s.Close()
|
||||
defer c.Close()
|
||||
writeAndReadUntilLine(t, email, c, scanner, "554 5.0.0 Error: transaction failed, blame it on the weather: unsupported content type")
|
||||
}
|
||||
|
||||
func newTestBackend(t *testing.T, handler func(http.ResponseWriter, *http.Request)) (*Config, *smtpBackend) {
|
||||
conf := newTestConfig(t)
|
||||
func TestSmtpBackend_InvalidAddress(t *testing.T) {
|
||||
email := `EHLO example.com
|
||||
MAIL FROM: phil@example.com
|
||||
RCPT TO: unsupported@ntfy.sh
|
||||
DATA
|
||||
Date: Tue, 28 Dec 2021 00:30:10 +0100
|
||||
Subject: and one more
|
||||
From: Phil <phil@example.com>
|
||||
To: mytopic@ntfy.sh
|
||||
Content-Type: text/plain
|
||||
|
||||
what's up
|
||||
.
|
||||
`
|
||||
s, c, _, scanner := newTestSMTPServer(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Fatal("This should not be called")
|
||||
})
|
||||
defer s.Close()
|
||||
defer c.Close()
|
||||
writeAndReadUntilLine(t, email, c, scanner, "451 4.0.0 invalid address")
|
||||
}
|
||||
|
||||
type smtpHandlerFunc func(http.ResponseWriter, *http.Request)
|
||||
|
||||
func newTestSMTPServer(t *testing.T, handler smtpHandlerFunc) (s *smtp.Server, c net.Conn, conf *Config, scanner *bufio.Scanner) {
|
||||
conf = newTestConfig(t)
|
||||
conf.SMTPServerListen = ":25"
|
||||
conf.SMTPServerDomain = "ntfy.sh"
|
||||
conf.SMTPServerAddrPrefix = "ntfy-"
|
||||
backend := newMailBackend(conf, handler)
|
||||
return conf, backend
|
||||
}
|
||||
|
||||
func fakeConnState(t *testing.T, remoteAddr string) *smtp.ConnectionState {
|
||||
ip, err := net.ResolveIPAddr("ip", remoteAddr)
|
||||
l, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return &smtp.ConnectionState{
|
||||
Hostname: "myhostname",
|
||||
LocalAddr: ip,
|
||||
RemoteAddr: ip,
|
||||
s = smtp.NewServer(backend)
|
||||
s.Domain = conf.SMTPServerDomain
|
||||
s.AllowInsecureAuth = true
|
||||
go func() {
|
||||
require.Nil(t, s.Serve(l))
|
||||
}()
|
||||
c, err = net.Dial("tcp", l.Addr().String())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
scanner = bufio.NewScanner(c)
|
||||
return
|
||||
}
|
||||
|
||||
func writeAndReadUntilLine(t *testing.T, email string, conn net.Conn, scanner *bufio.Scanner, expectedLine string) {
|
||||
_, err := io.WriteString(conn, email)
|
||||
require.Nil(t, err)
|
||||
readUntilLine(t, conn, scanner, expectedLine)
|
||||
}
|
||||
|
||||
func readUntilLine(t *testing.T, conn net.Conn, scanner *bufio.Scanner, expectedLine string) {
|
||||
cancelChan := make(chan bool)
|
||||
go func() {
|
||||
select {
|
||||
case <-cancelChan:
|
||||
case <-time.After(3 * time.Second):
|
||||
conn.Close()
|
||||
t.Error("Failed waiting for expected output")
|
||||
}
|
||||
}()
|
||||
var output string
|
||||
for scanner.Scan() {
|
||||
text := scanner.Text()
|
||||
if strings.TrimSpace(text) == expectedLine {
|
||||
cancelChan <- true
|
||||
return
|
||||
}
|
||||
output += text + "\n"
|
||||
//fmt.Println(text)
|
||||
}
|
||||
t.Fatalf("Expected line '%s' not found in output:\n%s", expectedLine, output)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue