forked from wneessen/go-mail
-
Notifications
You must be signed in to change notification settings - Fork 0
/
senderror.go
226 lines (202 loc) · 6.96 KB
/
senderror.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
// SPDX-FileCopyrightText: 2022-2023 The go-mail Authors
//
// SPDX-License-Identifier: MIT
package mail
import (
"errors"
"strings"
)
// List of SendError reasons
const (
// ErrGetSender is returned if the Msg.GetSender method fails during a Client.Send
ErrGetSender SendErrReason = iota
// ErrGetRcpts is returned if the Msg.GetRecipients method fails during a Client.Send
ErrGetRcpts
// ErrSMTPMailFrom is returned if the Msg delivery failed when sending the MAIL FROM command
// to the sending SMTP server
ErrSMTPMailFrom
// ErrSMTPRcptTo is returned if the Msg delivery failed when sending the RCPT TO command
// to the sending SMTP server
ErrSMTPRcptTo
// ErrSMTPData is returned if the Msg delivery failed when sending the DATA command
// to the sending SMTP server
ErrSMTPData
// ErrSMTPDataClose is returned if the Msg delivery failed when trying to close the
// Client data writer
ErrSMTPDataClose
// ErrSMTPReset is returned if the Msg delivery failed when sending the RSET command
// to the sending SMTP server
ErrSMTPReset
// ErrWriteContent is returned if the Msg delivery failed when sending Msg content
// to the Client writer
ErrWriteContent
// ErrConnCheck is returned if the Msg delivery failed when checking if the SMTP
// server connection is still working
ErrConnCheck
// ErrNoUnencoded is returned if the Msg delivery failed when the Msg is configured for
// unencoded delivery but the server does not support this
ErrNoUnencoded
// ErrAmbiguous is a generalized delivery error for the SendError type that is
// returned if the exact reason for the delivery failure is ambiguous
ErrAmbiguous
)
// SendError is an error wrapper for delivery errors of the Msg.
//
// This struct represents an error that occurs during the delivery of a message. It holds
// details about the affected message, a list of errors, the recipient list, and whether
// the error is temporary or permanent. It also includes a reason code for the error.
type SendError struct {
affectedMsg *Msg
errlist []error
isTemp bool
rcpt []string
Reason SendErrReason
}
// SendErrReason represents a comparable reason on why the delivery failed
type SendErrReason int
// Error implements the error interface for the SendError type.
//
// This function returns a detailed error message string for the SendError, including the
// reason for failure, list of errors, affected recipients, and the message ID of the
// affected message (if available). If the reason is unknown (greater than 10), it returns
// "unknown reason". The error message is built dynamically based on the content of the
// error list, recipient list, and message ID.
//
// Returns:
// - A string representing the error message.
func (e *SendError) Error() string {
if e.Reason > 10 {
return "unknown reason"
}
var errMessage strings.Builder
errMessage.WriteString(e.Reason.String())
if len(e.errlist) > 0 {
errMessage.WriteRune(':')
for i := range e.errlist {
errMessage.WriteRune(' ')
errMessage.WriteString(e.errlist[i].Error())
if i != len(e.errlist)-1 {
errMessage.WriteString(", ")
}
}
}
if len(e.rcpt) > 0 {
errMessage.WriteString(", affected recipient(s): ")
for i := range e.rcpt {
errMessage.WriteString(e.rcpt[i])
if i != len(e.rcpt)-1 {
errMessage.WriteString(", ")
}
}
}
if e.affectedMsg != nil && e.affectedMsg.GetMessageID() != "" {
errMessage.WriteString(", affected message ID: ")
errMessage.WriteString(e.affectedMsg.GetMessageID())
}
return errMessage.String()
}
// Is implements the errors.Is functionality and compares the SendErrReason.
//
// This function allows for comparison between two errors by checking if the provided
// error matches the SendError type and, if so, compares the SendErrReason and the
// temporary status (isTemp) of both errors.
//
// Parameters:
// - errType: The error to compare against the current SendError.
//
// Returns:
// - true if the errors have the same reason and temporary status, false otherwise.
func (e *SendError) Is(errType error) bool {
var t *SendError
if errors.As(errType, &t) && t != nil {
return e.Reason == t.Reason && e.isTemp == t.isTemp
}
return false
}
// IsTemp returns true if the delivery error is of a temporary nature and can be retried.
//
// This function checks whether the SendError indicates a temporary error, which suggests
// that the delivery can be retried. If the SendError is nil, it returns false.
//
// Returns:
// - true if the error is temporary, false otherwise.
func (e *SendError) IsTemp() bool {
if e == nil {
return false
}
return e.isTemp
}
// MessageID returns the message ID of the affected Msg that caused the error.
//
// This function retrieves the message ID of the Msg associated with the SendError.
// If no message ID was set or if the SendError or Msg is nil, it returns an empty string.
//
// Returns:
// - The message ID as a string, or an empty string if no ID is available.
func (e *SendError) MessageID() string {
if e == nil || e.affectedMsg == nil {
return ""
}
return e.affectedMsg.GetMessageID()
}
// Msg returns the pointer to the affected message that caused the error.
//
// This function retrieves the Msg associated with the SendError. If the SendError or
// the affectedMsg is nil, it returns nil.
//
// Returns:
// - A pointer to the Msg that caused the error, or nil if not available.
func (e *SendError) Msg() *Msg {
if e == nil || e.affectedMsg == nil {
return nil
}
return e.affectedMsg
}
// String satisfies the fmt.Stringer interface for the SendErrReason type.
//
// This function converts the SendErrReason into a human-readable string representation based
// on the error type. If the error reason does not match any predefined case, it returns
// "unknown reason".
//
// Returns:
// - A string representation of the SendErrReason.
func (r SendErrReason) String() string {
switch r {
case ErrGetSender:
return "getting sender address"
case ErrGetRcpts:
return "getting recipient addresses"
case ErrSMTPMailFrom:
return "sending SMTP MAIL FROM command"
case ErrSMTPRcptTo:
return "sending SMTP RCPT TO command"
case ErrSMTPData:
return "sending SMTP DATA command"
case ErrSMTPDataClose:
return "closing SMTP DATA writer"
case ErrSMTPReset:
return "sending SMTP RESET command"
case ErrWriteContent:
return "sending message content"
case ErrConnCheck:
return "checking SMTP connection"
case ErrNoUnencoded:
return ErrServerNoUnencoded.Error()
case ErrAmbiguous:
return "ambiguous reason, check Msg.SendError for message specific reasons"
}
return "unknown reason"
}
// isTempError checks if the given SMTP error is of a temporary nature and should be retried.
//
// This function inspects the error message and returns true if the first character of the
// error message is '4', indicating a temporary SMTP error that can be retried.
//
// Parameters:
// - err: The error to check.
//
// Returns:
// - true if the error is temporary, false otherwise.
func isTempError(err error) bool {
return err.Error()[0] == '4'
}