Compare commits

...

22 commits

Author SHA1 Message Date
Michael Fuchs
e9f7be7dce
Merge 894936092e into 619318ba0d 2024-09-20 11:33:59 +02:00
619318ba0d
Merge pull request #301 from wneessen/feature/168_improve-error-handling
Improved error handling
2024-09-20 11:02:49 +02:00
d400379e2f
Refactor error handling for SendError struct
Reformat the construction of SendError objects for better readability. This improves the clarity and maintainability of the error handling code within the client.go file.
2024-09-20 10:31:19 +02:00
fcbd202595
Add test for IsTemp method on nil SendError
This commit introduces a new test case to verify the behavior of the IsTemp method when called on a nil SendError instance. It ensures that the method returns false as expected, improving test coverage for edge cases.
2024-09-20 10:30:30 +02:00
db2ec99cd6
Merge pull request #300 from wneessen/dependabot/github_actions/github/codeql-action-3.26.8
Bump github/codeql-action from 3.26.7 to 3.26.8
2024-09-19 15:45:49 +02:00
dependabot[bot]
664b7299e6
Bump github/codeql-action from 3.26.7 to 3.26.8
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.26.7 to 3.26.8.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](8214744c54...294a9d9291)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-19 13:44:59 +00:00
508a2f2a6c
Refactor connection handling in tests
Replaced `getTestConnection` with `getTestConnectionNoTestPort` for tests that skip port configuration, improving connection setup flexibility. Added environment variable support for setting ports in `getTestClient` to streamline port configuration in tests.
2024-09-19 15:21:17 +02:00
f469ba977d
Add affected message ID to error log
Include the affected message ID in the error message to provide more context for debugging. This change ensures that each error log contains essential information about the specific message associated with the error.
2024-09-19 12:12:48 +02:00
0239318d94
Add affectedMsg field to SendError struct
Included the affected message in the SendError struct for better error tracking and debugging. This enhancement ensures that any errors encountered during the sending process can be directly associated with the specific message that caused them.
2024-09-19 12:09:23 +02:00
69e211682c
Add GetMessageID method to Msg
Introduced GetMessageID to retrieve the Message ID from the Msg
2024-09-19 11:46:53 +02:00
3bdb6f7cca
Refactor variable naming in Send method
Renamed variable `cerr` to `err` for consistency. This improves readability and standardizes error variable naming within the method.
2024-09-19 10:59:22 +02:00
277ae9be19
Refactor message sending logic
Consolidated the message sending logic into a single `sendSingleMsg` function to reduce duplication and improve code maintainability. This change simplifies the `Send` method in multiple Go version files by removing redundant code and calling the new helper function instead.
2024-09-19 10:56:01 +02:00
2e7156182a
Rename variable 'failed' to 'hasError' for clarity
Renamed the variable 'failed' to 'hasError' to better reflect its purpose and improve code readability. This change helps in understanding that the variable indicates the presence of an error rather than a generic failure.
2024-09-19 10:35:32 +02:00
8ee37abca2
Refactor error handling in message sending loop
Changed from range over messages to range with index to correctly update sendError field in the original messages slice. This prevents shadowing issues and ensures proper error logging for each message.
2024-09-18 12:29:42 +02:00
ee726487f1
Refactor message sending logic for better modularity
Extracted message sending functionality into a new helper function `sendSingleMsg`. This improves the code readability and maintainability by reducing the complexity of the main loop and encapsulating error handling per message.
2024-09-18 11:06:48 +02:00
1dd73778d4
Merge branch 'main' into feature/168_improve-error-handling 2024-09-18 10:33:06 +02:00
d3bea90761
Merge pull request #299 from wneessen/dependabot/github_actions/github/codeql-action-3.26.7
Bump github/codeql-action from 3.26.6 to 3.26.7
2024-09-16 15:51:08 +02:00
dependabot[bot]
68109ed40d
Bump github/codeql-action from 3.26.6 to 3.26.7
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.26.6 to 3.26.7.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](4dd16135b6...8214744c54)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-16 13:46:00 +00:00
94d5f3f7a1
Merge pull request #297 from wneessen/dependabot/github_actions/step-security/harden-runner-2.10.1
Bump step-security/harden-runner from 2.9.1 to 2.10.1
2024-09-11 16:14:56 +02:00
dependabot[bot]
6d9829776a
Bump step-security/harden-runner from 2.9.1 to 2.10.1
Bumps [step-security/harden-runner](https://github.com/step-security/harden-runner) from 2.9.1 to 2.10.1.
- [Release notes](https://github.com/step-security/harden-runner/releases)
- [Commits](5c7944e73c...91182cccc0)

---
updated-dependencies:
- dependency-name: step-security/harden-runner
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-11 14:09:18 +00:00
a747f5f74c
Merge branch 'main' into feature/168_improve-error-handling 2024-09-06 11:15:50 +02:00
0ea9631855
Refactor error handling in message delivery process
The error handling process in sending messages has been refactored for better accuracy and efficiency. Instead of immediately joining errors and returning, they are now collected in a slice and joined in a deferred function to ensure all errors are captured before being returned. Furthermore, the SendError structure has been updated to include a reference to the affected message, providing better context for each error.
2024-07-16 13:07:20 +02:00
16 changed files with 217 additions and 201 deletions

View file

@ -39,7 +39,7 @@ jobs:
go: ['1.20', '1.21', '1.22', '1.23']
steps:
- name: Harden Runner
uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1
uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
with:
egress-policy: audit

View file

@ -45,7 +45,7 @@ jobs:
steps:
- name: Harden Runner
uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1
uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
with:
egress-policy: audit
@ -54,7 +54,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6
uses: github/codeql-action/init@294a9d92911152fe08befb9ec03e240add280cb3 # v3.26.8
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@ -65,7 +65,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6
uses: github/codeql-action/autobuild@294a9d92911152fe08befb9ec03e240add280cb3 # v3.26.8
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@ -79,4 +79,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6
uses: github/codeql-action/analyze@294a9d92911152fe08befb9ec03e240add280cb3 # v3.26.8

View file

@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1
uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
with:
egress-policy: audit

View file

@ -20,7 +20,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1
uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
with:
egress-policy: audit

View file

@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1
uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
with:
egress-policy: audit
- name: Run govulncheck

View file

@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1
uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
with:
egress-policy: audit

View file

@ -35,7 +35,7 @@ jobs:
steps:
- name: Harden Runner
uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1
uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
with:
egress-policy: audit
@ -75,6 +75,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6
uses: github/codeql-action/upload-sarif@294a9d92911152fe08befb9ec03e240add280cb3 # v3.26.8
with:
sarif_file: results.sarif

View file

@ -27,7 +27,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1
uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
with:
egress-policy: audit

View file

@ -787,3 +787,100 @@ func (c *Client) auth() error {
}
return nil
}
// sendSingleMsg sends out a single message and returns an error if the transmission/delivery fails.
// It is invoked by the public Send methods
func (c *Client) sendSingleMsg(message *Msg) error {
if message.encoding == NoEncoding {
if ok, _ := c.smtpClient.Extension("8BITMIME"); !ok {
return &SendError{Reason: ErrNoUnencoded, isTemp: false, affectedMsg: message}
}
}
from, err := message.GetSender(false)
if err != nil {
return &SendError{
Reason: ErrGetSender, errlist: []error{err}, isTemp: isTempError(err),
affectedMsg: message,
}
}
rcpts, err := message.GetRecipients()
if err != nil {
return &SendError{
Reason: ErrGetRcpts, errlist: []error{err}, isTemp: isTempError(err),
affectedMsg: message,
}
}
if c.dsn {
if c.dsnmrtype != "" {
c.smtpClient.SetDSNMailReturnOption(string(c.dsnmrtype))
}
}
if err = c.smtpClient.Mail(from); err != nil {
retError := &SendError{
Reason: ErrSMTPMailFrom, errlist: []error{err}, isTemp: isTempError(err),
affectedMsg: message,
}
if resetSendErr := c.smtpClient.Reset(); resetSendErr != nil {
retError.errlist = append(retError.errlist, resetSendErr)
}
return retError
}
hasError := false
rcptSendErr := &SendError{affectedMsg: message}
rcptSendErr.errlist = make([]error, 0)
rcptSendErr.rcpt = make([]string, 0)
rcptNotifyOpt := strings.Join(c.dsnrntype, ",")
c.smtpClient.SetDSNRcptNotifyOption(rcptNotifyOpt)
for _, rcpt := range rcpts {
if err = c.smtpClient.Rcpt(rcpt); err != nil {
rcptSendErr.Reason = ErrSMTPRcptTo
rcptSendErr.errlist = append(rcptSendErr.errlist, err)
rcptSendErr.rcpt = append(rcptSendErr.rcpt, rcpt)
rcptSendErr.isTemp = isTempError(err)
hasError = true
}
}
if hasError {
if resetSendErr := c.smtpClient.Reset(); resetSendErr != nil {
rcptSendErr.errlist = append(rcptSendErr.errlist, resetSendErr)
}
return rcptSendErr
}
writer, err := c.smtpClient.Data()
if err != nil {
return &SendError{
Reason: ErrSMTPData, errlist: []error{err}, isTemp: isTempError(err),
affectedMsg: message,
}
}
_, err = message.WriteTo(writer)
if err != nil {
return &SendError{
Reason: ErrWriteContent, errlist: []error{err}, isTemp: isTempError(err),
affectedMsg: message,
}
}
message.isDelivered = true
if err = writer.Close(); err != nil {
return &SendError{
Reason: ErrSMTPDataClose, errlist: []error{err}, isTemp: isTempError(err),
affectedMsg: message,
}
}
if err = c.Reset(); err != nil {
return &SendError{
Reason: ErrSMTPReset, errlist: []error{err}, isTemp: isTempError(err),
affectedMsg: message,
}
}
if err = c.checkConn(); err != nil {
return &SendError{
Reason: ErrConnCheck, errlist: []error{err}, isTemp: isTempError(err),
affectedMsg: message,
}
}
return nil
}

View file

@ -7,110 +7,16 @@
package mail
import "strings"
// Send sends out the mail message
func (c *Client) Send(messages ...*Msg) error {
if cerr := c.checkConn(); cerr != nil {
return &SendError{Reason: ErrConnCheck, errlist: []error{cerr}, isTemp: isTempError(cerr)}
if err := c.checkConn(); err != nil {
return &SendError{Reason: ErrConnCheck, errlist: []error{err}, isTemp: isTempError(err)}
}
var errs []*SendError
for _, message := range messages {
message.sendError = nil
if message.encoding == NoEncoding {
if ok, _ := c.smtpClient.Extension("8BITMIME"); !ok {
sendErr := &SendError{Reason: ErrNoUnencoded, isTemp: false}
message.sendError = sendErr
if sendErr := c.sendSingleMsg(message); sendErr != nil {
messages[id].sendError = sendErr
errs = append(errs, sendErr)
continue
}
}
from, err := message.GetSender(false)
if err != nil {
sendErr := &SendError{Reason: ErrGetSender, errlist: []error{err}, isTemp: isTempError(err)}
message.sendError = sendErr
errs = append(errs, sendErr)
continue
}
rcpts, err := message.GetRecipients()
if err != nil {
sendErr := &SendError{Reason: ErrGetRcpts, errlist: []error{err}, isTemp: isTempError(err)}
message.sendError = sendErr
errs = append(errs, sendErr)
continue
}
if c.dsn {
if c.dsnmrtype != "" {
c.smtpClient.SetDSNMailReturnOption(string(c.dsnmrtype))
}
}
if err = c.smtpClient.Mail(from); err != nil {
sendErr := &SendError{Reason: ErrSMTPMailFrom, errlist: []error{err}, isTemp: isTempError(err)}
if resetSendErr := c.smtpClient.Reset(); resetSendErr != nil {
sendErr.errlist = append(sendErr.errlist, resetSendErr)
}
message.sendError = sendErr
errs = append(errs, sendErr)
continue
}
failed := false
rcptSendErr := &SendError{}
rcptSendErr.errlist = make([]error, 0)
rcptSendErr.rcpt = make([]string, 0)
rcptNotifyOpt := strings.Join(c.dsnrntype, ",")
c.smtpClient.SetDSNRcptNotifyOption(rcptNotifyOpt)
for _, rcpt := range rcpts {
if err = c.smtpClient.Rcpt(rcpt); err != nil {
rcptSendErr.Reason = ErrSMTPRcptTo
rcptSendErr.errlist = append(rcptSendErr.errlist, err)
rcptSendErr.rcpt = append(rcptSendErr.rcpt, rcpt)
rcptSendErr.isTemp = isTempError(err)
failed = true
}
}
if failed {
if resetSendErr := c.smtpClient.Reset(); resetSendErr != nil {
rcptSendErr.errlist = append(rcptSendErr.errlist, err)
}
message.sendError = rcptSendErr
errs = append(errs, rcptSendErr)
continue
}
writer, err := c.smtpClient.Data()
if err != nil {
sendErr := &SendError{Reason: ErrSMTPData, errlist: []error{err}, isTemp: isTempError(err)}
message.sendError = sendErr
errs = append(errs, sendErr)
continue
}
_, err = message.WriteTo(writer)
if err != nil {
sendErr := &SendError{Reason: ErrWriteContent, errlist: []error{err}, isTemp: isTempError(err)}
message.sendError = sendErr
errs = append(errs, sendErr)
continue
}
message.isDelivered = true
if err = writer.Close(); err != nil {
sendErr := &SendError{Reason: ErrSMTPDataClose, errlist: []error{err}, isTemp: isTempError(err)}
message.sendError = sendErr
errs = append(errs, sendErr)
continue
}
if err = c.Reset(); err != nil {
sendErr := &SendError{Reason: ErrSMTPReset, errlist: []error{err}, isTemp: isTempError(err)}
message.sendError = sendErr
errs = append(errs, sendErr)
continue
}
if err = c.checkConn(); err != nil {
sendErr := &SendError{Reason: ErrConnCheck, errlist: []error{err}, isTemp: isTempError(err)}
message.sendError = sendErr
errs = append(errs, sendErr)
continue
}
}

View file

@ -9,7 +9,6 @@ package mail
import (
"errors"
"strings"
)
// Send sends out the mail message
@ -18,92 +17,16 @@ func (c *Client) Send(messages ...*Msg) (returnErr error) {
returnErr = &SendError{Reason: ErrConnCheck, errlist: []error{err}, isTemp: isTempError(err)}
return
}
for _, message := range messages {
message.sendError = nil
if message.encoding == NoEncoding {
if ok, _ := c.smtpClient.Extension("8BITMIME"); !ok {
message.sendError = &SendError{Reason: ErrNoUnencoded, isTemp: false}
returnErr = errors.Join(returnErr, message.sendError)
continue
}
}
from, err := message.GetSender(false)
if err != nil {
message.sendError = &SendError{Reason: ErrGetSender, errlist: []error{err}, isTemp: isTempError(err)}
returnErr = errors.Join(returnErr, message.sendError)
continue
}
rcpts, err := message.GetRecipients()
if err != nil {
message.sendError = &SendError{Reason: ErrGetRcpts, errlist: []error{err}, isTemp: isTempError(err)}
returnErr = errors.Join(returnErr, message.sendError)
continue
}
if c.dsn {
if c.dsnmrtype != "" {
c.smtpClient.SetDSNMailReturnOption(string(c.dsnmrtype))
}
}
if err = c.smtpClient.Mail(from); err != nil {
message.sendError = &SendError{Reason: ErrSMTPMailFrom, errlist: []error{err}, isTemp: isTempError(err)}
returnErr = errors.Join(returnErr, message.sendError)
if resetSendErr := c.smtpClient.Reset(); resetSendErr != nil {
returnErr = errors.Join(returnErr, resetSendErr)
}
continue
}
failed := false
rcptSendErr := &SendError{}
rcptSendErr.errlist = make([]error, 0)
rcptSendErr.rcpt = make([]string, 0)
rcptNotifyOpt := strings.Join(c.dsnrntype, ",")
c.smtpClient.SetDSNRcptNotifyOption(rcptNotifyOpt)
for _, rcpt := range rcpts {
if err = c.smtpClient.Rcpt(rcpt); err != nil {
rcptSendErr.Reason = ErrSMTPRcptTo
rcptSendErr.errlist = append(rcptSendErr.errlist, err)
rcptSendErr.rcpt = append(rcptSendErr.rcpt, rcpt)
rcptSendErr.isTemp = isTempError(err)
failed = true
}
}
if failed {
if resetSendErr := c.smtpClient.Reset(); resetSendErr != nil {
returnErr = errors.Join(returnErr, resetSendErr)
}
message.sendError = rcptSendErr
returnErr = errors.Join(returnErr, message.sendError)
continue
}
writer, err := c.smtpClient.Data()
if err != nil {
message.sendError = &SendError{Reason: ErrSMTPData, errlist: []error{err}, isTemp: isTempError(err)}
returnErr = errors.Join(returnErr, message.sendError)
continue
}
_, err = message.WriteTo(writer)
if err != nil {
message.sendError = &SendError{Reason: ErrWriteContent, errlist: []error{err}, isTemp: isTempError(err)}
returnErr = errors.Join(returnErr, message.sendError)
continue
}
message.isDelivered = true
var errs []error
defer func() {
returnErr = errors.Join(errs...)
}()
if err = writer.Close(); err != nil {
message.sendError = &SendError{Reason: ErrSMTPDataClose, errlist: []error{err}, isTemp: isTempError(err)}
returnErr = errors.Join(returnErr, message.sendError)
continue
}
if err = c.Reset(); err != nil {
message.sendError = &SendError{Reason: ErrSMTPReset, errlist: []error{err}, isTemp: isTempError(err)}
returnErr = errors.Join(returnErr, message.sendError)
continue
}
if err = c.checkConn(); err != nil {
message.sendError = &SendError{Reason: ErrConnCheck, errlist: []error{err}, isTemp: isTempError(err)}
returnErr = errors.Join(returnErr, message.sendError)
for id, message := range messages {
if sendErr := c.sendSingleMsg(message); sendErr != nil {
messages[id].sendError = sendErr
errs = append(errs, sendErr)
}
}

View file

@ -629,7 +629,7 @@ func TestClient_DialWithContext(t *testing.T) {
// TestClient_DialWithContext_Fallback tests the Client.DialWithContext method with the fallback
// port functionality
func TestClient_DialWithContext_Fallback(t *testing.T) {
c, err := getTestConnection(true)
c, err := getTestConnectionNoTestPort(true)
if err != nil {
t.Skipf("failed to create test client: %s. Skipping tests", err)
}
@ -1302,6 +1302,50 @@ func getTestConnection(auth bool) (*Client, error) {
return c, nil
}
// getTestConnectionNoTestPort takes environment variables (except the port) to establish a
// connection to a real SMTP server to test all functionality that requires a connection
func getTestConnectionNoTestPort(auth bool) (*Client, error) {
if os.Getenv("TEST_SKIP_ONLINE") != "" {
return nil, fmt.Errorf("env variable TEST_SKIP_ONLINE is set. Skipping online tests")
}
th := os.Getenv("TEST_HOST")
if th == "" {
return nil, fmt.Errorf("no TEST_HOST set")
}
sv := false
if sve := os.Getenv("TEST_TLS_SKIP_VERIFY"); sve != "" {
sv = true
}
c, err := NewClient(th)
if err != nil {
return c, err
}
c.tlsconfig.InsecureSkipVerify = sv
if auth {
st := os.Getenv("TEST_SMTPAUTH_TYPE")
if st != "" {
c.SetSMTPAuth(SMTPAuthType(st))
}
u := os.Getenv("TEST_SMTPAUTH_USER")
if u != "" {
c.SetUsername(u)
}
p := os.Getenv("TEST_SMTPAUTH_PASS")
if p != "" {
c.SetPassword(p)
}
// We don't want to log authentication data in tests
c.SetDebugLog(false)
}
if err := c.DialWithContext(context.Background()); err != nil {
return c, fmt.Errorf("connection to test server failed: %w", err)
}
if err := c.Close(); err != nil {
return c, fmt.Errorf("disconnect from test server failed: %w", err)
}
return c, nil
}
// getTestClient takes environment variables to establish a client without connecting
// to the SMTP server
func getTestClient(auth bool) (*Client, error) {
@ -1357,7 +1401,14 @@ func getTestConnectionWithDSN(auth bool) (*Client, error) {
if th == "" {
return nil, fmt.Errorf("no TEST_HOST set")
}
c, err := NewClient(th, WithDSN())
tp := 25
if tps := os.Getenv("TEST_PORT"); tps != "" {
tpi, err := strconv.Atoi(tps)
if err == nil {
tp = tpi
}
}
c, err := NewClient(th, WithDSN(), WithPort(tp))
if err != nil {
return c, err
}

11
msg.go
View file

@ -491,6 +491,17 @@ func (m *Msg) SetMessageID() {
m.SetMessageIDWithValue(messageID)
}
// GetMessageID returns the message ID of the Msg as string value. If no message ID
// is set, an empty string will be returned
func (m *Msg) GetMessageID() string {
if msgidheader, ok := m.genHeader[HeaderMessageID]; ok {
if len(msgidheader) > 0 {
return msgidheader[0]
}
}
return ""
}
// SetMessageIDWithValue sets the message id for the mail
func (m *Msg) SetMessageIDWithValue(messageID string) {
m.SetGenHeader(HeaderMessageID, fmt.Sprintf("<%s>", messageID))

View file

@ -805,6 +805,21 @@ func TestMsg_SetMessageIDRandomness(t *testing.T) {
}
}
func TestMsg_GetMessageID(t *testing.T) {
expected := "this.is.a.message.id"
msg := NewMsg()
msg.SetMessageIDWithValue(expected)
val := msg.GetMessageID()
if !strings.EqualFold(val, fmt.Sprintf("<%s>", expected)) {
t.Errorf("GetMessageID() failed. Expected: %s, got: %s", fmt.Sprintf("<%s>", expected), val)
}
msg.genHeader[HeaderMessageID] = nil
val = msg.GetMessageID()
if val != "" {
t.Errorf("GetMessageID() failed. Expected empty string, got: %s", val)
}
}
// TestMsg_FromFormat tests the FromFormat and EnvelopeFrom methods for the Msg object
func TestMsg_FromFormat(t *testing.T) {
tests := []struct {

View file

@ -56,10 +56,11 @@ const (
// SendError is an error wrapper for delivery errors of the Msg
type SendError struct {
Reason SendErrReason
isTemp bool
affectedMsg *Msg
errlist []error
isTemp bool
rcpt []string
Reason SendErrReason
}
// SendErrReason represents a comparable reason on why the delivery failed
@ -92,6 +93,11 @@ func (e *SendError) Error() string {
}
}
}
if e.affectedMsg != nil && e.affectedMsg.GetMessageID() != "" {
errMessage.WriteString(", affected message ID: ")
errMessage.WriteString(e.affectedMsg.GetMessageID())
}
return errMessage.String()
}

View file

@ -83,6 +83,13 @@ func TestSendError_IsTemp(t *testing.T) {
}
}
func TestSendError_IsTempNil(t *testing.T) {
var se *SendError
if se.IsTemp() {
t.Error("expected false on nil-senderror")
}
}
// returnSendError is a helper method to retunr a SendError with a specific reason
func returnSendError(r SendErrReason, t bool) error {
return &SendError{Reason: r, isTemp: t}