mirror of
https://github.com/wneessen/go-mail.git
synced 2024-11-22 13:50:49 +01:00
Implemented SMTP AUTH
This commit is contained in:
parent
1f6fc2cadc
commit
4babc309fb
5 changed files with 190 additions and 32 deletions
|
@ -4,8 +4,13 @@
|
||||||
<option name="autoReloadType" value="ALL" />
|
<option name="autoReloadType" value="ALL" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ChangeListManager">
|
<component name="ChangeListManager">
|
||||||
<list default="true" id="b7733b42-c5ae-46b3-8a96-8b973d69a11d" name="Changes" comment="Make GoLint happy">
|
<list default="true" id="b79e8e7a-d892-4ce4-8bf4-f9e45415b803" name="Changes" comment="Progress">
|
||||||
<change beforePath="$PROJECT_DIR$/doc.go" beforeDir="false" afterPath="$PROJECT_DIR$/doc.go" afterDir="false" />
|
<change afterPath="$PROJECT_DIR$/auth.go" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/client.go" beforeDir="false" afterPath="$PROJECT_DIR$/client.go" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/cmd/main.go" beforeDir="false" afterPath="$PROJECT_DIR$/cmd/main.go" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/mailmsg.go" beforeDir="false" afterPath="$PROJECT_DIR$/mailmsg.go" afterDir="false" />
|
||||||
</list>
|
</list>
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
|
@ -19,17 +24,22 @@
|
||||||
</list>
|
</list>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
<component name="GOROOT" url="file://$PROJECT_DIR$/../../go1.17.8" />
|
<component name="GOROOT" url="file://$USER_HOME$/go/go1.17.8" />
|
||||||
<component name="Git.Settings">
|
<component name="Git.Settings">
|
||||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||||
</component>
|
</component>
|
||||||
|
<component name="GitSEFilterConfiguration">
|
||||||
|
<file-type-list>
|
||||||
|
<filtered-out-file-type name="LOCAL_BRANCH" />
|
||||||
|
<filtered-out-file-type name="REMOTE_BRANCH" />
|
||||||
|
<filtered-out-file-type name="TAG" />
|
||||||
|
<filtered-out-file-type name="COMMIT_BY_MESSAGE" />
|
||||||
|
</file-type-list>
|
||||||
|
</component>
|
||||||
<component name="MarkdownSettingsMigration">
|
<component name="MarkdownSettingsMigration">
|
||||||
<option name="stateVersion" value="1" />
|
<option name="stateVersion" value="1" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectId" id="25y1BKo9v06G2DMj9UoZG0ZxDb1" />
|
<component name="ProjectId" id="265yKSOR8vrrhyM3NTraVXli3pO" />
|
||||||
<component name="ProjectLevelVcsManager">
|
|
||||||
<OptionsSetting value="false" id="Update" />
|
|
||||||
</component>
|
|
||||||
<component name="ProjectViewState">
|
<component name="ProjectViewState">
|
||||||
<option name="hideEmptyMiddlePackages" value="true" />
|
<option name="hideEmptyMiddlePackages" value="true" />
|
||||||
<option name="showLibraryContents" value="true" />
|
<option name="showLibraryContents" value="true" />
|
||||||
|
@ -38,9 +48,7 @@
|
||||||
<property name="DefaultGoTemplateProperty" value="Go File" />
|
<property name="DefaultGoTemplateProperty" value="Go File" />
|
||||||
<property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
|
<property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
|
||||||
<property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
|
<property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
|
||||||
<property name="TF_FMT" value="false" />
|
<property name="WebServerToolWindowFactoryState" value="false" />
|
||||||
<property name="WebServerToolWindowFactoryState" value="true" />
|
|
||||||
<property name="go.format.on.save.advertiser.fired" value="true" />
|
|
||||||
<property name="go.formatter.settings.were.checked" value="true" />
|
<property name="go.formatter.settings.were.checked" value="true" />
|
||||||
<property name="go.import.settings.migrated" value="true" />
|
<property name="go.import.settings.migrated" value="true" />
|
||||||
<property name="go.modules.go.list.on.any.changes.was.set" value="true" />
|
<property name="go.modules.go.list.on.any.changes.was.set" value="true" />
|
||||||
|
@ -54,8 +62,7 @@
|
||||||
<module name="go-mail" />
|
<module name="go-mail" />
|
||||||
<working_directory value="$PROJECT_DIR$" />
|
<working_directory value="$PROJECT_DIR$" />
|
||||||
<go_parameters value="-i" />
|
<go_parameters value="-i" />
|
||||||
<kind value="PACKAGE" />
|
<kind value="FILE" />
|
||||||
<package value="go-mail" />
|
|
||||||
<directory value="$PROJECT_DIR$" />
|
<directory value="$PROJECT_DIR$" />
|
||||||
<filePath value="$PROJECT_DIR$" />
|
<filePath value="$PROJECT_DIR$" />
|
||||||
<method v="2" />
|
<method v="2" />
|
||||||
|
@ -65,17 +72,6 @@
|
||||||
<working_directory value="$PROJECT_DIR$" />
|
<working_directory value="$PROJECT_DIR$" />
|
||||||
<go_parameters value="-i" />
|
<go_parameters value="-i" />
|
||||||
<kind value="DIRECTORY" />
|
<kind value="DIRECTORY" />
|
||||||
<package value="go-mail" />
|
|
||||||
<directory value="$PROJECT_DIR$" />
|
|
||||||
<filePath value="$PROJECT_DIR$" />
|
|
||||||
<framework value="gotest" />
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
<configuration name="go test github.com/wneessen/go-mail" type="GoTestRunConfiguration" factoryName="Go Test" nameIsGenerated="true">
|
|
||||||
<module name="go-mail" />
|
|
||||||
<working_directory value="$PROJECT_DIR$" />
|
|
||||||
<kind value="PACKAGE" />
|
|
||||||
<package value="github.com/wneessen/go-mail" />
|
|
||||||
<directory value="$PROJECT_DIR$" />
|
<directory value="$PROJECT_DIR$" />
|
||||||
<filePath value="$PROJECT_DIR$" />
|
<filePath value="$PROJECT_DIR$" />
|
||||||
<framework value="gotest" />
|
<framework value="gotest" />
|
||||||
|
@ -98,11 +94,8 @@
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
<component name="VcsManagerConfiguration">
|
<component name="VcsManagerConfiguration">
|
||||||
<MESSAGE value="Makeing some progress..." />
|
<MESSAGE value="Progress" />
|
||||||
<MESSAGE value="Calling it a day..." />
|
<option name="LAST_COMMIT_MESSAGE" value="Progress" />
|
||||||
<MESSAGE value="More progress... calling it a day." />
|
|
||||||
<MESSAGE value="Make GoLint happy" />
|
|
||||||
<option name="LAST_COMMIT_MESSAGE" value="Make GoLint happy" />
|
|
||||||
</component>
|
</component>
|
||||||
<component name="VgoProject">
|
<component name="VgoProject">
|
||||||
<integration-enabled>true</integration-enabled>
|
<integration-enabled>true</integration-enabled>
|
||||||
|
|
44
README.md
44
README.md
|
@ -2,7 +2,45 @@
|
||||||
|
|
||||||
[![Go Report Card](https://goreportcard.com/badge/github.com/wneessen/go-mail)](https://goreportcard.com/report/github.com/wneessen/go-mail) [![Build Status](https://api.cirrus-ci.com/github/wneessen/go-mail.svg)](https://cirrus-ci.com/github/wneessen/go-mail) <a href="https://ko-fi.com/D1D24V9IX"><img src="https://uploads-ssl.webflow.com/5c14e387dab576fe667689cf/5cbed8a4ae2b88347c06c923_BuyMeACoffee_blue.png" height="20" alt="buy ma a coffee"></a>
|
[![Go Report Card](https://goreportcard.com/badge/github.com/wneessen/go-mail)](https://goreportcard.com/report/github.com/wneessen/go-mail) [![Build Status](https://api.cirrus-ci.com/github/wneessen/go-mail.svg)](https://cirrus-ci.com/github/wneessen/go-mail) <a href="https://ko-fi.com/D1D24V9IX"><img src="https://uploads-ssl.webflow.com/5c14e387dab576fe667689cf/5cbed8a4ae2b88347c06c923_BuyMeACoffee_blue.png" height="20" alt="buy ma a coffee"></a>
|
||||||
|
|
||||||
The main idea of this library is to provide a simple interface to sending mails for
|
The main idea of this library was to provide a simple interface to sending mails for
|
||||||
my [JS-Mailer](https://github.com/wneessen/js-mailer) project.
|
my [JS-Mailer](https://github.com/wneessen/js-mailer) project. It quickly evolved into a
|
||||||
|
full-fledged mail library.
|
||||||
|
|
||||||
This library is **WIP** an should not be considered "production ready" before v1.0 is released.
|
**This library is "WIP" an should not be considered "production ready", yet.**
|
||||||
|
|
||||||
|
go-mail follows idiomatic Go style and best practice.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/wneessen/go-mail"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
c, err := mail.NewClient("mail.example.com", mail.WithTimeout(time.Millisecond*500),
|
||||||
|
mail.WithTLSPolicy(mail.TLSMandatory), mail.WithSMTPAuth(mail.SMTPAuthDigestMD5),
|
||||||
|
mail.WithUsername("tester@example.com"), mail.WithPassword("secureP4ssW0rd!"))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("failed to create new client: %s\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cfn := context.WithCancel(context.Background())
|
||||||
|
defer cfn()
|
||||||
|
|
||||||
|
if err := c.DialWithContext(ctx); err != nil {
|
||||||
|
fmt.Printf("failed to dial: %s\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if err := c.Send(); err != nil {
|
||||||
|
fmt.Printf("failed to send: %s\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
36
auth.go
Normal file
36
auth.go
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
package mail
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
// SMTPAuthType represents a string to any SMTP AUTH type
|
||||||
|
type SMTPAuthType string
|
||||||
|
|
||||||
|
// Supported SMTP AUTH types
|
||||||
|
const (
|
||||||
|
// SMTPAuthLogin is the "LOGIN" SASL authentication mechanism
|
||||||
|
SMTPAuthLogin SMTPAuthType = "LOGIN"
|
||||||
|
|
||||||
|
// SMTPAuthPlain is the "PLAIN" authentication mechanism as described in RFC 4616
|
||||||
|
SMTPAuthPlain SMTPAuthType = "PLAIN"
|
||||||
|
|
||||||
|
// SMTPAuthCramMD5 is the "CRAM-MD5" SASL authentication mechanism as described in RFC 4954
|
||||||
|
SMTPAuthCramMD5 SMTPAuthType = "CRAM-MD5"
|
||||||
|
|
||||||
|
// SMTPAuthDigestMD5 is the "DIGEST-MD5" SASL authentication mechanism as described in RFC 4954
|
||||||
|
SMTPAuthDigestMD5 SMTPAuthType = "DIGEST-MD5"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SMTP Auth related static errors
|
||||||
|
var (
|
||||||
|
// ErrPlainAuthNotSupported should be used if the target server does not support the "PLAIN" schema
|
||||||
|
ErrPlainAuthNotSupported = errors.New("server does not support SMTP AUTH type: PLAIN")
|
||||||
|
|
||||||
|
// ErrLoginAuthNotSupported should be used if the target server does not support the "LOGIN" schema
|
||||||
|
ErrLoginAuthNotSupported = errors.New("server does not support SMTP AUTH type: LOGIN")
|
||||||
|
|
||||||
|
// ErrCramMD5AuthNotSupported should be used if the target server does not support the "CRAM-MD5" schema
|
||||||
|
ErrCramMD5AuthNotSupported = errors.New("server does not support SMTP AUTH type: CRAM-MD5")
|
||||||
|
|
||||||
|
// ErrDigestMD5AuthNotSupported should be used if the target server does not support the "DIGEST-MD5" schema
|
||||||
|
ErrDigestMD5AuthNotSupported = errors.New("server does not support SMTP AUTH type: DIGEST-MD5")
|
||||||
|
)
|
87
client.go
87
client.go
|
@ -8,6 +8,7 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"net/smtp"
|
"net/smtp"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -43,6 +44,18 @@ type Client struct {
|
||||||
// enc indicates if a Client connection is encrypted or not
|
// enc indicates if a Client connection is encrypted or not
|
||||||
enc bool
|
enc bool
|
||||||
|
|
||||||
|
// user is the SMTP AUTH username
|
||||||
|
user string
|
||||||
|
|
||||||
|
// pass is the corresponding SMTP AUTH password
|
||||||
|
pass string
|
||||||
|
|
||||||
|
// satype represents the authentication type for SMTP AUTH
|
||||||
|
satype SMTPAuthType
|
||||||
|
|
||||||
|
// smtpauth is a pointer to smtp.Auth
|
||||||
|
sa smtp.Auth
|
||||||
|
|
||||||
// The SMTP client that is set up when using the Dial*() methods
|
// The SMTP client that is set up when using the Dial*() methods
|
||||||
sc *smtp.Client
|
sc *smtp.Client
|
||||||
}
|
}
|
||||||
|
@ -129,6 +142,34 @@ func WithTLSConfig(co *tls.Config) Option {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithSMTPAuth tells the client to use the provided SMTPAuthType for authentication
|
||||||
|
func WithSMTPAuth(t SMTPAuthType) Option {
|
||||||
|
return func(c *Client) {
|
||||||
|
c.satype = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithSMTPAuthCustom tells the client to use the provided smtp.Auth for SMTP authentication
|
||||||
|
func WithSMTPAuthCustom(a smtp.Auth) Option {
|
||||||
|
return func(c *Client) {
|
||||||
|
c.sa = a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithUsername tells the client to use the provided string as username for authentication
|
||||||
|
func WithUsername(u string) Option {
|
||||||
|
return func(c *Client) {
|
||||||
|
c.user = u
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithPassword tells the client to use the provided string as password/secret for authentication
|
||||||
|
func WithPassword(p string) Option {
|
||||||
|
return func(c *Client) {
|
||||||
|
c.pass = p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TLSPolicy returns the currently set TLSPolicy as string
|
// TLSPolicy returns the currently set TLSPolicy as string
|
||||||
func (c *Client) TLSPolicy() string {
|
func (c *Client) TLSPolicy() string {
|
||||||
return c.tlspolicy.String()
|
return c.tlspolicy.String()
|
||||||
|
@ -226,5 +267,51 @@ func (c *Client) DialWithContext(uctx context.Context) error {
|
||||||
_, c.enc = c.sc.TLSConnectionState()
|
_, c.enc = c.sc.TLSConnectionState()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := c.auth(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// auth will try to perform SMTP AUTH if requested
|
||||||
|
func (c *Client) auth() error {
|
||||||
|
if c.sa == nil && c.satype != "" {
|
||||||
|
sa, sat := c.sc.Extension("AUTH")
|
||||||
|
if !sa {
|
||||||
|
return fmt.Errorf("server does not support SMTP AUTH")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch c.satype {
|
||||||
|
case SMTPAuthPlain:
|
||||||
|
if !strings.Contains(sat, string(SMTPAuthPlain)) {
|
||||||
|
return ErrPlainAuthNotSupported
|
||||||
|
}
|
||||||
|
c.sa = smtp.PlainAuth("", c.user, c.pass, c.host)
|
||||||
|
case SMTPAuthLogin:
|
||||||
|
if !strings.Contains(sat, string(SMTPAuthLogin)) {
|
||||||
|
return ErrLoginAuthNotSupported
|
||||||
|
}
|
||||||
|
c.sa = smtp.PlainAuth("", c.user, c.pass, c.host)
|
||||||
|
case SMTPAuthCramMD5:
|
||||||
|
if !strings.Contains(sat, string(SMTPAuthCramMD5)) {
|
||||||
|
return ErrCramMD5AuthNotSupported
|
||||||
|
}
|
||||||
|
c.sa = smtp.CRAMMD5Auth(c.user, c.pass)
|
||||||
|
case SMTPAuthDigestMD5:
|
||||||
|
if !strings.Contains(sat, string(SMTPAuthDigestMD5)) {
|
||||||
|
return ErrDigestMD5AuthNotSupported
|
||||||
|
}
|
||||||
|
c.sa = smtp.CRAMMD5Auth(c.user, c.pass)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported SMTP AUTH type %q", c.satype)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.sa != nil {
|
||||||
|
if err := c.sc.Auth(c.sa); err != nil {
|
||||||
|
return fmt.Errorf("SMTP AUTH failed: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,11 @@ func main() {
|
||||||
fmt.Printf("$TEST_HOST env variable cannot be empty\n")
|
fmt.Printf("$TEST_HOST env variable cannot be empty\n")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
c, err := mail.NewClient(th, mail.WithTimeout(time.Millisecond*500))
|
|
||||||
|
tu := os.Getenv("TEST_USER")
|
||||||
|
tp := os.Getenv("TEST_PASS")
|
||||||
|
c, err := mail.NewClient(th, mail.WithTimeout(time.Millisecond*500), mail.WithTLSPolicy(mail.TLSMandatory),
|
||||||
|
mail.WithSMTPAuth(mail.SMTPAuthDigestMD5), mail.WithUsername(tu), mail.WithPassword(tp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("failed to create new client: %s\n", err)
|
fmt.Printf("failed to create new client: %s\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
Loading…
Reference in a new issue