mirror of
https://github.com/wneessen/go-mail.git
synced 2024-11-21 21:30:50 +01:00
Compare commits
3 commits
b7ffce62aa
...
d446b491e2
Author | SHA1 | Date | |
---|---|---|---|
d446b491e2 | |||
0d8d097ae1 | |||
0df228178a |
1 changed files with 640 additions and 58 deletions
|
@ -1867,44 +1867,612 @@ func TestClient_TLSConnectionState(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Issue 17794: don't send a trailing space on AUTH command when there's no password.
|
func TestClient_Verify(t *testing.T) {
|
||||||
func TestClient_Auth_trimSpace(t *testing.T) {
|
t.Run("Verify on existing user succeeds", func(t *testing.T) {
|
||||||
server := "220 hello world\r\n" +
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
"200 some more"
|
defer cancel()
|
||||||
var wrote strings.Builder
|
PortAdder.Add(1)
|
||||||
var fake faker
|
serverPort := int(TestServerPortBase + PortAdder.Load())
|
||||||
fake.ReadWriter = struct {
|
featureSet := "250-STARTTLS\r\n250-8BITMIME\r\n250-DSN\r\n250 SMTPUTF8"
|
||||||
io.Reader
|
go func() {
|
||||||
io.Writer
|
if err := simpleSMTPServer(ctx, t, &serverProps{
|
||||||
}{
|
FeatureSet: featureSet,
|
||||||
strings.NewReader(server),
|
ListenPort: serverPort,
|
||||||
&wrote,
|
},
|
||||||
}
|
); err != nil {
|
||||||
c, err := NewClient(fake, "fake.host")
|
t.Errorf("failed to start test server: %s", err)
|
||||||
if err != nil {
|
return
|
||||||
t.Fatalf("NewClient: %v", err)
|
}
|
||||||
}
|
}()
|
||||||
c.tls = true
|
time.Sleep(time.Millisecond * 30)
|
||||||
c.didHello = true
|
|
||||||
_ = c.Auth(toServerEmptyAuth{})
|
client, err := Dial(fmt.Sprintf("%s:%d", TestServerAddr, serverPort))
|
||||||
if err = c.Close(); err != nil {
|
if err != nil {
|
||||||
t.Errorf("close failed: %s", err)
|
t.Errorf("failed to dial to test server: %s", err)
|
||||||
}
|
}
|
||||||
if got, want := wrote.String(), "AUTH FOOAUTH\r\n*\r\nQUIT\r\n"; got != want {
|
t.Cleanup(func() {
|
||||||
t.Errorf("wrote %q; want %q", got, want)
|
if err = client.Close(); err != nil {
|
||||||
}
|
t.Errorf("failed to close client: %s", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if err = client.Verify("toni.tester@example.com"); err != nil {
|
||||||
|
t.Errorf("failed to verify user: %s", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
t.Run("Verify on non-existing user fails", func(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
PortAdder.Add(1)
|
||||||
|
serverPort := int(TestServerPortBase + PortAdder.Load())
|
||||||
|
featureSet := "250-STARTTLS\r\n250-8BITMIME\r\n250-DSN\r\n250 SMTPUTF8"
|
||||||
|
go func() {
|
||||||
|
if err := simpleSMTPServer(ctx, t, &serverProps{
|
||||||
|
FeatureSet: featureSet,
|
||||||
|
ListenPort: serverPort,
|
||||||
|
VRFYUserUnknown: true,
|
||||||
|
},
|
||||||
|
); err != nil {
|
||||||
|
t.Errorf("failed to start test server: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
time.Sleep(time.Millisecond * 30)
|
||||||
|
|
||||||
|
client, err := Dial(fmt.Sprintf("%s:%d", TestServerAddr, serverPort))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to dial to test server: %s", err)
|
||||||
|
}
|
||||||
|
t.Cleanup(func() {
|
||||||
|
if err = client.Close(); err != nil {
|
||||||
|
t.Errorf("failed to close client: %s", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if err = client.Verify("toni.tester@example.com"); err == nil {
|
||||||
|
t.Error("verify on non-existing user should fail")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
t.Run("Verify with newlines should fails", func(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
PortAdder.Add(1)
|
||||||
|
serverPort := int(TestServerPortBase + PortAdder.Load())
|
||||||
|
featureSet := "250-STARTTLS\r\n250-8BITMIME\r\n250-DSN\r\n250 SMTPUTF8"
|
||||||
|
go func() {
|
||||||
|
if err := simpleSMTPServer(ctx, t, &serverProps{
|
||||||
|
FeatureSet: featureSet,
|
||||||
|
ListenPort: serverPort,
|
||||||
|
},
|
||||||
|
); err != nil {
|
||||||
|
t.Errorf("failed to start test server: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
time.Sleep(time.Millisecond * 30)
|
||||||
|
|
||||||
|
client, err := Dial(fmt.Sprintf("%s:%d", TestServerAddr, serverPort))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to dial to test server: %s", err)
|
||||||
|
}
|
||||||
|
t.Cleanup(func() {
|
||||||
|
if err = client.Close(); err != nil {
|
||||||
|
t.Errorf("failed to close client: %s", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if err = client.Verify("toni.tester@example.com\r\n"); err == nil {
|
||||||
|
t.Error("verify with new lines should fail")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
t.Run("Verify should fail on HELO/EHLO", func(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
PortAdder.Add(1)
|
||||||
|
serverPort := int(TestServerPortBase + PortAdder.Load())
|
||||||
|
featureSet := "250-STARTTLS\r\n250-8BITMIME\r\n250-DSN\r\n250 SMTPUTF8"
|
||||||
|
go func() {
|
||||||
|
if err := simpleSMTPServer(ctx, t, &serverProps{
|
||||||
|
FailOnEhlo: true,
|
||||||
|
FailOnHelo: true,
|
||||||
|
FeatureSet: featureSet,
|
||||||
|
ListenPort: serverPort,
|
||||||
|
},
|
||||||
|
); err != nil {
|
||||||
|
t.Errorf("failed to start test server: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
time.Sleep(time.Millisecond * 30)
|
||||||
|
|
||||||
|
client, err := Dial(fmt.Sprintf("%s:%d", TestServerAddr, serverPort))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to dial to test server: %s", err)
|
||||||
|
}
|
||||||
|
t.Cleanup(func() {
|
||||||
|
if err = client.Close(); err != nil {
|
||||||
|
t.Errorf("failed to close client: %s", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if err = client.Verify("toni.tester@example.com"); err == nil {
|
||||||
|
t.Error("verify with new lines should fail")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClient_Auth(t *testing.T) {
|
||||||
|
t.Run("Auth fails on EHLO/HELO", func(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
PortAdder.Add(1)
|
||||||
|
serverPort := int(TestServerPortBase + PortAdder.Load())
|
||||||
|
featureSet := "250-STARTTLS\r\n250-8BITMIME\r\n250-DSN\r\n250 SMTPUTF8"
|
||||||
|
go func() {
|
||||||
|
if err := simpleSMTPServer(ctx, t, &serverProps{
|
||||||
|
FailOnEhlo: true,
|
||||||
|
FailOnHelo: true,
|
||||||
|
FeatureSet: featureSet,
|
||||||
|
ListenPort: serverPort,
|
||||||
|
},
|
||||||
|
); err != nil {
|
||||||
|
t.Errorf("failed to start test server: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
time.Sleep(time.Millisecond * 30)
|
||||||
|
|
||||||
|
client, err := Dial(fmt.Sprintf("%s:%d", TestServerAddr, serverPort))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to dial to test server: %s", err)
|
||||||
|
}
|
||||||
|
auth := LoginAuth("username", "password", TestServerAddr, false)
|
||||||
|
if err = client.Auth(auth); err == nil {
|
||||||
|
t.Error("auth should fail on EHLO/HELO")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
t.Run("Auth fails on auth-start", func(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
PortAdder.Add(1)
|
||||||
|
serverPort := int(TestServerPortBase + PortAdder.Load())
|
||||||
|
featureSet := "250-STARTTLS\r\n250-8BITMIME\r\n250-DSN\r\n250 SMTPUTF8"
|
||||||
|
go func() {
|
||||||
|
if err := simpleSMTPServer(ctx, t, &serverProps{
|
||||||
|
FailOnAuth: true,
|
||||||
|
FeatureSet: featureSet,
|
||||||
|
ListenPort: serverPort,
|
||||||
|
},
|
||||||
|
); err != nil {
|
||||||
|
t.Errorf("failed to start test server: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
time.Sleep(time.Millisecond * 30)
|
||||||
|
|
||||||
|
client, err := Dial(fmt.Sprintf("%s:%d", TestServerAddr, serverPort))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to dial to test server: %s", err)
|
||||||
|
}
|
||||||
|
auth := LoginAuth("username", "password", "not.localhost.com", false)
|
||||||
|
if err = client.Auth(auth); err == nil {
|
||||||
|
t.Error("auth should fail on auth-start, then on quit")
|
||||||
|
}
|
||||||
|
expErr := "wrong host name"
|
||||||
|
if !strings.EqualFold(expErr, err.Error()) {
|
||||||
|
t.Errorf("expected error: %q, got: %q", expErr, err.Error())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
t.Run("Auth fails on auth-start and then on quit", func(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
PortAdder.Add(1)
|
||||||
|
serverPort := int(TestServerPortBase + PortAdder.Load())
|
||||||
|
featureSet := "250-STARTTLS\r\n250-8BITMIME\r\n250-DSN\r\n250 SMTPUTF8"
|
||||||
|
go func() {
|
||||||
|
if err := simpleSMTPServer(ctx, t, &serverProps{
|
||||||
|
FailOnAuth: true,
|
||||||
|
FailOnQuit: true,
|
||||||
|
FeatureSet: featureSet,
|
||||||
|
ListenPort: serverPort,
|
||||||
|
},
|
||||||
|
); err != nil {
|
||||||
|
t.Errorf("failed to start test server: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
time.Sleep(time.Millisecond * 30)
|
||||||
|
|
||||||
|
client, err := Dial(fmt.Sprintf("%s:%d", TestServerAddr, serverPort))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to dial to test server: %s", err)
|
||||||
|
}
|
||||||
|
auth := LoginAuth("username", "password", "not.localhost.com", false)
|
||||||
|
if err = client.Auth(auth); err == nil {
|
||||||
|
t.Error("auth should fail on auth-start, then on quit")
|
||||||
|
}
|
||||||
|
expErr := "wrong host name, 500 5.1.2 Error: quit failed"
|
||||||
|
if !strings.EqualFold(expErr, err.Error()) {
|
||||||
|
t.Errorf("expected error: %q, got: %q", expErr, err.Error())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// Issue 17794: don't send a trailing space on AUTH command when there's no password.
|
||||||
|
t.Run("No trailing space on AUTH when there is no password (Issue 17794)", func(t *testing.T) {
|
||||||
|
server := "220 hello world\r\n" +
|
||||||
|
"200 some more"
|
||||||
|
var wrote strings.Builder
|
||||||
|
var fake faker
|
||||||
|
fake.ReadWriter = struct {
|
||||||
|
io.Reader
|
||||||
|
io.Writer
|
||||||
|
}{
|
||||||
|
strings.NewReader(server),
|
||||||
|
&wrote,
|
||||||
|
}
|
||||||
|
c, err := NewClient(fake, "fake.host")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewClient: %v", err)
|
||||||
|
}
|
||||||
|
c.tls = true
|
||||||
|
c.didHello = true
|
||||||
|
_ = c.Auth(toServerEmptyAuth{})
|
||||||
|
if err = c.Close(); err != nil {
|
||||||
|
t.Errorf("close failed: %s", err)
|
||||||
|
}
|
||||||
|
if got, want := wrote.String(), "AUTH FOOAUTH\r\n*\r\nQUIT\r\n"; got != want {
|
||||||
|
t.Errorf("wrote %q; want %q", got, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClient_Mail(t *testing.T) {
|
||||||
|
t.Run("normal from address succeeds", func(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
PortAdder.Add(1)
|
||||||
|
serverPort := int(TestServerPortBase + PortAdder.Load())
|
||||||
|
featureSet := "250 STARTTLS"
|
||||||
|
go func() {
|
||||||
|
if err := simpleSMTPServer(ctx, t, &serverProps{
|
||||||
|
FeatureSet: featureSet,
|
||||||
|
ListenPort: serverPort,
|
||||||
|
},
|
||||||
|
); err != nil {
|
||||||
|
t.Errorf("failed to start test server: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
time.Sleep(time.Millisecond * 30)
|
||||||
|
|
||||||
|
client, err := Dial(fmt.Sprintf("%s:%d", TestServerAddr, serverPort))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to dial to test server: %s", err)
|
||||||
|
}
|
||||||
|
t.Cleanup(func() {
|
||||||
|
if err = client.Close(); err != nil {
|
||||||
|
t.Errorf("failed to close client: %s", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if err = client.Mail("valid-from@domain.tld"); err != nil {
|
||||||
|
t.Errorf("failed to set mail from address: %s", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
t.Run("from address with new lines fails", func(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
PortAdder.Add(1)
|
||||||
|
serverPort := int(TestServerPortBase + PortAdder.Load())
|
||||||
|
featureSet := "250 STARTTLS"
|
||||||
|
go func() {
|
||||||
|
if err := simpleSMTPServer(ctx, t, &serverProps{
|
||||||
|
FeatureSet: featureSet,
|
||||||
|
ListenPort: serverPort,
|
||||||
|
},
|
||||||
|
); err != nil {
|
||||||
|
t.Errorf("failed to start test server: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
time.Sleep(time.Millisecond * 30)
|
||||||
|
|
||||||
|
client, err := Dial(fmt.Sprintf("%s:%d", TestServerAddr, serverPort))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to dial to test server: %s", err)
|
||||||
|
}
|
||||||
|
t.Cleanup(func() {
|
||||||
|
if err = client.Close(); err != nil {
|
||||||
|
t.Errorf("failed to close client: %s", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if err = client.Mail("valid-from@domain.tld\r\n"); err == nil {
|
||||||
|
t.Error("mail from address with new lines should fail")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
t.Run("from address fails on EHLO/HELO", func(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
PortAdder.Add(1)
|
||||||
|
serverPort := int(TestServerPortBase + PortAdder.Load())
|
||||||
|
featureSet := "250 STARTTLS"
|
||||||
|
go func() {
|
||||||
|
if err := simpleSMTPServer(ctx, t, &serverProps{
|
||||||
|
FailOnEhlo: true,
|
||||||
|
FailOnHelo: true,
|
||||||
|
FeatureSet: featureSet,
|
||||||
|
ListenPort: serverPort,
|
||||||
|
},
|
||||||
|
); err != nil {
|
||||||
|
t.Errorf("failed to start test server: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
time.Sleep(time.Millisecond * 30)
|
||||||
|
|
||||||
|
client, err := Dial(fmt.Sprintf("%s:%d", TestServerAddr, serverPort))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to dial to test server: %s", err)
|
||||||
|
}
|
||||||
|
t.Cleanup(func() {
|
||||||
|
if err = client.Close(); err != nil {
|
||||||
|
t.Errorf("failed to close client: %s", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if err = client.Mail("valid-from@domain.tld"); err == nil {
|
||||||
|
t.Error("mail from address should fail on EHLO/HELO")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
t.Run("from address and server supports 8BITMIME", func(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
PortAdder.Add(1)
|
||||||
|
serverPort := int(TestServerPortBase + PortAdder.Load())
|
||||||
|
featureSet := "250-8BITMIME\r\n250 STARTTLS"
|
||||||
|
go func() {
|
||||||
|
if err := simpleSMTPServer(ctx, t, &serverProps{
|
||||||
|
EchoCommandAsError: "MAIL FROM:",
|
||||||
|
FeatureSet: featureSet,
|
||||||
|
ListenPort: serverPort,
|
||||||
|
},
|
||||||
|
); err != nil {
|
||||||
|
t.Errorf("failed to start test server: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
time.Sleep(time.Millisecond * 30)
|
||||||
|
|
||||||
|
client, err := Dial(fmt.Sprintf("%s:%d", TestServerAddr, serverPort))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to dial to test server: %s", err)
|
||||||
|
}
|
||||||
|
t.Cleanup(func() {
|
||||||
|
if err = client.Close(); err != nil {
|
||||||
|
t.Errorf("failed to close client: %s", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if err = client.Mail("valid-from@domain.tld"); err == nil {
|
||||||
|
t.Error("server should echo the command as error but didn't")
|
||||||
|
}
|
||||||
|
sent := strings.Replace(err.Error(), "500 ", "", -1)
|
||||||
|
expected := "MAIL FROM:<valid-from@domain.tld> BODY=8BITMIME"
|
||||||
|
if !strings.EqualFold(sent, expected) {
|
||||||
|
t.Errorf("expected mail from command to be %q, but sent %q", expected, sent)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
t.Run("from address and server supports SMTPUTF8", func(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
PortAdder.Add(1)
|
||||||
|
serverPort := int(TestServerPortBase + PortAdder.Load())
|
||||||
|
featureSet := "250-SMTPUTF8\r\n250 STARTTLS"
|
||||||
|
go func() {
|
||||||
|
if err := simpleSMTPServer(ctx, t, &serverProps{
|
||||||
|
EchoCommandAsError: "MAIL FROM:",
|
||||||
|
FeatureSet: featureSet,
|
||||||
|
ListenPort: serverPort,
|
||||||
|
},
|
||||||
|
); err != nil {
|
||||||
|
t.Errorf("failed to start test server: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
time.Sleep(time.Millisecond * 30)
|
||||||
|
|
||||||
|
client, err := Dial(fmt.Sprintf("%s:%d", TestServerAddr, serverPort))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to dial to test server: %s", err)
|
||||||
|
}
|
||||||
|
t.Cleanup(func() {
|
||||||
|
if err = client.Close(); err != nil {
|
||||||
|
t.Errorf("failed to close client: %s", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if err = client.Mail("valid-from@domain.tld"); err == nil {
|
||||||
|
t.Error("server should echo the command as error but didn't")
|
||||||
|
}
|
||||||
|
sent := strings.Replace(err.Error(), "500 ", "", -1)
|
||||||
|
expected := "MAIL FROM:<valid-from@domain.tld> SMTPUTF8"
|
||||||
|
if !strings.EqualFold(sent, expected) {
|
||||||
|
t.Errorf("expected mail from command to be %q, but sent %q", expected, sent)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
t.Run("from address and server supports DSN", func(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
PortAdder.Add(1)
|
||||||
|
serverPort := int(TestServerPortBase + PortAdder.Load())
|
||||||
|
featureSet := "250-DSN\r\n250 STARTTLS"
|
||||||
|
go func() {
|
||||||
|
if err := simpleSMTPServer(ctx, t, &serverProps{
|
||||||
|
EchoCommandAsError: "MAIL FROM:",
|
||||||
|
FeatureSet: featureSet,
|
||||||
|
ListenPort: serverPort,
|
||||||
|
},
|
||||||
|
); err != nil {
|
||||||
|
t.Errorf("failed to start test server: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
time.Sleep(time.Millisecond * 30)
|
||||||
|
|
||||||
|
client, err := Dial(fmt.Sprintf("%s:%d", TestServerAddr, serverPort))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to dial to test server: %s", err)
|
||||||
|
}
|
||||||
|
t.Cleanup(func() {
|
||||||
|
if err = client.Close(); err != nil {
|
||||||
|
t.Errorf("failed to close client: %s", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
client.dsnmrtype = "FULL"
|
||||||
|
if err = client.Mail("valid-from@domain.tld"); err == nil {
|
||||||
|
t.Error("server should echo the command as error but didn't")
|
||||||
|
}
|
||||||
|
sent := strings.Replace(err.Error(), "500 ", "", -1)
|
||||||
|
expected := "MAIL FROM:<valid-from@domain.tld> RET=FULL"
|
||||||
|
if !strings.EqualFold(sent, expected) {
|
||||||
|
t.Errorf("expected mail from command to be %q, but sent %q", expected, sent)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
t.Run("from address and server supports DSN, SMTPUTF8 and 8BITMIME", func(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
PortAdder.Add(1)
|
||||||
|
serverPort := int(TestServerPortBase + PortAdder.Load())
|
||||||
|
featureSet := "250-DSN\r\n250-8BITMIME\r\n250-SMTPUTF8\r\n250 STARTTLS"
|
||||||
|
go func() {
|
||||||
|
if err := simpleSMTPServer(ctx, t, &serverProps{
|
||||||
|
EchoCommandAsError: "MAIL FROM:",
|
||||||
|
FeatureSet: featureSet,
|
||||||
|
ListenPort: serverPort,
|
||||||
|
},
|
||||||
|
); err != nil {
|
||||||
|
t.Errorf("failed to start test server: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
time.Sleep(time.Millisecond * 30)
|
||||||
|
|
||||||
|
client, err := Dial(fmt.Sprintf("%s:%d", TestServerAddr, serverPort))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to dial to test server: %s", err)
|
||||||
|
}
|
||||||
|
t.Cleanup(func() {
|
||||||
|
if err = client.Close(); err != nil {
|
||||||
|
t.Errorf("failed to close client: %s", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
client.dsnmrtype = "FULL"
|
||||||
|
if err = client.Mail("valid-from@domain.tld"); err == nil {
|
||||||
|
t.Error("server should echo the command as error but didn't")
|
||||||
|
}
|
||||||
|
sent := strings.Replace(err.Error(), "500 ", "", -1)
|
||||||
|
expected := "MAIL FROM:<valid-from@domain.tld> BODY=8BITMIME SMTPUTF8 RET=FULL"
|
||||||
|
if !strings.EqualFold(sent, expected) {
|
||||||
|
t.Errorf("expected mail from command to be %q, but sent %q", expected, sent)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClient_Rcpt(t *testing.T) {
|
||||||
|
t.Run("normal recipient address succeeds", func(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
PortAdder.Add(1)
|
||||||
|
serverPort := int(TestServerPortBase + PortAdder.Load())
|
||||||
|
featureSet := "250-DSN\r\n250-8BITMIME\r\n250-SMTPUTF8\r\n250 STARTTLS"
|
||||||
|
go func() {
|
||||||
|
if err := simpleSMTPServer(ctx, t, &serverProps{
|
||||||
|
FeatureSet: featureSet,
|
||||||
|
ListenPort: serverPort,
|
||||||
|
},
|
||||||
|
); err != nil {
|
||||||
|
t.Errorf("failed to start test server: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
time.Sleep(time.Millisecond * 30)
|
||||||
|
client, err := Dial(fmt.Sprintf("%s:%d", TestServerAddr, serverPort))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to dial to test server: %s", err)
|
||||||
|
}
|
||||||
|
t.Cleanup(func() {
|
||||||
|
if err = client.Close(); err != nil {
|
||||||
|
t.Errorf("failed to close client: %s", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if err = client.Rcpt("valid-to@domain.tld"); err != nil {
|
||||||
|
t.Errorf("failed to set recipient address: %s", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
t.Run("recipient address with newlines fails", func(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
PortAdder.Add(1)
|
||||||
|
serverPort := int(TestServerPortBase + PortAdder.Load())
|
||||||
|
featureSet := "250 STARTTLS"
|
||||||
|
go func() {
|
||||||
|
if err := simpleSMTPServer(ctx, t, &serverProps{
|
||||||
|
FeatureSet: featureSet,
|
||||||
|
ListenPort: serverPort,
|
||||||
|
},
|
||||||
|
); err != nil {
|
||||||
|
t.Errorf("failed to start test server: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
time.Sleep(time.Millisecond * 30)
|
||||||
|
client, err := Dial(fmt.Sprintf("%s:%d", TestServerAddr, serverPort))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to dial to test server: %s", err)
|
||||||
|
}
|
||||||
|
t.Cleanup(func() {
|
||||||
|
if err = client.Close(); err != nil {
|
||||||
|
t.Errorf("failed to close client: %s", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if err = client.Rcpt("valid-to@domain.tld\r\n"); err == nil {
|
||||||
|
t.Error("recpient address with newlines should fail")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
t.Run("recipient address with DSN", func(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
PortAdder.Add(1)
|
||||||
|
serverPort := int(TestServerPortBase + PortAdder.Load())
|
||||||
|
featureSet := "250-DSN\r\n250 STARTTLS"
|
||||||
|
go func() {
|
||||||
|
if err := simpleSMTPServer(ctx, t, &serverProps{
|
||||||
|
EchoCommandAsError: "RCPT TO:",
|
||||||
|
FeatureSet: featureSet,
|
||||||
|
ListenPort: serverPort,
|
||||||
|
},
|
||||||
|
); err != nil {
|
||||||
|
t.Errorf("failed to start test server: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
time.Sleep(time.Millisecond * 30)
|
||||||
|
client, err := Dial(fmt.Sprintf("%s:%d", TestServerAddr, serverPort))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to dial to test server: %s", err)
|
||||||
|
}
|
||||||
|
t.Cleanup(func() {
|
||||||
|
if err = client.Close(); err != nil {
|
||||||
|
t.Errorf("failed to close client: %s", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if err = client.Hello(TestServerAddr); err != nil {
|
||||||
|
t.Fatalf("failed to send hello to test server: %s", err)
|
||||||
|
}
|
||||||
|
client.dsnrntype = "SUCCESS"
|
||||||
|
if err = client.Rcpt("valid-to@domain.tld"); err == nil {
|
||||||
|
t.Error("recpient address with newlines should fail")
|
||||||
|
}
|
||||||
|
sent := strings.Replace(err.Error(), "500 ", "", -1)
|
||||||
|
expected := "RCPT TO:<valid-to@domain.tld> NOTIFY=SUCCESS"
|
||||||
|
if !strings.EqualFold(sent, expected) {
|
||||||
|
t.Errorf("expected rcpt to command to be %q, but sent %q", expected, sent)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func TestBasic(t *testing.T) {
|
func TestBasic(t *testing.T) {
|
||||||
server := strings.Join(strings.Split(basicServer, "\n"), "\r\n")
|
server := strings.Join(strings.Split(basicServer, "\n"), "\r\n")
|
||||||
client := strings.Join(strings.Split(basicClient, "\n"), "\r\n")
|
client := strings.Join(strings.Split(basicClient, "\n"), "\r\n")
|
||||||
|
@ -3422,26 +3990,28 @@ func testingKey(s string) string { return strings.ReplaceAll(s, "TESTING KEY", "
|
||||||
|
|
||||||
// serverProps represents the configuration properties for the SMTP server.
|
// serverProps represents the configuration properties for the SMTP server.
|
||||||
type serverProps struct {
|
type serverProps struct {
|
||||||
FailOnAuth bool
|
EchoCommandAsError string
|
||||||
FailOnDataInit bool
|
FailOnAuth bool
|
||||||
FailOnDataClose bool
|
FailOnDataInit bool
|
||||||
FailOnDial bool
|
FailOnDataClose bool
|
||||||
FailOnEhlo bool
|
FailOnDial bool
|
||||||
FailOnHelo bool
|
FailOnEhlo bool
|
||||||
FailOnMailFrom bool
|
FailOnHelo bool
|
||||||
FailOnNoop bool
|
FailOnMailFrom bool
|
||||||
FailOnQuit bool
|
FailOnNoop bool
|
||||||
FailOnReset bool
|
FailOnQuit bool
|
||||||
FailOnSTARTTLS bool
|
FailOnReset bool
|
||||||
FailTemp bool
|
FailOnSTARTTLS bool
|
||||||
FeatureSet string
|
FailTemp bool
|
||||||
ListenPort int
|
FeatureSet string
|
||||||
SSLListener bool
|
ListenPort int
|
||||||
IsSCRAMPlus bool
|
HashFunc func() hash.Hash
|
||||||
IsTLS bool
|
IsSCRAMPlus bool
|
||||||
SupportDSN bool
|
IsTLS bool
|
||||||
TestSCRAM bool
|
SupportDSN bool
|
||||||
HashFunc func() hash.Hash
|
SSLListener bool
|
||||||
|
TestSCRAM bool
|
||||||
|
VRFYUserUnknown bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// simpleSMTPServer starts a simple TCP server that resonds to SMTP commands.
|
// simpleSMTPServer starts a simple TCP server that resonds to SMTP commands.
|
||||||
|
@ -3540,6 +4110,9 @@ func handleTestServerConnection(connection net.Conn, t *testing.T, props *server
|
||||||
var datastring string
|
var datastring string
|
||||||
data = strings.TrimSpace(data)
|
data = strings.TrimSpace(data)
|
||||||
switch {
|
switch {
|
||||||
|
case props.EchoCommandAsError != "" && strings.HasPrefix(data, props.EchoCommandAsError):
|
||||||
|
writeLine("500 " + data)
|
||||||
|
break
|
||||||
case strings.HasPrefix(data, "HELO"):
|
case strings.HasPrefix(data, "HELO"):
|
||||||
if len(strings.Split(data, " ")) != 2 {
|
if len(strings.Split(data, " ")) != 2 {
|
||||||
writeLine("501 Syntax: HELO hostname")
|
writeLine("501 Syntax: HELO hostname")
|
||||||
|
@ -3643,8 +4216,17 @@ func handleTestServerConnection(connection net.Conn, t *testing.T, props *server
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
writeOK()
|
writeOK()
|
||||||
case strings.EqualFold(data, "vrfy"):
|
case strings.HasPrefix(data, "VRFY"):
|
||||||
writeOK()
|
if props.VRFYUserUnknown {
|
||||||
|
writeLine("550 5.1.1 User unknown")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
parts := strings.SplitN(data, " ", 2)
|
||||||
|
if len(parts) != 2 {
|
||||||
|
writeLine("500 5.0.0 Error: invalid syntax for VRFY")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
writeLine(fmt.Sprintf("250 2.0.0 Ok: %s OK", parts[1]))
|
||||||
case strings.EqualFold(data, "rset"):
|
case strings.EqualFold(data, "rset"):
|
||||||
if props.FailOnReset {
|
if props.FailOnReset {
|
||||||
writeLine("500 5.1.2 Error: reset failed")
|
writeLine("500 5.1.2 Error: reset failed")
|
||||||
|
@ -3674,7 +4256,7 @@ func handleTestServerConnection(connection net.Conn, t *testing.T, props *server
|
||||||
props.IsTLS = true
|
props.IsTLS = true
|
||||||
handleTestServerConnection(connection, t, props)
|
handleTestServerConnection(connection, t, props)
|
||||||
default:
|
default:
|
||||||
writeLine("500 5.5.2 Error: bad syntax")
|
writeLine("500 5.5.2 Error: bad syntax - " + data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue