Releases: wneessen/go-mail
v0.3.9 SMTP client port, debug logging and more
This release is the biggest since the initial release of the library. While there wasn't really much change on the surface, there happend a lot under the hood.
net/smtp fork
go-mail heavily relies on the Go stdlib net/smtp
package. Unfortunately this lib is in feature freeze mode, so that enhancing/extending the package is not possible. This restricted us - at least to a certain degree - as we can't add features that would be useful for go-mail. For that reason we ported/forked the whole package over and adjusted all parts of go-mail to use it instead.
The original license has been imported and the license headers have been adjusted to reflect the go-mail's MIT license and the original Go BSD-3-Clause license. Additionally, the license headers have been converted to SPDX style.
Debug logging
Since we now have full control over the SMTP client part of go-mail, this allowed us to implement ldebug logging for the SMTP communication. Via the Client.WithDebugLog
client option the user can enable this feature. It will then make use of the new smtp/Client.SetDebugLog
method. Once the flag is set to true, the SMTP client will start logging incoming and outgoing messages to os.Stderr.
We've also implemented a simple log.Logger
interface, as well as a standard logger that satisfies this interface. This allows the user to provide a custom logger, as long as the interface is stasified. If no custom logger is provided, the Stdlog
will be used (which makes use of the Go stdlib again). Accordingly, a Client.WithLogger
option and Client.SetLogger
method have been implemented. Same applies for the smtp counterparts.
Details can be found in #102 and #115 .
More support for middlewares
With #108, #109 and #117 we provide more access to message parts (like attachments or embeds) for middlewares. Parts can now be gotten, set and deleted by middlewares.
Fix attachment reader consectuive writes
Msg.AttachReader()
would not output the attached file after consecutive writes (e.g. a write to a file and then send via Client). In #111 we fixed this behaviour by first reading the io.Reader into memory and then creating a new bytes.Reader
, which does support seeking. In the writeFunc
we then seek to position 0 after a successful io.Copy
. This is probably not the most memory efficient way of handling this, but otherwise we'll have to break the io.Reader
interface, which we do not want..
To compensate this, additionally, a new way of attaching/embedding files has been added: Msg.AttachReadSeeker()
and Msg.EmbedReadSeeker()
which take a io.ReadSeeker
as argument instead. These two methods will skip the reading into memory and make use of the Seek
method of the corresponding interface instead.
We recommend to make use of the new methods instead, to make the memory footprint low.
Special thanks
Special thanks go to @karitham, @cvette and @halilylm for reporting bugs and/or providing PRs to address issues. Also big thanks to @james-d-elliott of the Authelia project for providing helpful insights on various issues with the library.
What's Changed
- Set up FreeBSD tests via CirrusCI by @wneessen in #99
- Fork the net/smtp package from Go's stdlib into go-mail by @wneessen in #100
- Implement SMTP client debug logging by @wneessen in #102
- Update documentation and copyright headers by @wneessen in #103
- Refactor DSN handling from client.go to smtp.go by @wneessen in #104
- Introducing Msg part deletion by @wneessen in #108
- Introduce GetEmbeds() and SetEmbeds() by @wneessen in #109
- Fix Attach/EmbedReader and implement Attach/EmbedReadSeeker by @wneessen in #111
- Go1.20 workflow updates by @wneessen in #113
- Update golangci-lint to Go 1.20 by @wneessen in #114
- Implement Logger interface by @wneessen in #115
- Provide more ways for middleware to interact with mail parts by @wneessen in #117
- fix: parsing of ReplyTo with special characters by @cvette in #118
- Make golangci-lint happy again by @wneessen in #120
- Remove defer from for loops by @wneessen in #122
New Contributors
Full Changelog: v0.3.8...v0.3.9
v0.3.8: Fix bug in SMTP LOGIN Auth for servers with non-normative responses
This release fixes a bug in the SMTP LOGIN Auth part of go-mail as reported in #94. The changes introduced in d0f0435 and 6a446bd caused issues with SMTP servers like ProtonMail Bridge, which respond with non-normative messages.
Thanks to @james-d-elliott from the Authelia team for the investigation and report.
What's Changed
Full Changelog: v0.3.7...v0.3.8
v0.3.7: Improved delivery error handling
This release introduces the SendError
type which satisfies the error interface and provides a better way to identify delivery errors. A new sendError
field has been added to the Msg
as well, to also allow per-message error handling in bulk mailings.
We've also added different SendErrReason
that indicate the different things that can go wrong during mail delivery. These reasons can be checked for, for each Msg
using the errors.Is
methods. Alternatively, the errors.As
method can be used to unwrap the SendError
to get access to it's methods. The SendError
provides a IsTemp
method that returns true if the delivery error is of temporary nature.
This is useful for delivery retries. For example the following code could be used to decide whether the error is retryable or not:
if err := c.DialAndSend(m); err != nil {
var se *mail.SendError
if errors.As(err, &se) && se.IsTemp() {
// retryable error
log.Printf("temporary error, will re-try")
/*
perform some re-try logic here
*/
}
// permanent error
log.Fatal(err)
}
If the Send
method runs into more than one error during delivery, these errors are accumulated and returned with the reason ErrAmbiguous
, since it's not possible to exactly say what caused the error. For this it comes handy, that the *Msg
now provides per-message send errors. The *Msg
now has HasSendError()
, SendErrorIsTemp()
and SendError()
. While HasSendError()
simply returns a bool in case a *Msg
failed during delivery and SendErrorIsTemp()
returns true if it's a temporary error, the SendError()
will return the full SendError
error of the corresponding *Msg
.
The Error()
method of SendError
will return a detailed error string based on the accumulated errors that were collected during the delivery.
Thanks to @imirkin and @iwittkau for providing valueable feedback and performing code review on the PR.
What's Changed
Full Changelog: v0.3.6...v0.3.7
v0.3.6: Bugfixes and improvements
This release is mainly a bugfix release but also introduces slight improvements.
One major bugfix is in Client.Send()
, not sending all mails in case an error occured during a bulk mailing. Client.Send()
provides the possibility to send multiple *Msg
in one go. If one of the *Msg
caused an error with the sending mail server, we were returning completely, while not processing any *Msg
that came after the failing message.
This release fixes this behaviour by processing each message first and then return a accumulated error in case any of the *Msg
processing failed
Additionally, this release separates the Client.Send()
method into two different versions. One that makes use of the new errors.Join()
functionality that is introduced with Go 1.20 and one that handles it the old way for any supported version lower than Go 1.20
Also welcome @james-d-elliott as new contributor to the project, providing a bugfix in the Client.Dialer
and adding an option to disable NOOP
calls. Thanks for the contribution, James!
What's Changed
- Fix #85: Client.Send() failing for all messages if one is broken by @wneessen in #86
- fix: tls config unused with dialer by @james-d-elliott in #87
- feat: without noop option by @james-d-elliott in #88
New Contributors
- @james-d-elliott made their first contribution in #87
Full Changelog: v0.3.5...v0.3.6
v0.3.5: Bugfix in msgWriter error handling and GetAddrHeader
This release fixes a bug in the msgWriter
error handling as well as introduces the new GetAddrHeader
method. Thanks to @oschwald for both, the error report and the feature request.
- The error handling in the
msgWriter.writeBody
method was not working properly. We basically overwrote themw.err
withnil
if the function that followed after a failed write attempt was successful again. SetHeader
andSetHeaderPreformatted
have been deprecated in favour ofSetGenHeader
andSetGenHeaderPreformatted
As pointed out in #80 the naming was pretty confusing, given that we already haveSetAddrHeader
.
With the new naming convention it should be more clear to the user, which method to use for which action. For compatibility reasons the old methods have been kept for now but in reality they are just aliases to the new methods.GetAddrHeader
andGetAddrHeaderString
have been introduced As requested in #80 analogous toGetGenHeader
we also need a similar method for the address headers. Since address headers are*mail.Address
pointer, we've also added a*String
method that will extract the address string and return a string slice instead.- Additionally we're introducing methods for the actual address headers:
GetTo
,GetFrom
,GetCc
andGetBcc
(with a*String
counterpart as well). This way the user has full flexibility. Either they use the more "low-level"GetAddrHeader
method or the higher level methods for the corresponding address type
NOTE; We encourage users who use the SetHeader
method in their code to switch to SetGenHeader
instead.
What's Changed
- #81: Fix error handling in body writer by @wneessen in #82
- #80: GetAddrHeader and SetGenHeader by @wneessen in #83
Full Changelog: v0.3.4...v0.3.5
v0.3.4: MessageIDs and preformatted headers
This release introduces a bugfix and an enhancement:
- A bug was fixed in the MessageID generation. Due to the lack of randomness, the generated IDs for several messages might be the same. Therefore the requirement of uniqueness is not fulfilled. This has been fixed via #75
- A new methods
SetHeaderPreformatted
has been introduced, which allows the user to set a message header with preformated text. The header will not be altered during the mail message processing/output. More details in #77
What's Changed
- Fixed SetMessageID message ID generation by @wneessen in #75
- Add SetHeaderPreformatted() method by @wneessen in #77
- The new messageID generated with #74 is a bit too long by default. Th… by @wneessen in #78
Full Changelog: v0.3.3...v0.3.4
[BREAKING CHANGE] v0.3.3: MiddlewareType and WriteToSkipMiddleware
This release will break current Middleware implementations
For middlewares to be able to access the fully written mail message, we need a way to execute WriteTo
without the calling middleware to be handled, otherwise we end up in an infinite loop.
This release introduces the MiddlewareType
and the corresponding change of the Middleware
interface. We now require to return the MiddlewareType
when the Type()
method on the interface is called. Conveniently MiddlewareType
is an alias to a string
.
This way we can also introduce the WriteToSkipMiddleware()
method which takes a MiddlewareType
as argument. This will allow us to use a WriteTo
call with the initiating Middleware
itself to be skipped.
Please note: This release will break any existing go-mail Middleware
, since they don't provide a Type()
method yet.
What's Changed
Full Changelog: v0.3.2...v0.3.3
[BREAKING CHANGE] v0.3.2: New Reader type to satisfy io.Reader
Initially the Msg
implemented a io.Reader
interface by providing a Read
methods. Unfortunately the method chosen for this method was very naive. It works fine for smaller messages but could result in wrong data returned for larger messages or i. e. used in a bufio.Reader
with non consecutive reads. Since we did not track the position and state of the reading operation, duplicate data might be returned to the caller eventually even leading into infinite loops.
This release fixes the issue be introducing a new Reader
type. The Reader
type satisfies the io.Reader
interface and returns the data properly as well as returns EOF
in case the end of data is reached.
The initial Read()
method has been removed from the Msg
type and instead a NewReader()
method has been introduced that returns the Reader
type.
BREAKING CHANGE: Since we remove the Read
method from the Msg
the Msg
does not satisfy the io.Reader
interface anymore, which is considered a breaking change. But given that the returned data of the original implementation might return duplicate or wrong data, this breaking change is considered as the right decision.
What's Changed
- Introduce golangci-lint by @wneessen in #66
- Fix codecov Go version setup by @wneessen in #67
- New Reader type by @wneessen in #68
Full Changelog: v0.3.1...v0.3.2
v0.3.1: Access to message attachments
This release introduces a couple of new methods on the Msg
, which will help the Msg.Middleware
to deal with Msg
attachments.
To get the Msg
attachments, we can now use the Msg.GetAttachments()
methods to get the list of currently assigned attachments to the Msg.
To set the Msg
attachments, we can now use the Msg.SetAttachments()
methods to set a list of *File
as Msg
attachments.
What's Changed
- Expose Msg attachments fields by @dhia-gharsallaoui in #63 (Thanks to Dhia Gharsallaoui for the PR!)
Full Changelog: v0.3.0...v0.3.1
v0.3.0: Access to message body parts
This release introduces a couple of new methods on the Msg
as well as the Part
. This will help the Msg.Middleware
to be more extensible since we allow access not only to headers but also to body parts.
To get the Msg body parts, one can now utilize the Msg.GetParts()
methods to get the list of currently assigned message parts to the Msg
.
Each Part
has now an additional list of getters/setters, to read and modify the given part:
Part.GetContent()
: get the parts contentPart.GetContentType()
: get the parts content typePart.GetEncoding()
: get the parts encodingPart.GetWriteFunc()
: get the parts write function
Each of these Part.Get*()
methods also has an corresponding Part.Set*()
methods what overrides the current value
Noteworthy changes
- a3a2b0e Introduces the new getter/setter methods