Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -1209,6 +1209,55 @@ func (c *Client) Send(messages ...*Msg) (returnErr error) {
return c.SendWithSMTPClient(c.smtpClient, messages...)
}

// SendWithSMTPClient attempts to send one or more Msg using a provided smtp.Client with an
// established connection to the SMTP server. If the smtp.Client has no active connection to
// the server, SendWithSMTPClient will fail with an error. For each of the provided Msg, it
// will associate a SendError with the Msg in case of a transmission or delivery error.
//
// This method first checks for an active connection to the SMTP server. If the connection is
// not valid, it returns a SendError. It then iterates over the provided messages, attempting
// to send each one. If an error occurs during sending, the method records the error and
// associates it with the corresponding Msg. If multiple errors are encountered, it aggregates
// them into a single SendError to be returned.
//
// Parameters:
// - client: A pointer to the smtp.Client that holds the connection to the SMTP server
// - messages: A variadic list of pointers to Msg objects to be sent.
//
// Returns:
// - An error that represents the sending result, which may include multiple SendErrors if
// any occurred; otherwise, returns nil.
func (c *Client) SendWithSMTPClient(client *smtp.Client, messages ...*Msg) (returnErr error) {
escSupport := false
if client != nil {
escSupport, _ = client.Extension("ENHANCEDSTATUSCODES")
}
if err := c.checkConn(client); err != nil {
returnErr = &SendError{
Reason: ErrConnCheck, errlist: []error{err}, isTemp: isTempError(err),
errcode: errorCode(err), enhancedStatusCode: enhancedStatusCode(err, escSupport),
}
return
}

var errs []error
defer func() {
returnErr = errors.Join(errs...)
}()

for id, message := range messages {
if message == nil {
continue
}
if sendErr := c.sendSingleMsg(client, message); sendErr != nil {
messages[id].sendError = sendErr
errs = append(errs, sendErr)
}
}

return
}

// auth attempts to authenticate the client using SMTP AUTH mechanisms. It checks the connection,
// determines the supported authentication methods, and applies the appropriate authentication
// type. An error is returned if authentication fails.
Expand Down
63 changes: 0 additions & 63 deletions client_120.go

This file was deleted.

126 changes: 0 additions & 126 deletions client_121_test.go

This file was deleted.

103 changes: 103 additions & 0 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3652,6 +3652,109 @@ func TestClient_sendSingleMsg(t *testing.T) {
})
}

func TestNewClientNewVersionsOnly(t *testing.T) {
tests := []struct {
name string
option Option
expectFunc func(c *Client) error
shouldfail bool
expectErr *error
}{
{
"WithLogger log.JSONlog", WithLogger(log.NewJSON(os.Stderr, log.LevelDebug)),
func(c *Client) error {
if c.logger == nil {
return errors.New("failed to set logger. Want logger bug got got nil")
}
loggerType := reflect.TypeOf(c.logger).String()
if loggerType != "*log.JSONlog" {
return fmt.Errorf("failed to set logger. Want logger type: %s, got: %s",
"*log.JSONlog", loggerType)
}
return nil
},
false, nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
client, err := NewClient(DefaultHost, tt.option)
if !tt.shouldfail && err != nil {
t.Fatalf("failed to create new client: %s", err)
}
if tt.shouldfail && err == nil {
t.Errorf("client creation was supposed to fail, but it didn't")
}
if tt.shouldfail && tt.expectErr != nil {
if !errors.Is(err, *tt.expectErr) {
t.Errorf("error for NewClient mismatch. Expected: %s, got: %s",
*tt.expectErr, err)
}
}
if tt.expectFunc != nil {
if err = tt.expectFunc(client); err != nil {
t.Errorf("NewClient with custom option failed: %s", err)
}
}
})
}
}

func TestClient_DialWithContextNewVersionsOnly(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
PortAdder.Add(1)
serverPort := int(TestServerPortBase + PortAdder.Load())
featureSet := "250-AUTH PLAIN\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)
t.Run("connect with full debug logging and auth logging", func(t *testing.T) {
ctxDial, cancelDial := context.WithTimeout(ctx, time.Millisecond*500)
t.Cleanup(cancelDial)

logBuffer := bytes.NewBuffer(nil)
client, err := NewClient(DefaultHost, WithPort(serverPort), WithTLSPolicy(NoTLS),
WithDebugLog(), WithLogAuthData(), WithLogger(log.NewJSON(logBuffer, log.LevelDebug)),
WithSMTPAuth(SMTPAuthPlain), WithUsername("test"), WithPassword("password"))
if err != nil {
t.Fatalf("failed to create new client: %s", err)
}

if err = client.DialWithContext(ctxDial); err != nil {
var netErr net.Error
if errors.As(err, &netErr) && netErr.Timeout() {
t.Skip("failed to connect to the test server due to timeout")
}
t.Fatalf("failed to connect to the test server: %s", err)
}
t.Cleanup(func() {
if err = client.Close(); err != nil {
t.Errorf("failed to close the client: %s", err)
}
})

logs := parseJSONLog(t, logBuffer)
if len(logs.Lines) == 0 {
t.Errorf("failed to enable debug logging, but no logs were found")
}
authFound := false
for _, logline := range logs.Lines {
if strings.EqualFold(logline.Message, "AUTH PLAIN AHRlc3QAcGFzc3dvcmQ=") &&
logline.Direction.From == "client" && logline.Direction.To == "server" {
authFound = true
}
}
if !authFound {
t.Errorf("logAuthData not working, no authentication info found in logs")
}
})
}

func TestClient_checkConn(t *testing.T) {
t.Run("connection is alive", func(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
Expand Down
3 changes: 0 additions & 3 deletions log/jsonlog.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
//
// SPDX-License-Identifier: MIT

//go:build go1.21
// +build go1.21

package log

import (
Expand Down
3 changes: 0 additions & 3 deletions log/jsonlog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
//
// SPDX-License-Identifier: MIT

//go:build go1.21
// +build go1.21

package log

import (
Expand Down
Loading
Loading