mirror of
https://github.com/wneessen/go-mail.git
synced 2024-09-19 02:42:47 +02:00
Winni Neessen
47bff15de9
This PR introduces the `SendError` type which implements the error interface. A new `senderror` field has been added to the `Msg` as well, so introduce this type to it. I've also added different error variables that indicate the different things that can go wrong during mail delivery. These variables can be checked for, for each `Msg` using the `errors.As` method The `Error()` method of `SendError` will return a detailed error string on why the `Msg` could not be delivered. Additionally, `HasSendError()` and `SendError()` methods have been added to `Msg`. While `HasSendError()` simply returns a bool in case a `Msg` failed during delivery, the `SendError()` will return the full `SendError` error interface.
101 lines
2.7 KiB
Go
101 lines
2.7 KiB
Go
// SPDX-FileCopyrightText: 2022 Winni Neessen <winni@neessen.dev>
|
|
//
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
//go:build go1.20
|
|
// +build go1.20
|
|
|
|
package mail
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
)
|
|
|
|
// Send sends out the mail message
|
|
func (c *Client) Send(ml ...*Msg) (rerr error) {
|
|
if err := c.checkConn(); err != nil {
|
|
rerr = fmt.Errorf("failed to send mail: %w", err)
|
|
return
|
|
}
|
|
for _, m := range ml {
|
|
if m.encoding == NoEncoding {
|
|
if ok, _ := c.sc.Extension("8BITMIME"); !ok {
|
|
rerr = errors.Join(rerr, ErrServerNoUnencoded)
|
|
m.sendError = SendError{Err: ErrServerNoUnencoded}
|
|
continue
|
|
}
|
|
}
|
|
f, err := m.GetSender(false)
|
|
if err != nil {
|
|
rerr = errors.Join(rerr, err)
|
|
m.sendError = SendError{Err: ErrGetSender, details: []error{err}}
|
|
continue
|
|
}
|
|
rl, err := m.GetRecipients()
|
|
if err != nil {
|
|
rerr = errors.Join(rerr, err)
|
|
m.sendError = SendError{Err: ErrGetRcpts, details: []error{err}}
|
|
continue
|
|
}
|
|
|
|
if err := c.mail(f); err != nil {
|
|
rerr = errors.Join(rerr, fmt.Errorf("sending MAIL FROM command failed: %w", err))
|
|
m.sendError = SendError{Err: ErrSMTPMailFrom, details: []error{err}}
|
|
if reserr := c.sc.Reset(); reserr != nil {
|
|
rerr = errors.Join(rerr, reserr)
|
|
}
|
|
continue
|
|
}
|
|
failed := false
|
|
rse := SendError{}
|
|
rse.details = make([]error, 0)
|
|
rse.rcpt = make([]string, 0)
|
|
for _, r := range rl {
|
|
if err := c.rcpt(r); err != nil {
|
|
rerr = errors.Join(rerr, fmt.Errorf("sending RCPT TO command failed: %w", err))
|
|
rse.Err = ErrSMTPRcptTo
|
|
rse.details = append(rse.details, err)
|
|
rse.rcpt = append(rse.rcpt, r)
|
|
failed = true
|
|
}
|
|
}
|
|
if failed {
|
|
if reserr := c.sc.Reset(); reserr != nil {
|
|
rerr = errors.Join(rerr, reserr)
|
|
}
|
|
m.sendError = rse
|
|
continue
|
|
}
|
|
w, err := c.sc.Data()
|
|
if err != nil {
|
|
rerr = errors.Join(rerr, fmt.Errorf("sending DATA command failed: %w", err))
|
|
m.sendError = SendError{Err: ErrSMTPData, details: []error{err}}
|
|
continue
|
|
}
|
|
_, err = m.WriteTo(w)
|
|
if err != nil {
|
|
rerr = errors.Join(rerr, fmt.Errorf("sending mail content failed: %w", err))
|
|
m.sendError = SendError{Err: ErrWriteContent, details: []error{err}}
|
|
continue
|
|
}
|
|
|
|
if err := w.Close(); err != nil {
|
|
rerr = errors.Join(rerr, fmt.Errorf("failed to close DATA writer: %w", err))
|
|
m.sendError = SendError{Err: ErrSMTPDataClose, details: []error{err}}
|
|
continue
|
|
}
|
|
|
|
if err := c.Reset(); err != nil {
|
|
rerr = errors.Join(rerr, fmt.Errorf("sending RSET command failed: %w", err))
|
|
m.sendError = SendError{Err: ErrSMTPReset, details: []error{err}}
|
|
continue
|
|
}
|
|
if err := c.checkConn(); err != nil {
|
|
rerr = errors.Join(rerr, fmt.Errorf("failed to check server connection: %w", err))
|
|
m.sendError = SendError{Err: ErrConnCheck, details: []error{err}}
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|