mirror of
https://github.com/wneessen/go-mail.git
synced 2024-09-11 23:22:47 +02: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" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="b7733b42-c5ae-46b3-8a96-8b973d69a11d" name="Changes" comment="Make GoLint happy">
|
||||
<change beforePath="$PROJECT_DIR$/doc.go" beforeDir="false" afterPath="$PROJECT_DIR$/doc.go" afterDir="false" />
|
||||
<list default="true" id="b79e8e7a-d892-4ce4-8bf4-f9e45415b803" name="Changes" comment="Progress">
|
||||
<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>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
|
@ -19,17 +24,22 @@
|
|||
</list>
|
||||
</option>
|
||||
</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">
|
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||
</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">
|
||||
<option name="stateVersion" value="1" />
|
||||
</component>
|
||||
<component name="ProjectId" id="25y1BKo9v06G2DMj9UoZG0ZxDb1" />
|
||||
<component name="ProjectLevelVcsManager">
|
||||
<OptionsSetting value="false" id="Update" />
|
||||
</component>
|
||||
<component name="ProjectId" id="265yKSOR8vrrhyM3NTraVXli3pO" />
|
||||
<component name="ProjectViewState">
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
|
@ -38,9 +48,7 @@
|
|||
<property name="DefaultGoTemplateProperty" value="Go File" />
|
||||
<property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
|
||||
<property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
|
||||
<property name="TF_FMT" value="false" />
|
||||
<property name="WebServerToolWindowFactoryState" value="true" />
|
||||
<property name="go.format.on.save.advertiser.fired" value="true" />
|
||||
<property name="WebServerToolWindowFactoryState" value="false" />
|
||||
<property name="go.formatter.settings.were.checked" value="true" />
|
||||
<property name="go.import.settings.migrated" value="true" />
|
||||
<property name="go.modules.go.list.on.any.changes.was.set" value="true" />
|
||||
|
@ -54,8 +62,7 @@
|
|||
<module name="go-mail" />
|
||||
<working_directory value="$PROJECT_DIR$" />
|
||||
<go_parameters value="-i" />
|
||||
<kind value="PACKAGE" />
|
||||
<package value="go-mail" />
|
||||
<kind value="FILE" />
|
||||
<directory value="$PROJECT_DIR$" />
|
||||
<filePath value="$PROJECT_DIR$" />
|
||||
<method v="2" />
|
||||
|
@ -65,17 +72,6 @@
|
|||
<working_directory value="$PROJECT_DIR$" />
|
||||
<go_parameters value="-i" />
|
||||
<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$" />
|
||||
<filePath value="$PROJECT_DIR$" />
|
||||
<framework value="gotest" />
|
||||
|
@ -98,11 +94,8 @@
|
|||
</option>
|
||||
</component>
|
||||
<component name="VcsManagerConfiguration">
|
||||
<MESSAGE value="Makeing some progress..." />
|
||||
<MESSAGE value="Calling it a day..." />
|
||||
<MESSAGE value="More progress... calling it a day." />
|
||||
<MESSAGE value="Make GoLint happy" />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="Make GoLint happy" />
|
||||
<MESSAGE value="Progress" />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="Progress" />
|
||||
</component>
|
||||
<component name="VgoProject">
|
||||
<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>
|
||||
|
||||
The main idea of this library is to provide a simple interface to sending mails for
|
||||
my [JS-Mailer](https://github.com/wneessen/js-mailer) project.
|
||||
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. 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/smtp"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -43,6 +44,18 @@ type Client struct {
|
|||
// enc indicates if a Client connection is encrypted or not
|
||||
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
|
||||
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
|
||||
func (c *Client) TLSPolicy() string {
|
||||
return c.tlspolicy.String()
|
||||
|
@ -226,5 +267,51 @@ func (c *Client) DialWithContext(uctx context.Context) error {
|
|||
_, 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
|
||||
}
|
||||
|
|
|
@ -14,7 +14,11 @@ func main() {
|
|||
fmt.Printf("$TEST_HOST env variable cannot be empty\n")
|
||||
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 {
|
||||
fmt.Printf("failed to create new client: %s\n", err)
|
||||
os.Exit(1)
|
||||
|
|
Loading…
Reference in a new issue