mirror of
https://github.com/wneessen/go-mail.git
synced 2024-11-09 23:42:55 +01:00
Winni Neessen
2c7ea3e532
As proposed by @iwittkau the `SendError` type now has a `IsTemp()` method as well indicating to the user if the delivery error is retryable or not. Since we want to use it in the error response from the Client functions like `Send` or `DialAndSend` we need to return the SendError type not only as part of the `*Msg` but also as return value for these methods. Hence, the changes made for #85 been overhauled to return the new error type instead. In the pre Go1.20 version of the `Send()` method we need to return an accumulated version of the SendError type, since we don't have `errors.Join()` and therefore, if more than one error occurred during the delivery we return an ambiguous error reason since we can't tell which of the captured errors is main error. For more details the user can always check the `*Msg.SendError`
128 lines
3.3 KiB
Go
128 lines
3.3 KiB
Go
// SPDX-FileCopyrightText: 2022 Winni Neessen <winni@neessen.dev>
|
|
//
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
//go:build !go1.20
|
|
// +build !go1.20
|
|
|
|
package mail
|
|
|
|
// Send sends out the mail message
|
|
func (c *Client) Send(ml ...*Msg) error {
|
|
if cerr := c.checkConn(); cerr != nil {
|
|
return &SendError{Reason: ErrConnCheck, errlist: []error{cerr}, isTemp: isTempError(cerr)}
|
|
}
|
|
var errs []*SendError
|
|
for _, m := range ml {
|
|
m.sendError = nil
|
|
if m.encoding == NoEncoding {
|
|
if ok, _ := c.sc.Extension("8BITMIME"); !ok {
|
|
se := &SendError{Reason: ErrNoUnencoded, isTemp: false}
|
|
m.sendError = se
|
|
errs = append(errs, se)
|
|
continue
|
|
}
|
|
}
|
|
f, err := m.GetSender(false)
|
|
if err != nil {
|
|
se := &SendError{Reason: ErrGetSender, errlist: []error{err}, isTemp: isTempError(err)}
|
|
m.sendError = se
|
|
errs = append(errs, se)
|
|
continue
|
|
}
|
|
rl, err := m.GetRecipients()
|
|
if err != nil {
|
|
se := &SendError{Reason: ErrGetRcpts, errlist: []error{err}, isTemp: isTempError(err)}
|
|
m.sendError = se
|
|
errs = append(errs, se)
|
|
continue
|
|
}
|
|
|
|
if err := c.mail(f); err != nil {
|
|
se := &SendError{Reason: ErrSMTPMailFrom, errlist: []error{err}, isTemp: isTempError(err)}
|
|
if reserr := c.sc.Reset(); reserr != nil {
|
|
se.errlist = append(se.errlist, reserr)
|
|
}
|
|
m.sendError = se
|
|
errs = append(errs, se)
|
|
continue
|
|
}
|
|
failed := false
|
|
rse := &SendError{}
|
|
rse.errlist = make([]error, 0)
|
|
rse.rcpt = make([]string, 0)
|
|
for _, r := range rl {
|
|
if err := c.rcpt(r); err != nil {
|
|
rse.Reason = ErrSMTPRcptTo
|
|
rse.errlist = append(rse.errlist, err)
|
|
rse.rcpt = append(rse.rcpt, r)
|
|
rse.isTemp = isTempError(err)
|
|
failed = true
|
|
}
|
|
}
|
|
if failed {
|
|
if reserr := c.sc.Reset(); reserr != nil {
|
|
rse.errlist = append(rse.errlist, err)
|
|
}
|
|
m.sendError = rse
|
|
errs = append(errs, rse)
|
|
continue
|
|
}
|
|
w, err := c.sc.Data()
|
|
if err != nil {
|
|
se := &SendError{Reason: ErrSMTPData, errlist: []error{err}, isTemp: isTempError(err)}
|
|
m.sendError = se
|
|
errs = append(errs, se)
|
|
continue
|
|
}
|
|
_, err = m.WriteTo(w)
|
|
if err != nil {
|
|
se := &SendError{Reason: ErrWriteContent, errlist: []error{err}, isTemp: isTempError(err)}
|
|
m.sendError = se
|
|
errs = append(errs, se)
|
|
continue
|
|
}
|
|
|
|
if err := w.Close(); err != nil {
|
|
se := &SendError{Reason: ErrSMTPDataClose, errlist: []error{err}, isTemp: isTempError(err)}
|
|
m.sendError = se
|
|
errs = append(errs, se)
|
|
continue
|
|
}
|
|
|
|
if err := c.Reset(); err != nil {
|
|
se := &SendError{Reason: ErrSMTPReset, errlist: []error{err}, isTemp: isTempError(err)}
|
|
m.sendError = se
|
|
errs = append(errs, se)
|
|
continue
|
|
}
|
|
if err := c.checkConn(); err != nil {
|
|
se := &SendError{Reason: ErrConnCheck, errlist: []error{err}, isTemp: isTempError(err)}
|
|
m.sendError = se
|
|
errs = append(errs, se)
|
|
continue
|
|
}
|
|
}
|
|
|
|
if len(errs) > 0 {
|
|
if len(errs) > 1 {
|
|
re := &SendError{Reason: ErrAmbiguous}
|
|
for i := range errs {
|
|
for _, e := range errs[i].errlist {
|
|
re.errlist = append(re.errlist, e)
|
|
}
|
|
for _, r := range errs[i].rcpt {
|
|
re.rcpt = append(re.rcpt, r)
|
|
}
|
|
}
|
|
|
|
// We assume that the isTemp flage from the last error we received should be the
|
|
// indicator for the returned isTemp flag as well
|
|
re.isTemp = errs[len(errs)-1].isTemp
|
|
|
|
return re
|
|
}
|
|
return errs[0]
|
|
}
|
|
return nil
|
|
}
|