2022-12-31 12:47:20 +01:00
|
|
|
// SPDX-FileCopyrightText: 2022 Winni Neessen <winni@neessen.dev>
|
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
|
2022-12-31 12:40:42 +01:00
|
|
|
package mail
|
|
|
|
|
|
|
|
import (
|
2023-01-01 14:25:00 +01:00
|
|
|
"errors"
|
2022-12-31 12:40:42 +01:00
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
2023-01-01 14:20:13 +01:00
|
|
|
// List of SendError reasons
|
|
|
|
const (
|
2022-12-31 12:40:42 +01:00
|
|
|
// ErrGetSender is returned if the Msg.GetSender method fails during a Client.Send
|
2023-01-01 14:20:13 +01:00
|
|
|
ErrGetSender SendErrReason = iota
|
2022-12-31 12:40:42 +01:00
|
|
|
|
|
|
|
// ErrGetRcpts is returned if the Msg.GetRecipients method fails during a Client.Send
|
2023-01-01 14:20:13 +01:00
|
|
|
ErrGetRcpts
|
2022-12-31 12:40:42 +01:00
|
|
|
|
|
|
|
// ErrSMTPMailFrom is returned if the Msg delivery failed when sending the MAIL FROM command
|
|
|
|
// to the sending SMTP server
|
2023-01-01 14:20:13 +01:00
|
|
|
ErrSMTPMailFrom
|
2022-12-31 12:40:42 +01:00
|
|
|
|
|
|
|
// ErrSMTPRcptTo is returned if the Msg delivery failed when sending the RCPT TO command
|
|
|
|
// to the sending SMTP server
|
2023-01-01 14:20:13 +01:00
|
|
|
ErrSMTPRcptTo
|
2022-12-31 12:40:42 +01:00
|
|
|
|
|
|
|
// ErrSMTPData is returned if the Msg delivery failed when sending the DATA command
|
|
|
|
// to the sending SMTP server
|
2023-01-01 14:20:13 +01:00
|
|
|
ErrSMTPData
|
2022-12-31 12:40:42 +01:00
|
|
|
|
|
|
|
// ErrSMTPDataClose is returned if the Msg delivery failed when trying to close the
|
|
|
|
// Client data writer
|
2023-01-01 14:20:13 +01:00
|
|
|
ErrSMTPDataClose
|
2022-12-31 12:40:42 +01:00
|
|
|
|
|
|
|
// ErrSMTPReset is returned if the Msg delivery failed when sending the RSET command
|
|
|
|
// to the sending SMTP server
|
2023-01-01 14:20:13 +01:00
|
|
|
ErrSMTPReset
|
2022-12-31 12:40:42 +01:00
|
|
|
|
|
|
|
// ErrWriteContent is returned if the Msg delivery failed when sending Msg content
|
|
|
|
// to the Client writer
|
2023-01-01 14:20:13 +01:00
|
|
|
ErrWriteContent
|
2022-12-31 12:40:42 +01:00
|
|
|
|
|
|
|
// ErrConnCheck is returned if the Msg delivery failed when checking if the SMTP
|
|
|
|
// server connection is still working
|
2023-01-01 14:20:13 +01:00
|
|
|
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
|
2022-12-31 12:40:42 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
// SendError is an error wrapper for delivery errors of the Msg
|
|
|
|
type SendError struct {
|
2023-01-01 14:20:13 +01:00
|
|
|
Reason SendErrReason
|
|
|
|
isTemp bool
|
|
|
|
errlist []error
|
2022-12-31 12:40:42 +01:00
|
|
|
rcpt []string
|
|
|
|
}
|
|
|
|
|
2023-01-01 14:20:13 +01:00
|
|
|
// SendErrReason represents a comparable reason on why the delivery failed
|
|
|
|
type SendErrReason int
|
|
|
|
|
2022-12-31 12:40:42 +01:00
|
|
|
// Error implements the error interface for the SendError type
|
2023-01-01 14:20:13 +01:00
|
|
|
func (e *SendError) Error() string {
|
|
|
|
if e.Reason > 9 {
|
|
|
|
return "client_send: unknown error"
|
|
|
|
}
|
|
|
|
|
2022-12-31 12:40:42 +01:00
|
|
|
var em strings.Builder
|
2023-01-01 14:20:13 +01:00
|
|
|
_, _ = fmt.Fprintf(&em, "client_send: %s", e.Reason)
|
|
|
|
if len(e.errlist) > 0 {
|
|
|
|
em.WriteRune(':')
|
|
|
|
for i := range e.errlist {
|
|
|
|
em.WriteRune(' ')
|
|
|
|
em.WriteString(e.errlist[i].Error())
|
|
|
|
if i != len(e.errlist)-1 {
|
|
|
|
em.WriteString(", ")
|
|
|
|
}
|
2022-12-31 12:40:42 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(e.rcpt) > 0 {
|
2023-01-01 14:20:13 +01:00
|
|
|
em.WriteString(", affected recipient(s): ")
|
2022-12-31 12:40:42 +01:00
|
|
|
for i := range e.rcpt {
|
2023-01-01 14:20:13 +01:00
|
|
|
em.WriteString(e.rcpt[i])
|
|
|
|
if i != len(e.rcpt)-1 {
|
|
|
|
em.WriteString(", ")
|
|
|
|
}
|
2022-12-31 12:40:42 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return em.String()
|
|
|
|
}
|
2023-01-01 14:20:13 +01:00
|
|
|
|
|
|
|
// Is implements the errors.Is functionality and compares the SendErrReason
|
|
|
|
func (e *SendError) Is(et error) bool {
|
2023-01-01 14:25:00 +01:00
|
|
|
var t *SendError
|
|
|
|
if errors.As(et, &t) {
|
|
|
|
return e.Reason == t.Reason && e.isTemp == t.isTemp
|
2023-01-01 14:20:13 +01:00
|
|
|
}
|
2023-01-01 14:25:00 +01:00
|
|
|
return false
|
2023-01-01 14:20:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// String implements the Stringer interface for 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()
|
|
|
|
}
|
|
|
|
return "unknown reason"
|
|
|
|
}
|
|
|
|
|
|
|
|
// isTempError checks the given SMTP error and returns true if the given error is of temporary nature
|
|
|
|
// and should be retried
|
|
|
|
func isTempError(e error) bool {
|
2023-01-01 20:35:21 +01:00
|
|
|
return e.Error()[0] == '4'
|
2023-01-01 14:20:13 +01:00
|
|
|
}
|