-
-
Notifications
You must be signed in to change notification settings - Fork 2
/
email.go
151 lines (128 loc) · 4.07 KB
/
email.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
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
InsecureSkipVerify bool // skip certificate verification
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))
}
if smtpParams.InsecureSkipVerify {
opts = append(opts, email.InsecureSkipVerify(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"<[email protected]>?subject=test-subj&from="Notifier"<[email protected]>
// - mailto:[email protected],[email protected]?subject=test-subj&[email protected]&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
}