That option is needed for outlook.com and Office 365, resolves #1472.
147 lines
3.9 KiB
Go
147 lines
3.9 KiB
Go
package notify
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/mail"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/go-pkgz/email"
|
|
)
|
|
|
|
// SMTPParams contain settings for smtp server connection
|
|
type SMTPParams struct {
|
|
Host string // SMTP host
|
|
Port int // SMTP port
|
|
TLS bool // TLS auth
|
|
StartTLS bool // StartTLS auth
|
|
ContentType string // Content type
|
|
Charset string // Character set
|
|
LoginAuth bool // LOGIN auth method instead of default PLAIN, needed for Office 365 and outlook.com
|
|
Username string // username
|
|
Password string // password
|
|
TimeOut time.Duration // TCP connection timeout
|
|
}
|
|
|
|
// Email notifications client
|
|
type Email struct {
|
|
SMTPParams
|
|
sender *email.Sender
|
|
}
|
|
|
|
// NewEmail makes new Email object
|
|
func NewEmail(smtpParams SMTPParams) *Email {
|
|
var opts []email.Option
|
|
|
|
if smtpParams.Username != "" {
|
|
opts = append(opts, email.Auth(smtpParams.Username, smtpParams.Password))
|
|
}
|
|
|
|
if smtpParams.ContentType != "" {
|
|
opts = append(opts, email.ContentType(smtpParams.ContentType))
|
|
}
|
|
|
|
if smtpParams.Charset != "" {
|
|
opts = append(opts, email.Charset(smtpParams.Charset))
|
|
}
|
|
|
|
if smtpParams.LoginAuth {
|
|
opts = append(opts, email.LoginAuth())
|
|
}
|
|
|
|
if smtpParams.Port != 0 {
|
|
opts = append(opts, email.Port(smtpParams.Port))
|
|
}
|
|
|
|
if smtpParams.TimeOut != 0 {
|
|
opts = append(opts, email.TimeOut(smtpParams.TimeOut))
|
|
}
|
|
|
|
if smtpParams.TLS {
|
|
opts = append(opts, email.TLS(true))
|
|
}
|
|
|
|
if smtpParams.StartTLS {
|
|
opts = append(opts, email.STARTTLS(true))
|
|
}
|
|
|
|
sender := email.NewSender(smtpParams.Host, opts...)
|
|
|
|
return &Email{sender: sender, SMTPParams: smtpParams}
|
|
}
|
|
|
|
// Send sends the message over Email, with "from", "subject" and "unsubscribeLink" parsed from destination field
|
|
// with "mailto:" schema.
|
|
// "unsubscribeLink" passed as a header, https://support.google.com/mail/answer/81126 -> "Use one-click unsubscribe"
|
|
//
|
|
// Example:
|
|
//
|
|
// - mailto:"John Wayne"<john@example.org>?subject=test-subj&from="Notifier"<notify@example.org>
|
|
// - mailto:addr1@example.org,addr2@example.org?subject=test-subj&from=notify@example.org&unsubscribeLink=http://example.org/unsubscribe
|
|
func (e *Email) Send(ctx context.Context, destination, text string) error {
|
|
emailParams, err := e.parseDestination(destination)
|
|
if err != nil {
|
|
return fmt.Errorf("problem parsing destination: %w", err)
|
|
}
|
|
|
|
select {
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
default:
|
|
return e.sender.Send(text, emailParams)
|
|
}
|
|
}
|
|
|
|
// Schema returns schema prefix supported by this client
|
|
func (e *Email) Schema() string {
|
|
return "mailto"
|
|
}
|
|
|
|
// String representation of Email object
|
|
func (e *Email) String() string {
|
|
str := fmt.Sprintf("email: with username '%s' at server %s:%d", e.Username, e.Host, e.Port)
|
|
if e.TLS {
|
|
str += " with TLS"
|
|
}
|
|
if e.StartTLS {
|
|
str += " with StartTLS"
|
|
}
|
|
return str
|
|
}
|
|
|
|
// parses "mailto:" URL and returns email parameters
|
|
func (e *Email) parseDestination(destination string) (email.Params, error) {
|
|
// parse URL
|
|
u, err := url.Parse(destination)
|
|
if err != nil {
|
|
return email.Params{}, err
|
|
}
|
|
if u.Scheme != "mailto" {
|
|
return email.Params{}, fmt.Errorf("unsupported scheme %s, should be mailto", u.Scheme)
|
|
}
|
|
|
|
// parse destination address(es)
|
|
addresses, err := mail.ParseAddressList(u.Opaque)
|
|
if err != nil {
|
|
return email.Params{}, fmt.Errorf("problem parsing email recipients: %w", err)
|
|
}
|
|
destinations := []string{}
|
|
for _, addr := range addresses {
|
|
stringAddr := addr.String()
|
|
// in case of mailgun, correct RFC5322 address with <> yield 501 error, so we need to remove brackets
|
|
if strings.HasPrefix(stringAddr, "<") && strings.HasSuffix(stringAddr, ">") {
|
|
stringAddr = stringAddr[1 : len(stringAddr)-1]
|
|
}
|
|
destinations = append(destinations, stringAddr)
|
|
}
|
|
|
|
return email.Params{
|
|
From: u.Query().Get("from"),
|
|
To: destinations,
|
|
Subject: u.Query().Get("subject"),
|
|
UnsubscribeLink: u.Query().Get("unsubscribeLink"),
|
|
}, nil
|
|
}
|